Compare commits

...

1 Commits

Author SHA1 Message Date
Michal Pikulski
bad16d5853 Stash work 2025-12-02 23:57:02 +01:00
100 changed files with 10105 additions and 124 deletions

View File

@@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/projectSettingsUpdater.xml
/contentModel.xml
/.idea.AppleHillsProduction.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -45,6 +45,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 60eb7d51afc72e845836d38e77462855
m_Address: Settings/FortFightSettings
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 8f5195fb013895049a19488fd4d8f2a1
m_Address: Settings/InteractionSettings
m_ReadOnly: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 119c34a520b135848916bca00d03b9eb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 973f02063e5446598db4cbffdbf8f113, type: 3}
m_Name: CeilingFan
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Data.ProjectileData
projectileName: CeilingFan
prefab: {fileID: 0}
baseDamage: 25
cooldownTime: 4
icon: {fileID: -765527507412255412, guid: f70246e6148769846aaea223ec0c2a55, type: 3}
description:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac7c85630db3d7246b832aea2e4c9191
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 973f02063e5446598db4cbffdbf8f113, type: 3}
m_Name: Toaster
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Data.ProjectileData
projectileName: Toaster
prefab: {fileID: 0}
baseDamage: 20
cooldownTime: 3
icon: {fileID: 6674386295937086461, guid: 3bd1c178a78fcd144965cd1731dc309b, type: 3}
description:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d84011b72b4f68342be14648246bc31b
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 973f02063e5446598db4cbffdbf8f113, type: 3}
m_Name: TrashBag
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Data.ProjectileData
projectileName: TrashBag
prefab: {fileID: 0}
baseDamage: 15
cooldownTime: 6
icon: {fileID: 3452003437791708593, guid: 4c13556eeb918624c9dd3d7e4086242e, type: 3}
description:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 23c4aa179d002af4080ec9d7fa22d2ba
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 973f02063e5446598db4cbffdbf8f113, type: 3}
m_Name: Vacumm
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Data.ProjectileData
projectileName: Vacumm
prefab: {fileID: 0}
baseDamage: 30
cooldownTime: 5
icon: {fileID: 8766740682603709380, guid: b0a0abcb5fec95649b581307eca6a444, type: 3}
description:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 457fd1232d5781743a87fc2a308e9446
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -25,3 +25,5 @@ MonoBehaviour:
requiredOrientation: 1
- sceneName: StatueDecoration
requiredOrientation: 1
- sceneName: FortFight
requiredOrientation: 1

View File

@@ -13,17 +13,19 @@ namespace AppleHills.Editor
private static PlayerFollowerSettings _playerFollowerSettings;
private static InteractionSettings _interactionSettings;
private static DivingMinigameSettings _divingMinigameSettings;
private static Minigames.FortFight.Core.FortFightSettings _fortFightSettings;
// Static constructor will be called when Unity loads/reloads scripts
static EditorSettingsProvider()
{
LoadAllSettings();
// Set up the delegates in SettingsAccess
// Set up the delegates in SettingsAccess (includes Fort Fight)
AppleHills.SettingsAccess.SetupEditorProviders(
GetPlayerStopDistance,
GetPlayerStopDistanceDirectInteraction,
GetPuzzlePromptRange
GetPuzzlePromptRange,
GetWeakPointExplosionRadius
);
// Subscribe to asset changes to auto-refresh when settings are modified
@@ -55,12 +57,14 @@ namespace AppleHills.Editor
_playerFollowerSettings = AssetDatabase.LoadAssetAtPath<PlayerFollowerSettings>("Assets/Settings/PlayerFollowerSettings.asset");
_interactionSettings = AssetDatabase.LoadAssetAtPath<InteractionSettings>("Assets/Settings/InteractionSettings.asset");
_divingMinigameSettings = AssetDatabase.LoadAssetAtPath<DivingMinigameSettings>("Assets/Settings/MinigameSettings.asset");
_fortFightSettings = AssetDatabase.LoadAssetAtPath<Minigames.FortFight.Core.FortFightSettings>("Assets/Settings/FortFightSettings.asset");
// Re-register the delegates in case they were lost
AppleHills.SettingsAccess.SetupEditorProviders(
GetPlayerStopDistance,
GetPlayerStopDistanceDirectInteraction,
GetPuzzlePromptRange
GetPuzzlePromptRange,
GetWeakPointExplosionRadius
);
Logging.Debug("Editor settings loaded for Scene View use");
@@ -88,6 +92,12 @@ namespace AppleHills.Editor
return _interactionSettings?.DefaultPuzzlePromptRange ?? 3.0f;
}
// Fort Fight delegate methods
private static float GetWeakPointExplosionRadius()
{
return _fortFightSettings?.WeakPointExplosionRadius ?? 2.5f;
}
// Other utility methods
public static T GetSettings<T>() where T : BaseSettings
{
@@ -97,6 +107,8 @@ namespace AppleHills.Editor
return _interactionSettings as T;
else if (typeof(T) == typeof(DivingMinigameSettings))
return _divingMinigameSettings as T;
else if (typeof(T) == typeof(Minigames.FortFight.Core.FortFightSettings))
return _fortFightSettings as T;
return null;
}

View File

@@ -10,7 +10,7 @@ namespace AppleHills.Core.Settings.Editor
{
private Vector2 scrollPosition;
private List<BaseSettings> allSettings = new List<BaseSettings>();
private string[] tabNames = new string[] { "Player & Follower", "Interaction & Items", "Diving Minigame", "Card System", "Card Sorting", "Bird Pooper", "Statue Dressup" };
private string[] tabNames = new string[] { "Player & Follower", "Interaction & Items", "Diving Minigame", "Card System", "Card Sorting", "Bird Pooper", "Statue Dressup", "Fort Fight" };
private int selectedTab = 0;
private Dictionary<string, SerializedObject> serializedSettingsObjects = new Dictionary<string, SerializedObject>();
private GUIStyle headerStyle;
@@ -52,6 +52,7 @@ namespace AppleHills.Core.Settings.Editor
CreateSettingsIfMissing<CardSortingSettings>("CardSortingSettings");
CreateSettingsIfMissing<BirdPooperSettings>("BirdPooperSettings");
CreateSettingsIfMissing<StatueDressupSettings>("StatueDressupSettings");
CreateSettingsIfMissing<Minigames.FortFight.Core.FortFightSettings>("FortFightSettings");
}
private void CreateSettingsIfMissing<T>(string fileName) where T : BaseSettings
@@ -130,6 +131,9 @@ namespace AppleHills.Core.Settings.Editor
case 6: // Statue Dressup
DrawSettingsEditor<StatueDressupSettings>();
break;
case 7: // Fort Fight
DrawSettingsEditor<Minigames.FortFight.Core.FortFightSettings>();
break;
}
EditorGUILayout.EndScrollView();

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 44868bc8b3350d94fb55014db90c912d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4a1aef5c4f1311742ad0f7272dc50443
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,220 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7086906997932553204
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4854439941706829338}
- component: {fileID: 1733951774359296854}
m_Layer: 0
m_Name: SpriteRenderer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4854439941706829338
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7086906997932553204}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0.0045017465, w: 0.99998987}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 2, y: 2, z: 2}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6512041784125052745}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: -0.516}
--- !u!212 &1733951774359296854
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7086906997932553204}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Sprite: {fileID: -4331849829665928538, guid: 8be45455b29f80241a3b8aae36291752, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 0.16, y: 0.16}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &7520512795364559713
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6512041784125052745}
- component: {fileID: 2825398552764198077}
- component: {fileID: 3408275740391398529}
- component: {fileID: 7748443280095270292}
m_Layer: 0
m_Name: BaseBlock
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6512041784125052745
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7520512795364559713}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4854439941706829338}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!50 &2825398552764198077
Rigidbody2D:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7520512795364559713}
m_BodyType: 0
m_Simulated: 1
m_UseFullKinematicContacts: 0
m_UseAutoMass: 0
m_Mass: 1
m_LinearDamping: 0
m_AngularDamping: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 0
--- !u!61 &3408275740391398529
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7520512795364559713}
m_Enabled: 1
serializedVersion: 3
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_CompositeOperation: 0
m_CompositeOrder: 0
m_Offset: {x: 0.01736021, y: 0.008680344}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
oldSize: {x: 0, y: 0}
newSize: {x: 0, y: 0}
adaptiveTilingThreshold: 0
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
m_Size: {x: 5.3053894, y: 2.093708}
m_EdgeRadius: 0
--- !u!114 &7748443280095270292
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7520512795364559713}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ace8ce8bea324389a9955e63081ccff7, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Fort.FortBlock
material: 0
size: 1
isWeakPoint: 0
explosionRadius: 2.5
explosionDamage: 50
weakPointVisualIndicator: {fileID: 0}
explosionEffectPrefab: {fileID: 0}
spriteRenderer: {fileID: 1733951774359296854}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 415d91c38289d6d48b9e02617c0077ff
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &5808897296274173900
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.b
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.g
value: 0.7391078
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.r
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7520512795364559713, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Name
value: BaseBlock Variant
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2a284fee98d5d9043ac5a6ae12b9be4a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &3300878914999042762
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.b
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.g
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.r
value: 0.68275356
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.x
value: -1.46451
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.y
value: -0.36305
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7520512795364559713, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Name
value: BaseBlock Variant
objectReference: {fileID: 0}
- target: {fileID: 7748443280095270292, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: material
value: 2
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 39e9717047cc0594aad150b00f191858
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &3542555417085833802
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.b
value: 0.990566
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.g
value: 0.5618454
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.r
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.x
value: -1.46451
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.y
value: -0.36305
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7520512795364559713, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Name
value: BaseBlock Variant
objectReference: {fileID: 0}
- target: {fileID: 7748443280095270292, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: size
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7748443280095270292, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: material
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cdd6daf258d61574fbc64bed0f42daf9
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &4844830117807925294
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.b
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.g
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1733951774359296854, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Color.r
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.x
value: -1.46451
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.y
value: -0.36305
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6512041784125052745, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7520512795364559713, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: m_Name
value: BaseBlock Variant
objectReference: {fileID: 0}
- target: {fileID: 7748443280095270292, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: isWeakPoint
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7748443280095270292, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}
propertyPath: explosionRadius
value: 5
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 415d91c38289d6d48b9e02617c0077ff, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6985c12e95c1a814485e55be3d36a243
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1c4af8148f9e1de458911c141d295db6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,387 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2303456945894359403
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8064621895759812076}
- component: {fileID: 8938858301725152068}
m_Layer: 0
m_Name: SimpleFort
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8064621895759812076
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2303456945894359403}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.04, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1731851162938573245}
- {fileID: 7350060303235597263}
- {fileID: 2023440682477717019}
- {fileID: 7643340122397420652}
- {fileID: 5247826848215981902}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8938858301725152068
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2303456945894359403}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 05031222348c421ab564757f52f24952, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Fort.FortController
fortName: SimpleFort
showDebugInfo: 1
--- !u!1001 &100308143899281775
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8064621895759812076}
m_Modifications:
- target: {fileID: 2190174229508730571, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_Size.x
value: 10.338011
objectReference: {fileID: 0}
- target: {fileID: 2190174229508730571, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_Offset.x
value: 0.077272415
objectReference: {fileID: 0}
- target: {fileID: 6446786228975401259, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_Name
value: Block_Steel (1)
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.y
value: 6.54
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8248272390735150160, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalScale.x
value: 3.875
objectReference: {fileID: 0}
- target: {fileID: 8248272390735150160, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
--- !u!4 &7643340122397420652 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
m_PrefabInstance: {fileID: 100308143899281775}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1265740765784495692
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8064621895759812076}
m_Modifications:
- target: {fileID: 5012833389701777835, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_Name
value: Block_Glass
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalPosition.x
value: 3.83
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalPosition.y
value: 2.6999998
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalRotation.w
value: 0.70710784
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalRotation.z
value: 0.70710576
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 90
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 39e9717047cc0594aad150b00f191858, type: 3}
--- !u!4 &7350060303235597263 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 8615518451581982083, guid: 39e9717047cc0594aad150b00f191858, type: 3}
m_PrefabInstance: {fileID: 1265740765784495692}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1354166924051577144
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8064621895759812076}
m_Modifications:
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalPosition.x
value: -3.76
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalPosition.y
value: 2.76
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalRotation.w
value: 0.70710784
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalRotation.z
value: 0.70710576
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 90
objectReference: {fileID: 0}
- target: {fileID: 4090222677377819821, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
propertyPath: m_Name
value: Block_Cardboard
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
--- !u!4 &1731851162938573245 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 775237956681103493, guid: 2a284fee98d5d9043ac5a6ae12b9be4a, type: 3}
m_PrefabInstance: {fileID: 1354166924051577144}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &5888217250810535977
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8064621895759812076}
m_Modifications:
- target: {fileID: 27624381625230900, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalScale.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalPosition.x
value: 0.04000002
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalPosition.y
value: 3.2
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3126186265912943439, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
propertyPath: m_Name
value: Block_Weakpoint
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
--- !u!4 &5247826848215981902 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 1829341962646880103, guid: 6985c12e95c1a814485e55be3d36a243, type: 3}
m_PrefabInstance: {fileID: 5888217250810535977}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &8602561888576316184
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8064621895759812076}
m_Modifications:
- target: {fileID: 6446786228975401259, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_Name
value: Block_Steel
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.y
value: 1.05
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
--- !u!4 &2023440682477717019 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 7743593083879499011, guid: cdd6daf258d61574fbc64bed0f42daf9, type: 3}
m_PrefabInstance: {fileID: 8602561888576316184}
m_PrefabAsset: {fileID: 0}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1481432e299ad794e937cb82505cfbb2
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ using AppleHills.Core.Settings;
using Core.Lifecycle;
using Core.Settings;
using Input;
using Minigames.FortFight.Core;
using UnityEngine;
namespace Core
@@ -173,7 +174,9 @@ namespace Core
var sortingGameSettings = SettingsProvider.Instance.LoadSettingsSynchronous<CardSortingSettings>();
var birdPooperSettings = SettingsProvider.Instance.LoadSettingsSynchronous<BirdPooperSettings>();
var statueDressupSettings = SettingsProvider.Instance.LoadSettingsSynchronous<StatueDressupSettings>();
var fortFightSettings = SettingsProvider.Instance.LoadSettingsSynchronous<FortFightSettings>();
// Register settings with service locator
if (playerSettings != null)
{
@@ -244,10 +247,21 @@ namespace Core
{
Debug.LogError("Failed to load StatueDressupSettings");
}
if (fortFightSettings != null)
{
ServiceLocator.Register<IFortFightSettings>(fortFightSettings);
Logging.Debug("FortFightSettings registered successfully");
}
else
{
Debug.LogError("Failed to load FortFightSettings");
}
// Log success
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null
&& cardSystemSettings != null && birdPooperSettings != null && statueDressupSettings != null;
&& cardSystemSettings != null && birdPooperSettings != null && statueDressupSettings != null
&& fortFightSettings != null;
if (_settingsLoaded)
{
Logging.Debug("All settings loaded and registered with ServiceLocator");
@@ -312,5 +326,8 @@ namespace Core
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
public float DefaultPuzzlePromptRange => GetSettings<IInteractionSettings>()?.DefaultPuzzlePromptRange ?? 3.0f;
// Fort Fight Settings
public float WeakPointExplosionRadius => GetSettings<IFortFightSettings>()?.WeakPointExplosionRadius ?? 2.5f;
}
}

View File

@@ -213,4 +213,30 @@ namespace AppleHills.Core.Settings
string StateSaveKey { get; }
int MaxSavedDecorations { get; }
}
/// <summary>
/// Interface for Fort Fight minigame settings
/// </summary>
public interface IFortFightSettings
{
// Block configurations
System.Collections.Generic.List<Minigames.FortFight.Settings.BlockMaterialConfig> MaterialConfigs { get; }
System.Collections.Generic.List<Minigames.FortFight.Settings.BlockSizeConfig> SizeConfigs { get; }
// Weak point settings
float WeakPointExplosionRadius { get; }
float WeakPointExplosionDamage { get; }
float WeakPointExplosionForce { get; }
// Fort settings
float FortDefeatThreshold { get; }
float PhysicsGravityScale { get; }
// Visual settings
Color DamageColorTint { get; }
// Helper methods
Minigames.FortFight.Settings.BlockMaterialConfig GetMaterialConfig(Minigames.FortFight.Data.BlockMaterial material);
Minigames.FortFight.Settings.BlockSizeConfig GetSizeConfig(Minigames.FortFight.Data.BlockSize size);
}
}

View File

@@ -15,12 +15,14 @@ namespace AppleHills
private static GetSettingsValueDelegate getPlayerStopDistanceProvider;
private static GetSettingsValueDelegate getPlayerStopDistanceDirectInteractionProvider;
private static GetSettingsValueDelegate getPuzzlePromptRangeProvider;
private static GetSettingsValueDelegate getWeakPointExplosionRadiusProvider;
// Editor-only method to set up providers - will be called from editor code
public static void SetupEditorProviders(
GetSettingsValueDelegate playerStopDistanceProvider,
GetSettingsValueDelegate playerStopDistanceDirectInteractionProvider,
GetSettingsValueDelegate puzzlePromptRangeProvider)
GetSettingsValueDelegate puzzlePromptRangeProvider,
GetSettingsValueDelegate weakPointExplosionRadiusProvider)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
@@ -28,6 +30,7 @@ namespace AppleHills
getPlayerStopDistanceProvider = playerStopDistanceProvider;
getPlayerStopDistanceDirectInteractionProvider = playerStopDistanceDirectInteractionProvider;
getPuzzlePromptRangeProvider = puzzlePromptRangeProvider;
getWeakPointExplosionRadiusProvider = weakPointExplosionRadiusProvider;
}
#endif
}
@@ -73,6 +76,19 @@ namespace AppleHills
return GameManager.Instance.DefaultPuzzlePromptRange;
}
// Fort Fight Settings
public static float GetWeakPointExplosionRadius()
{
#if UNITY_EDITOR
if (!Application.isPlaying && getWeakPointExplosionRadiusProvider != null)
{
return getWeakPointExplosionRadiusProvider();
}
#endif
return GameManager.Instance.WeakPointExplosionRadius;
}
// Add more methods as needed for other settings
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 39b2d9cda7ea6d745a490a155fc6f9ca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fccff300fcb6488419e3871d8f59fb95
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
using System.Collections;
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Core;
using Minigames.FortFight.Data;
using UnityEngine;
namespace Minigames.FortFight.AI
{
/// <summary>
/// AI controller for the PigMan opponent.
/// Phase 1: Stubbed implementation - just simulates taking a turn.
/// Phase 4: Full implementation with trajectory calculation and target selection.
/// </summary>
public class FortFightAIController : ManagedBehaviour
{
[Header("AI Settings (Stubbed)")]
[SerializeField] private float aiThinkTime = 1.5f; // Time AI "thinks" before acting
private TurnManager turnManager;
private bool isThinking = false;
#region Initialization
/// <summary>
/// Initialize the AI controller
/// </summary>
public void Initialize()
{
// Get reference to turn manager
turnManager = FindFirstObjectByType<TurnManager>();
if (turnManager == null)
{
Logging.Error("[FortFightAIController] TurnManager not found!");
return;
}
// Subscribe to turn events
turnManager.OnTurnStarted += OnTurnStarted;
Logging.Debug("[FortFightAIController] AI initialized");
}
#endregion
#region Turn Handling
/// <summary>
/// Called when a new turn starts
/// </summary>
private void OnTurnStarted(PlayerData currentPlayer, TurnState turnState)
{
// Only act if it's AI's turn
if (turnState == TurnState.AITurn && !isThinking)
{
StartCoroutine(ExecuteAITurn());
}
}
/// <summary>
/// Execute the AI's turn (stubbed for Phase 1)
/// </summary>
private IEnumerator ExecuteAITurn()
{
isThinking = true;
Logging.Debug($"[FortFightAIController] AI is thinking... (for {aiThinkTime}s)");
// Simulate AI "thinking"
yield return new WaitForSeconds(aiThinkTime);
// STUBBED: Perform AI action
Logging.Debug("[FortFightAIController] AI takes action! (STUBBED - no actual projectile fired yet)");
// End AI turn
isThinking = false;
if (turnManager != null)
{
turnManager.EndTurn();
}
}
#endregion
#region Cleanup
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
if (turnManager != null)
{
turnManager.OnTurnStarted -= OnTurnStarted;
}
}
#endregion
#region Future Implementation (Phase 4)
// TODO Phase 4: Implement ballistic trajectory calculation
// TODO Phase 4: Implement target selection logic
// TODO Phase 4: Implement shot deviation system
// TODO Phase 4: Implement ammunition selection
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d82cbd88db94eec4ba7c19ef60b9fbbc

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 461e23829a7d28547bfabd54136aff7b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Data;
using UnityEngine;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Manages ammunition selection and cooldowns for Fort Fight.
/// Tracks which ammo types are available and handles cooldown timers.
/// </summary>
public class AmmunitionManager : ManagedBehaviour
{
#region Inspector Properties
[Header("Available Ammunition")]
[Tooltip("All projectile types available in this game")]
[SerializeField] private List<ProjectileData> availableAmmo = new List<ProjectileData>();
[Header("Starting Ammo")]
[Tooltip("Default selected ammo at game start")]
[SerializeField] private ProjectileData defaultAmmo;
[Header("Debug")]
[SerializeField] private bool showDebugLogs = true;
#endregion
#region Events
/// <summary>
/// Fired when ammo selection changes. Parameters: (ProjectileData selectedAmmo)
/// </summary>
public event Action<ProjectileData> OnAmmoSelected;
/// <summary>
/// Fired when ammo is used and enters cooldown. Parameters: (ProjectileData ammo, float cooldownTime)
/// </summary>
public event Action<ProjectileData, float> OnAmmoCooldownStarted;
/// <summary>
/// Fired when ammo cooldown completes. Parameters: (ProjectileData ammo)
/// </summary>
public event Action<ProjectileData> OnAmmoCooldownCompleted;
#endregion
#region State
private Dictionary<ProjectileData, float> cooldowns = new Dictionary<ProjectileData, float>();
private ProjectileData selectedAmmo;
public ProjectileData SelectedAmmo => selectedAmmo;
public IReadOnlyList<ProjectileData> AvailableAmmo => availableAmmo;
#endregion
#region Lifecycle
internal override void OnManagedStart()
{
base.OnManagedStart();
// Initialize cooldowns to 0
foreach (var ammo in availableAmmo)
{
cooldowns[ammo] = 0f;
}
// Select default ammo
if (defaultAmmo != null)
{
SelectAmmo(defaultAmmo);
}
else if (availableAmmo.Count > 0)
{
SelectAmmo(availableAmmo[0]);
}
}
private void Update()
{
// Update cooldown timers
List<ProjectileData> completedCooldowns = new List<ProjectileData>();
foreach (var kvp in cooldowns)
{
if (kvp.Value > 0f)
{
cooldowns[kvp.Key] = Mathf.Max(0f, kvp.Value - Time.deltaTime);
// Check if cooldown just completed
if (cooldowns[kvp.Key] == 0f)
{
completedCooldowns.Add(kvp.Key);
}
}
}
// Fire events for completed cooldowns
foreach (var ammo in completedCooldowns)
{
string ammoName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] {ammoName} cooldown completed");
OnAmmoCooldownCompleted?.Invoke(ammo);
}
}
#endregion
#region Ammo Selection
/// <summary>
/// Select ammunition type (if available - not on cooldown)
/// </summary>
public bool SelectAmmo(ProjectileData ammo)
{
if (ammo == null)
{
Logging.Warning("[AmmunitionManager] Attempted to select null ammo");
return false;
}
if (!availableAmmo.Contains(ammo))
{
string ammoName = GetAmmoName(ammo);
Logging.Warning($"[AmmunitionManager] {ammoName} not in available ammo list");
return false;
}
if (!IsAmmoAvailable(ammo))
{
string ammoName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] {ammoName} is on cooldown");
return false;
}
selectedAmmo = ammo;
string selectedName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] Selected ammo: {selectedName}");
OnAmmoSelected?.Invoke(ammo);
return true;
}
/// <summary>
/// Check if ammo is available (not on cooldown)
/// </summary>
public bool IsAmmoAvailable(ProjectileData ammo)
{
if (!cooldowns.ContainsKey(ammo))
{
return true;
}
return cooldowns[ammo] <= 0f;
}
/// <summary>
/// Get remaining cooldown time for ammo
/// </summary>
public float GetCooldownRemaining(ProjectileData ammo)
{
if (!cooldowns.ContainsKey(ammo))
{
return 0f;
}
return cooldowns[ammo];
}
#endregion
#region Ammo Usage
/// <summary>
/// Use current ammo (trigger cooldown)
/// </summary>
public void UseAmmo()
{
if (selectedAmmo == null)
{
Logging.Warning("[AmmunitionManager] No ammo selected to use");
return;
}
UseAmmo(selectedAmmo);
}
/// <summary>
/// Use specific ammo (trigger cooldown)
/// </summary>
public void UseAmmo(ProjectileData ammo)
{
if (ammo == null) return;
if (!cooldowns.ContainsKey(ammo))
{
cooldowns[ammo] = 0f;
}
// Start cooldown
cooldowns[ammo] = ammo.cooldownTime;
string ammoName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] {ammoName} used - cooldown: {ammo.cooldownTime}s");
OnAmmoCooldownStarted?.Invoke(ammo, ammo.cooldownTime);
}
#endregion
#region Public API
/// <summary>
/// Add ammo to available list (for unlocks/powerups)
/// </summary>
public void AddAmmo(ProjectileData ammo)
{
if (!availableAmmo.Contains(ammo))
{
availableAmmo.Add(ammo);
cooldowns[ammo] = 0f;
string ammoName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] Added ammo: {ammoName}");
}
}
/// <summary>
/// Remove ammo from available list
/// </summary>
public void RemoveAmmo(ProjectileData ammo)
{
if (availableAmmo.Contains(ammo))
{
availableAmmo.Remove(ammo);
cooldowns.Remove(ammo);
// If this was selected, select first available
if (selectedAmmo == ammo && availableAmmo.Count > 0)
{
SelectAmmo(availableAmmo[0]);
}
string ammoName = GetAmmoName(ammo);
if (showDebugLogs) Logging.Debug($"[AmmunitionManager] Removed ammo: {ammoName}");
}
}
/// <summary>
/// Reset all cooldowns
/// </summary>
public void ResetAllCooldowns()
{
foreach (var ammo in cooldowns.Keys)
{
cooldowns[ammo] = 0f;
}
if (showDebugLogs) Logging.Debug("[AmmunitionManager] All cooldowns reset");
}
#endregion
#region Helpers
/// <summary>
/// Get display name for ammo (uses displayName or prefab name as fallback)
/// </summary>
private string GetAmmoName(ProjectileData ammo)
{
if (ammo == null) return "Unknown";
if (!string.IsNullOrEmpty(ammo.displayName)) return ammo.displayName;
if (ammo.prefab != null) return ammo.prefab.name;
return "Unknown Ammo";
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4701ebc78fda468e9d8f3cf7fa7ee9f3
timeCreated: 1764682641

View File

@@ -0,0 +1,209 @@
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Data;
using Unity.Cinemachine;
using UnityEngine;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Manages camera states and transitions for the Fort Fight minigame.
/// Subscribes to turn events and switches camera views accordingly.
/// Uses Cinemachine for smooth camera blending.
/// </summary>
public class CameraController : ManagedBehaviour
{
#region Inspector References
[Header("Cinemachine Cameras")]
[Tooltip("Virtual camera showing wide battlefield view (both forts)")]
[SerializeField] private CinemachineCamera wideViewCamera;
[Tooltip("Player One's dedicated camera (position this in the scene for Player 1's view)")]
[SerializeField] private CinemachineCamera playerOneCamera;
[Tooltip("Player Two's dedicated camera (position this in the scene for Player 2's view)")]
[SerializeField] private CinemachineCamera playerTwoCamera;
[Header("References")]
[Tooltip("Turn manager to subscribe to turn events")]
[SerializeField] private TurnManager turnManager;
[Header("Settings")]
[Tooltip("Use wide view between turns (optional transition)")]
[SerializeField] private bool useWideViewTransitions;
#endregion
#region Public Properties
public CinemachineCamera WideViewCamera => wideViewCamera;
public CinemachineCamera PlayerOneCamera => playerOneCamera;
public CinemachineCamera PlayerTwoCamera => playerTwoCamera;
#endregion
#region Lifecycle
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Validate references
if (wideViewCamera == null)
{
Logging.Error("[CameraController] Wide view camera not assigned!");
}
if (playerOneCamera == null)
{
Logging.Error("[CameraController] Player One camera not assigned!");
}
if (playerTwoCamera == null)
{
Logging.Error("[CameraController] Player Two camera not assigned!");
}
if (turnManager == null)
{
Logging.Error("[CameraController] Turn manager not assigned!");
}
}
internal override void OnManagedStart()
{
base.OnManagedStart();
// Subscribe to turn events
if (turnManager != null)
{
turnManager.OnTurnStarted += HandleTurnStarted;
turnManager.OnTurnEnded += HandleTurnEnded;
Logging.Debug("[CameraController] Subscribed to turn events");
}
// Start in wide view
ShowWideView();
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from events
if (turnManager != null)
{
turnManager.OnTurnStarted -= HandleTurnStarted;
turnManager.OnTurnEnded -= HandleTurnEnded;
}
}
#endregion
#region Event Handlers
/// <summary>
/// Called when a player's turn starts - activate their dedicated camera
/// </summary>
private void HandleTurnStarted(PlayerData player, TurnState turnState)
{
Logging.Debug($"[CameraController] Turn started for {player.PlayerName} (Index: {player.PlayerIndex})");
// Activate the appropriate player camera based on player index
if (player.PlayerIndex == 0)
{
// Player One's turn
ActivateCamera(playerOneCamera);
}
else if (player.PlayerIndex == 1)
{
// Player Two's turn
ActivateCamera(playerTwoCamera);
}
else
{
Logging.Warning($"[CameraController] Unknown player index: {player.PlayerIndex}, defaulting to wide view");
ActivateCamera(wideViewCamera);
}
}
/// <summary>
/// Called when a player's turn ends - optionally switch to wide view for transition
/// </summary>
private void HandleTurnEnded(PlayerData player)
{
Logging.Debug($"[CameraController] Turn ended for {player.PlayerName}");
// Optional: briefly show wide view between turns
if (useWideViewTransitions)
{
ActivateCamera(wideViewCamera);
}
}
/// <summary>
/// Activate a specific camera by setting its priority highest
/// </summary>
private void ActivateCamera(CinemachineCamera camera)
{
if (camera == null) return;
// Set all cameras to low priority
if (wideViewCamera != null) wideViewCamera.Priority.Value = 10;
if (playerOneCamera != null) playerOneCamera.Priority.Value = 10;
if (playerTwoCamera != null) playerTwoCamera.Priority.Value = 10;
// Set target camera to high priority
camera.Priority.Value = 20;
Logging.Debug($"[CameraController] Activated camera: {camera.gameObject.name}");
}
#endregion
#region Public API
/// <summary>
/// Manually switch to wide view (useful for game start/end)
/// </summary>
public void ShowWideView()
{
ActivateCamera(wideViewCamera);
}
/// <summary>
/// Manually switch to a specific player's camera
/// </summary>
public void ShowPlayerCamera(int playerIndex)
{
if (playerIndex == 0)
{
ActivateCamera(playerOneCamera);
}
else if (playerIndex == 1)
{
ActivateCamera(playerTwoCamera);
}
}
#endregion
#region Editor Helpers
#if UNITY_EDITOR
private void OnValidate()
{
// Auto-find TurnManager if not assigned
if (turnManager == null)
{
turnManager = FindFirstObjectByType<TurnManager>();
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa30fcfc16ed44d59edd73fd0224d03c
timeCreated: 1764674161

View File

@@ -0,0 +1,263 @@
using System;
using Core;
using UnityEngine;
using Core.Lifecycle;
using Minigames.FortFight.AI;
using Minigames.FortFight.Data;
using Minigames.FortFight.UI;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Main game manager for Fort Fight minigame.
/// Orchestrates game flow, mode selection, and coordinates between systems.
/// Singleton pattern for easy access.
/// </summary>
public class FortFightGameManager : ManagedBehaviour
{
#region Singleton
private static FortFightGameManager _instance;
public static FortFightGameManager Instance => _instance;
#endregion
#region Inspector References
[Header("Core Systems")]
[SerializeField] private TurnManager turnManager;
[SerializeField] private FortFightAIController aiController;
[SerializeField] private FortManager fortManager;
[Header("UI References")]
[SerializeField] private ModeSelectionPage modeSelectionPage;
[SerializeField] private GameplayPage gameplayPage;
#endregion
#region Events
/// <summary>
/// Fired when game mode is selected and game is starting
/// </summary>
public event Action<FortFightGameMode> OnGameModeSelected;
/// <summary>
/// Fired when the game actually starts (after mode selection)
/// </summary>
public event Action OnGameStarted;
/// <summary>
/// Fired when game ends
/// </summary>
public event Action OnGameEnded;
#endregion
#region State
private FortFightGameMode currentGameMode;
private PlayerData playerOne;
private PlayerData playerTwo;
private bool isGameActive = false;
public FortFightGameMode CurrentGameMode => currentGameMode;
public bool IsGameActive => isGameActive;
#endregion
#region Lifecycle
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Set singleton
if (_instance != null && _instance != this)
{
Logging.Warning("[FortFightGameManager] Multiple instances detected! Destroying duplicate.");
Destroy(gameObject);
return;
}
_instance = this;
// Validate references
ValidateReferences();
}
internal override void OnManagedStart()
{
base.OnManagedStart();
// Show mode selection page
ShowModeSelection();
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
if (_instance == this)
{
_instance = null;
}
// Clear events
OnGameModeSelected = null;
OnGameStarted = null;
OnGameEnded = null;
}
#endregion
#region Validation
private void ValidateReferences()
{
if (turnManager == null)
{
Logging.Error("[FortFightGameManager] TurnManager reference not assigned!");
}
if (aiController == null)
{
Logging.Warning("[FortFightGameManager] AIController reference not assigned! AI mode will not work.");
}
if (modeSelectionPage == null)
{
Logging.Error("[FortFightGameManager] ModeSelectionPage reference not assigned!");
}
if (gameplayPage == null)
{
Logging.Error("[FortFightGameManager] GameplayPage reference not assigned!");
}
}
#endregion
#region Game Flow
/// <summary>
/// Show the mode selection screen
/// </summary>
private void ShowModeSelection()
{
if (modeSelectionPage != null)
{
modeSelectionPage.gameObject.SetActive(true);
modeSelectionPage.TransitionIn();
Logging.Debug("[FortFightGameManager] Showing mode selection page");
}
if (gameplayPage != null)
{
gameplayPage.gameObject.SetActive(false);
}
}
/// <summary>
/// Called when player selects a game mode
/// </summary>
public void SelectGameMode(FortFightGameMode mode)
{
currentGameMode = mode;
Logging.Debug($"[FortFightGameManager] Game mode selected: {mode}");
OnGameModeSelected?.Invoke(mode);
// Initialize players based on mode
InitializePlayers();
// Transition to gameplay
StartGame();
}
/// <summary>
/// Initialize player data based on selected game mode
/// </summary>
private void InitializePlayers()
{
playerOne = new PlayerData("Player", false, 0);
if (currentGameMode == FortFightGameMode.SinglePlayer)
{
playerTwo = new PlayerData("PigMan AI", true, 1);
}
else
{
playerTwo = new PlayerData("Player 2", false, 1);
}
Logging.Debug($"[FortFightGameManager] Players initialized - P1: {playerOne.PlayerName}, P2: {playerTwo.PlayerName}");
// Spawn forts for both players
if (fortManager != null)
{
fortManager.SpawnForts();
Logging.Debug("[FortFightGameManager] Forts spawned for both players");
}
else
{
Logging.Warning("[FortFightGameManager] FortManager not assigned! Forts will not spawn.");
}
}
/// <summary>
/// Start the game
/// </summary>
private void StartGame()
{
// Hide mode selection, show gameplay
if (modeSelectionPage != null)
{
modeSelectionPage.TransitionOut();
}
if (gameplayPage != null)
{
gameplayPage.gameObject.SetActive(true);
gameplayPage.TransitionIn();
}
// Initialize turn manager
if (turnManager != null)
{
turnManager.Initialize(playerOne, playerTwo);
turnManager.StartGame();
}
// Initialize AI if in single player mode
if (currentGameMode == FortFightGameMode.SinglePlayer && aiController != null)
{
aiController.Initialize();
}
isGameActive = true;
OnGameStarted?.Invoke();
Logging.Debug("[FortFightGameManager] Game started!");
}
/// <summary>
/// End the game
/// </summary>
public void EndGame()
{
isGameActive = false;
if (turnManager != null)
{
turnManager.SetGameOver();
}
OnGameEnded?.Invoke();
Logging.Debug("[FortFightGameManager] Game ended");
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 517ef0a4f14e16f42987a95684371b73

View File

@@ -0,0 +1,111 @@
using System.Collections.Generic;
using System.Linq;
using AppleHills.Core.Settings;
using Minigames.FortFight.Data;
using Minigames.FortFight.Settings;
using UnityEngine;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Settings for the Fort Fight minigame.
/// Contains all configurable gameplay values for balancing.
/// </summary>
[CreateAssetMenu(fileName = "FortFightSettings", menuName = "AppleHills/Settings/Fort Fight", order = 8)]
public class FortFightSettings : BaseSettings, IFortFightSettings
{
[Header("Block Material Configurations")]
[Tooltip("HP and mass configurations for each material type")]
[SerializeField] private List<BlockMaterialConfig> materialConfigs = new List<BlockMaterialConfig>
{
new BlockMaterialConfig { material = BlockMaterial.Cardboard, baseHp = 20f, baseMass = 0.5f },
new BlockMaterialConfig { material = BlockMaterial.Metal, baseHp = 50f, baseMass = 2f },
new BlockMaterialConfig { material = BlockMaterial.Glass, baseHp = 15f, baseMass = 0.8f }
};
[Header("Block Size Configurations")]
[Tooltip("HP and mass multipliers for each size type")]
[SerializeField] private List<BlockSizeConfig> sizeConfigs = new List<BlockSizeConfig>
{
new BlockSizeConfig { size = BlockSize.Small, hpMultiplier = 1f, massMultiplier = 0.5f },
new BlockSizeConfig { size = BlockSize.Medium, hpMultiplier = 1.5f, massMultiplier = 1f },
new BlockSizeConfig { size = BlockSize.Large, hpMultiplier = 2f, massMultiplier = 2f }
};
[Header("Weak Point Settings")]
[Tooltip("Radius of explosion effect from weak points")]
[SerializeField] private float weakPointExplosionRadius = 2.5f;
[Tooltip("Base damage dealt by weak point explosion")]
[SerializeField] private float weakPointExplosionDamage = 50f;
[Tooltip("Force applied to blocks hit by weak point explosion")]
[SerializeField] private float weakPointExplosionForce = 300f;
[Header("Fort Configuration")]
[Tooltip("HP percentage threshold for fort defeat (0.3 = 30%)")]
[SerializeField] private float fortDefeatThreshold = 0.3f;
[Tooltip("Global gravity scale for all blocks")]
[SerializeField] private float physicsGravityScale = 1f;
[Header("Visual Settings")]
[Tooltip("Color tint applied to damaged blocks")]
[SerializeField] private Color damageColorTint = new Color(0.5f, 0.5f, 0.5f);
#region IFortFightSettings Implementation
public List<BlockMaterialConfig> MaterialConfigs => materialConfigs;
public List<BlockSizeConfig> SizeConfigs => sizeConfigs;
public float WeakPointExplosionRadius => weakPointExplosionRadius;
public float WeakPointExplosionDamage => weakPointExplosionDamage;
public float WeakPointExplosionForce => weakPointExplosionForce;
public float FortDefeatThreshold => fortDefeatThreshold;
public float PhysicsGravityScale => physicsGravityScale;
public Color DamageColorTint => damageColorTint;
public BlockMaterialConfig GetMaterialConfig(BlockMaterial material)
{
return materialConfigs.FirstOrDefault(c => c.material == material);
}
public BlockSizeConfig GetSizeConfig(BlockSize size)
{
return sizeConfigs.FirstOrDefault(c => c.size == size);
}
#endregion
#region Validation
private void OnValidate()
{
// Ensure defeat threshold is between 0 and 1
fortDefeatThreshold = Mathf.Clamp01(fortDefeatThreshold);
// Ensure all materials are configured
foreach (BlockMaterial material in System.Enum.GetValues(typeof(BlockMaterial)))
{
if (!materialConfigs.Any(c => c.material == material))
{
Debug.LogWarning($"[FortFightSettings] Missing configuration for material: {material}");
}
}
// Ensure all sizes are configured
foreach (BlockSize size in System.Enum.GetValues(typeof(BlockSize)))
{
if (!sizeConfigs.Any(c => c.size == size))
{
Debug.LogWarning($"[FortFightSettings] Missing configuration for size: {size}");
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eaaa527529c5438f80d27ff95c7c7930
timeCreated: 1764669847

View File

@@ -0,0 +1,267 @@
using System;
using Core;
using Core.Lifecycle;
using UnityEngine;
using Minigames.FortFight.Data;
using Minigames.FortFight.Fort;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Manages fort prefab spawning and references during gameplay.
/// </summary>
public class FortManager : ManagedBehaviour
{
#region Inspector Properties
[Header("Fort Prefabs")]
[SerializeField] private GameObject[] premadeFortPrefabs;
[Tooltip("Leave empty to spawn random forts. Assign specific prefabs for testing.")]
[SerializeField] private GameObject debugPlayerFortPrefab;
[SerializeField] private GameObject debugEnemyFortPrefab;
[Header("Spawn Points")]
[SerializeField] private Transform playerSpawnPoint;
[SerializeField] private Transform enemySpawnPoint;
[Header("Settings")]
[SerializeField] private bool useDebugForts = false;
#endregion
#region Events
/// <summary>
/// Fired when player fort is spawned
/// </summary>
public event Action<FortController> OnPlayerFortSpawned;
/// <summary>
/// Fired when enemy fort is spawned
/// </summary>
public event Action<FortController> OnEnemyFortSpawned;
#endregion
#region Properties
public FortController PlayerFort { get; private set; }
public FortController EnemyFort { get; private set; }
#endregion
#region Lifecycle
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Validate spawn points
if (playerSpawnPoint == null)
{
Logging.Error("[FortManager] Player spawn point not assigned!");
}
if (enemySpawnPoint == null)
{
Logging.Error("[FortManager] Enemy spawn point not assigned!");
}
// Validate fort prefabs
if (premadeFortPrefabs == null || premadeFortPrefabs.Length == 0)
{
Logging.Warning("[FortManager] No premade fort prefabs assigned! Add at least one fort prefab.");
}
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
OnPlayerFortSpawned = null;
OnEnemyFortSpawned = null;
}
#endregion
#region Spawning
/// <summary>
/// Spawn forts for both player and enemy at game start
/// </summary>
public void SpawnForts()
{
Logging.Debug("[FortManager] Spawning forts for both players");
// Spawn player fort
if (useDebugForts && debugPlayerFortPrefab != null)
{
PlayerFort = SpawnFort(debugPlayerFortPrefab, playerSpawnPoint, "Player Fort");
}
else
{
PlayerFort = SpawnRandomFort(playerSpawnPoint, "Player Fort");
}
if (PlayerFort != null)
{
OnPlayerFortSpawned?.Invoke(PlayerFort);
}
// Spawn enemy fort
if (useDebugForts && debugEnemyFortPrefab != null)
{
EnemyFort = SpawnFort(debugEnemyFortPrefab, enemySpawnPoint, "Enemy Fort");
}
else
{
EnemyFort = SpawnRandomFort(enemySpawnPoint, "Enemy Fort");
}
if (EnemyFort != null)
{
OnEnemyFortSpawned?.Invoke(EnemyFort);
}
}
/// <summary>
/// Spawn a random fort from the premade prefabs array
/// </summary>
public FortController SpawnRandomFort(Transform spawnPoint, string overrideName = null)
{
if (premadeFortPrefabs == null || premadeFortPrefabs.Length == 0)
{
Logging.Error("[FortManager] Cannot spawn random fort - no prefabs available!");
return null;
}
GameObject randomPrefab = premadeFortPrefabs[UnityEngine.Random.Range(0, premadeFortPrefabs.Length)];
return SpawnFort(randomPrefab, spawnPoint, overrideName);
}
/// <summary>
/// Spawn a specific fort prefab
/// </summary>
public FortController SpawnFort(GameObject fortPrefab, Transform spawnPoint, string overrideName = null)
{
if (fortPrefab == null)
{
Logging.Error("[FortManager] Cannot spawn fort - prefab is null!");
return null;
}
if (spawnPoint == null)
{
Logging.Error("[FortManager] Cannot spawn fort - spawn point is null!");
return null;
}
// Instantiate fort
GameObject fortInstance = Instantiate(fortPrefab, spawnPoint.position, Quaternion.identity, spawnPoint);
fortInstance.name = overrideName ?? fortPrefab.name;
// Get FortController
FortController controller = fortInstance.GetComponent<FortController>();
if (controller == null)
{
Logging.Error($"[FortManager] Fort prefab {fortPrefab.name} is missing FortController component!");
Destroy(fortInstance);
return null;
}
// Fort will self-initialize in Start() and register with this manager
Logging.Debug($"[FortManager] Spawned fort: {controller.FortName} at {spawnPoint.name} (will self-initialize)");
return controller;
}
#endregion
#region Fort Registration
/// <summary>
/// Called by FortController when it finishes initialization.
/// Determines if player/enemy fort and fires appropriate events.
/// </summary>
public void RegisterFort(FortController fort)
{
if (fort == null)
{
Logging.Error("[FortManager] Cannot register null fort!");
return;
}
// Determine if this is player or enemy fort by checking which spawn point it's under
bool isPlayerFort = fort.transform.IsChildOf(playerSpawnPoint);
bool isEnemyFort = fort.transform.IsChildOf(enemySpawnPoint);
if (isPlayerFort)
{
PlayerFort = fort;
Logging.Debug($"[FortManager] Registered PLAYER fort: {fort.FortName}");
OnPlayerFortSpawned?.Invoke(fort);
}
else if (isEnemyFort)
{
EnemyFort = fort;
Logging.Debug($"[FortManager] Registered ENEMY fort: {fort.FortName}");
OnEnemyFortSpawned?.Invoke(fort);
}
else
{
Logging.Warning($"[FortManager] Fort {fort.FortName} is not under player or enemy spawn point! Cannot determine fort type.");
}
}
#endregion
#region Cleanup
/// <summary>
/// Destroy all spawned forts (for game restart)
/// </summary>
public void ClearForts()
{
Logging.Debug("[FortManager] Clearing all forts");
if (PlayerFort != null)
{
Destroy(PlayerFort.gameObject);
PlayerFort = null;
}
if (EnemyFort != null)
{
Destroy(EnemyFort.gameObject);
EnemyFort = null;
}
}
#endregion
#region Queries
/// <summary>
/// Get fort for a specific player
/// </summary>
public FortController GetFortForPlayer(PlayerData player)
{
if (player == null) return null;
return player.PlayerIndex == 0 ? PlayerFort : EnemyFort;
}
/// <summary>
/// Get opponent's fort
/// </summary>
public FortController GetOpponentFort(PlayerData player)
{
if (player == null) return null;
return player.PlayerIndex == 0 ? EnemyFort : PlayerFort;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 47c585eb15414e8f802a6e31cbc6f501
timeCreated: 1764592116

View File

@@ -0,0 +1,125 @@
using Core;
using Minigames.FortFight.Projectiles;
using UnityEngine;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Turn action for projectile launching.
/// Enables slingshot, waits for player to launch, waits for projectile to complete.
/// </summary>
public class ProjectileTurnAction
{
private SlingshotController slingshot;
private AmmunitionManager ammoManager;
private ProjectileBase activeProjectile;
private bool launchComplete = false;
private bool projectileSettled = false;
private float settleTimer = 0f;
private float settleDelay = 2.5f; // Wait 2.5s after impact before ending turn
public bool IsComplete => projectileSettled;
public ProjectileTurnAction(SlingshotController slingshot, AmmunitionManager ammoManager)
{
this.slingshot = slingshot;
this.ammoManager = ammoManager;
}
/// <summary>
/// Execute the turn action - enable slingshot and wait for launch
/// </summary>
public void Execute()
{
Logging.Debug("[ProjectileTurnAction] Executing - enabling slingshot");
// Enable slingshot
if (slingshot != null)
{
slingshot.Enable();
// Subscribe to launch event
slingshot.OnProjectileLaunched += HandleProjectileLaunched;
}
}
/// <summary>
/// Update the action (check if projectile has settled)
/// </summary>
public void Update()
{
if (!launchComplete) return;
// Check if projectile is destroyed or stopped
if (activeProjectile == null)
{
// Projectile destroyed - start settle timer
if (settleTimer == 0f)
{
Logging.Debug("[ProjectileTurnAction] Projectile destroyed - starting settle timer");
}
settleTimer += Time.deltaTime;
if (settleTimer >= settleDelay)
{
projectileSettled = true;
Logging.Debug("[ProjectileTurnAction] Turn action complete");
}
}
else
{
// Check if projectile has stopped moving
Rigidbody2D rb = activeProjectile.GetComponent<Rigidbody2D>();
if (rb != null && rb.linearVelocity.magnitude < 0.5f)
{
settleTimer += Time.deltaTime;
if (settleTimer >= settleDelay)
{
projectileSettled = true;
Logging.Debug("[ProjectileTurnAction] Projectile settled - turn action complete");
}
}
else
{
// Reset settle timer if still moving
settleTimer = 0f;
}
}
}
/// <summary>
/// Cancel the action (disable slingshot)
/// </summary>
public void Cancel()
{
if (slingshot != null)
{
slingshot.Disable();
slingshot.OnProjectileLaunched -= HandleProjectileLaunched;
}
}
private void HandleProjectileLaunched(ProjectileBase projectile)
{
Logging.Debug($"[ProjectileTurnAction] Projectile launched: {projectile.gameObject.name}");
launchComplete = true;
activeProjectile = projectile;
// Disable slingshot after launch
if (slingshot != null)
{
slingshot.Disable();
}
// Trigger cooldown for used ammo
if (ammoManager != null)
{
ammoManager.UseAmmo();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac54e08365f94dbd91d2bace2b5964a6
timeCreated: 1764682659

View File

@@ -0,0 +1,312 @@
using System;
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Data;
using Minigames.FortFight.Projectiles;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Controls slingshot aiming and projectile launching.
/// Angry Birds-style drag-to-aim mechanic with trajectory preview.
/// </summary>
public class SlingshotController : ManagedBehaviour
{
#region Inspector Properties
[Header("Launch Settings")]
[Tooltip("Maximum launch force")]
[SerializeField] private float maxForce = 20f;
[Tooltip("Drag distance to reach max force")]
[SerializeField] private float maxDragDistance = 5f;
[Tooltip("Spawn point for projectiles")]
[SerializeField] private Transform projectileSpawnPoint;
[Header("References")]
[Tooltip("Trajectory preview component")]
[SerializeField] private TrajectoryPreview trajectoryPreview;
[Header("Debug")]
[SerializeField] private bool showDebugLogs = true;
#endregion
#region Events
/// <summary>
/// Fired when projectile is launched. Parameters: (ProjectileBase projectile)
/// </summary>
public event Action<ProjectileBase> OnProjectileLaunched;
#endregion
#region State
private bool isDragging = false;
private Vector2 dragStartPosition;
private ProjectileData currentAmmo;
private Projectiles.ProjectileBase activeProjectile;
private Camera mainCamera;
public bool IsDragging => isDragging;
public bool IsEnabled { get; private set; } = true;
#endregion
#region Lifecycle
internal override void OnManagedAwake()
{
base.OnManagedAwake();
mainCamera = Camera.main;
if (projectileSpawnPoint == null)
{
projectileSpawnPoint = transform;
}
if (trajectoryPreview == null)
{
trajectoryPreview = GetComponent<TrajectoryPreview>();
}
}
internal override void OnManagedStart()
{
base.OnManagedStart();
// Hide trajectory by default
if (trajectoryPreview != null)
{
trajectoryPreview.Hide();
}
}
private void Update()
{
if (!IsEnabled) return;
HandleInput();
}
#endregion
#region Input Handling
private void HandleInput()
{
// Check for mouse/touch input
if (Mouse.current != null)
{
// Mouse down - start drag
if (Mouse.current.leftButton.wasPressedThisFrame)
{
StartDrag(Mouse.current.position.ReadValue());
}
// Mouse held - update drag
if (Mouse.current.leftButton.isPressed && isDragging)
{
UpdateDrag(Mouse.current.position.ReadValue());
}
// Mouse up - release
if (Mouse.current.leftButton.wasReleasedThisFrame && isDragging)
{
EndDrag();
}
}
// Touch input (for mobile)
if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)
{
var touch = Touchscreen.current.primaryTouch;
if (touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Began)
{
StartDrag(touch.position.ReadValue());
}
else if (touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Moved && isDragging)
{
UpdateDrag(touch.position.ReadValue());
}
else if (touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Ended && isDragging)
{
EndDrag();
}
}
}
private void StartDrag(Vector2 screenPosition)
{
if (currentAmmo == null)
{
if (showDebugLogs) Logging.Warning("[SlingshotController] No ammo selected!");
return;
}
isDragging = true;
dragStartPosition = screenPosition;
// Show trajectory preview
if (trajectoryPreview != null)
{
trajectoryPreview.Show();
}
if (showDebugLogs) Logging.Debug($"[SlingshotController] Started drag at {screenPosition}");
}
private void UpdateDrag(Vector2 currentScreenPosition)
{
// Calculate drag vector
Vector2 dragVector = dragStartPosition - currentScreenPosition;
// Calculate force and direction
float dragDistance = dragVector.magnitude;
float force = Mathf.Min(dragDistance / maxDragDistance, 1f) * maxForce;
Vector2 direction = dragVector.normalized;
// Update trajectory preview
if (trajectoryPreview != null)
{
Vector2 worldStartPos = projectileSpawnPoint.position;
trajectoryPreview.UpdateTrajectory(worldStartPos, direction, force);
}
}
private void EndDrag()
{
isDragging = false;
// Hide trajectory
if (trajectoryPreview != null)
{
trajectoryPreview.Hide();
}
// Calculate final launch parameters
Vector2 currentScreenPosition = Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero;
Vector2 dragVector = dragStartPosition - currentScreenPosition;
float dragDistance = dragVector.magnitude;
float force = Mathf.Min(dragDistance / maxDragDistance, 1f) * maxForce;
Vector2 direction = dragVector.normalized;
// Launch projectile
if (force > 0.1f) // Minimum drag threshold
{
LaunchProjectile(direction, force);
}
else
{
if (showDebugLogs) Logging.Debug("[SlingshotController] Drag too short - no launch");
}
}
#endregion
#region Projectile Management
/// <summary>
/// Set the current ammunition type
/// </summary>
public void SetAmmo(ProjectileData ammoData)
{
currentAmmo = ammoData;
string ammoName = GetAmmoName(ammoData);
if (showDebugLogs) Logging.Debug($"[SlingshotController] Ammo set to: {ammoName}");
}
/// <summary>
/// Launch a projectile with calculated force and direction
/// </summary>
private void LaunchProjectile(Vector2 direction, float force)
{
if (currentAmmo == null || currentAmmo.prefab == null)
{
Logging.Error("[SlingshotController] Cannot launch - no ammo or prefab!");
return;
}
// Spawn projectile
GameObject projectileObj = Instantiate(currentAmmo.prefab, projectileSpawnPoint.position, Quaternion.identity);
activeProjectile = projectileObj.GetComponent<Projectiles.ProjectileBase>();
if (activeProjectile == null)
{
Logging.Error($"[SlingshotController] Projectile prefab {currentAmmo.prefab.name} missing ProjectileBase component!");
Destroy(projectileObj);
return;
}
// Launch it
activeProjectile.Launch(direction, force);
string ammoName = GetAmmoName(currentAmmo);
if (showDebugLogs) Logging.Debug($"[SlingshotController] Launched {ammoName} with force {force}");
// Fire event
OnProjectileLaunched?.Invoke(activeProjectile);
}
/// <summary>
/// Get currently active projectile (in flight)
/// </summary>
public Projectiles.ProjectileBase GetActiveProjectile()
{
return activeProjectile;
}
#endregion
#region Enable/Disable
/// <summary>
/// Enable slingshot (allow aiming/launching)
/// </summary>
public void Enable()
{
IsEnabled = true;
if (showDebugLogs) Logging.Debug("[SlingshotController] Enabled");
}
/// <summary>
/// Disable slingshot (prevent aiming/launching)
/// </summary>
public void Disable()
{
IsEnabled = false;
isDragging = false;
if (trajectoryPreview != null)
{
trajectoryPreview.Hide();
}
if (showDebugLogs) Logging.Debug("[SlingshotController] Disabled");
}
#endregion
#region Helpers
/// <summary>
/// Get display name for ammo (uses displayName or prefab name as fallback)
/// </summary>
private string GetAmmoName(ProjectileData ammo)
{
if (ammo == null) return "None";
if (!string.IsNullOrEmpty(ammo.displayName)) return ammo.displayName;
if (ammo.prefab != null) return ammo.prefab.name;
return "Unknown";
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc81b72132764f09a0ba180c90b432cf
timeCreated: 1764682598

View File

@@ -0,0 +1,113 @@
using Core;
using UnityEngine;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Displays trajectory prediction line for projectile launches.
/// Shows dotted line preview of projectile arc.
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class TrajectoryPreview : MonoBehaviour
{
[Header("Trajectory Settings")]
[Tooltip("Number of points to simulate")]
[SerializeField] private int simulationSteps = 30;
[Tooltip("Time step for each simulation point")]
[SerializeField] private float timeStep = 0.1f;
[Tooltip("Mass to use for simulation (should match projectile mass)")]
[SerializeField] private float projectileMass = 1f;
[Tooltip("Drag to use for simulation")]
[SerializeField] private float drag = 0f;
[Header("Visual")]
[Tooltip("Color of trajectory line")]
[SerializeField] private Color lineColor = Color.yellow;
[Tooltip("Width of trajectory line")]
[SerializeField] private float lineWidth = 0.1f;
private LineRenderer lineRenderer;
private void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
// Configure line renderer
if (lineRenderer != null)
{
lineRenderer.startWidth = lineWidth;
lineRenderer.endWidth = lineWidth;
lineRenderer.startColor = lineColor;
lineRenderer.endColor = lineColor;
lineRenderer.positionCount = simulationSteps;
lineRenderer.enabled = false;
}
}
/// <summary>
/// Show the trajectory line
/// </summary>
public void Show()
{
if (lineRenderer != null)
{
lineRenderer.enabled = true;
}
}
/// <summary>
/// Hide the trajectory line
/// </summary>
public void Hide()
{
if (lineRenderer != null)
{
lineRenderer.enabled = false;
}
}
/// <summary>
/// Update trajectory preview with new launch parameters
/// </summary>
public void UpdateTrajectory(Vector2 startPosition, Vector2 direction, float force)
{
if (lineRenderer == null) return;
Vector2 velocity = direction * force;
Vector2 gravity = Physics2D.gravity;
// Simulate trajectory points
Vector3[] points = new Vector3[simulationSteps];
Vector2 currentPos = startPosition;
Vector2 currentVelocity = velocity;
for (int i = 0; i < simulationSteps; i++)
{
points[i] = currentPos;
// Apply physics simulation
currentVelocity += gravity * timeStep;
currentPos += currentVelocity * timeStep;
// Optional: Stop if hits ground (y < some threshold)
if (currentPos.y < -10f)
{
// Fill remaining points at ground level
for (int j = i + 1; j < simulationSteps; j++)
{
points[j] = new Vector3(currentPos.x, -10f, 0);
}
break;
}
}
lineRenderer.positionCount = simulationSteps;
lineRenderer.SetPositions(points);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b1e26667c6d4415f8dc51e4a58ba9479
timeCreated: 1764682615

View File

@@ -0,0 +1,148 @@
using System;
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Data;
namespace Minigames.FortFight.Core
{
/// <summary>
/// Manages turn order and turn state for Fort Fight minigame.
/// Handles transitions between Player One, Player Two/AI turns.
/// </summary>
public class TurnManager : ManagedBehaviour
{
#region Events
/// <summary>
/// Fired when a new turn begins. Parameters: (PlayerData currentPlayer, TurnState turnState)
/// </summary>
public event Action<PlayerData, TurnState> OnTurnStarted;
/// <summary>
/// Fired when the current turn ends. Parameters: (PlayerData playerWhoFinished)
/// </summary>
public event Action<PlayerData> OnTurnEnded;
/// <summary>
/// Fired when transitioning between turns
/// </summary>
public event Action OnTurnTransitioning;
#endregion
#region State
private TurnState currentTurnState = TurnState.PlayerOneTurn;
private PlayerData playerOne;
private PlayerData playerTwo;
private PlayerData currentPlayer;
private int turnCount = 0;
public TurnState CurrentTurnState => currentTurnState;
public PlayerData CurrentPlayer => currentPlayer;
public int TurnCount => turnCount;
#endregion
#region Initialization
/// <summary>
/// Initialize the turn manager with player data
/// </summary>
public void Initialize(PlayerData pPlayerOne, PlayerData pPlayerTwo)
{
this.playerOne = pPlayerOne;
this.playerTwo = pPlayerTwo;
Logging.Debug($"[TurnManager] Initialized with P1: {pPlayerOne.PlayerName} (AI: {pPlayerOne.IsAI}), P2: {pPlayerTwo.PlayerName} (AI: {pPlayerTwo.IsAI})");
}
/// <summary>
/// Start the first turn
/// </summary>
public void StartGame()
{
turnCount = 0;
currentTurnState = TurnState.PlayerOneTurn;
currentPlayer = playerOne;
Logging.Debug($"[TurnManager] Game started. First turn: {currentPlayer.PlayerName}");
OnTurnStarted?.Invoke(currentPlayer, currentTurnState);
}
#endregion
#region Turn Management
/// <summary>
/// End the current turn and advance to the next player
/// </summary>
public void EndTurn()
{
if (currentTurnState == TurnState.GameOver)
{
Logging.Warning("[TurnManager] Cannot end turn - game is over");
return;
}
Logging.Debug($"[TurnManager] Turn ended for {currentPlayer.PlayerName}");
OnTurnEnded?.Invoke(currentPlayer);
// Transition state
currentTurnState = TurnState.TransitioningTurn;
OnTurnTransitioning?.Invoke();
// Advance to next player
AdvanceToNextPlayer();
}
/// <summary>
/// Advance to the next player's turn
/// </summary>
private void AdvanceToNextPlayer()
{
turnCount++;
// Switch players
if (currentPlayer == playerOne)
{
currentPlayer = playerTwo;
currentTurnState = playerTwo.IsAI ? TurnState.AITurn : TurnState.PlayerTwoTurn;
}
else
{
currentPlayer = playerOne;
currentTurnState = TurnState.PlayerOneTurn;
}
Logging.Debug($"[TurnManager] Advanced to turn {turnCount}. Current player: {currentPlayer.PlayerName} (State: {currentTurnState})");
OnTurnStarted?.Invoke(currentPlayer, currentTurnState);
}
/// <summary>
/// Force game over state
/// </summary>
public void SetGameOver()
{
currentTurnState = TurnState.GameOver;
Logging.Debug("[TurnManager] Game over state set");
}
#endregion
#region Cleanup
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Clear all event listeners
OnTurnStarted = null;
OnTurnEnded = null;
OnTurnTransitioning = null;
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c0b22cad084c0ba44b522474269c2c4b

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 08a9c8eba88cf1148bcf11e305a91051
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using System;
namespace Minigames.FortFight.Data
{
/// <summary>
/// Game mode for Fort Fight minigame
/// </summary>
public enum FortFightGameMode
{
SinglePlayer, // Player vs AI
TwoPlayer // Player vs Player
}
/// <summary>
/// Current turn state in the game
/// </summary>
public enum TurnState
{
PlayerOneTurn,
PlayerTwoTurn,
AITurn,
TransitioningTurn,
GameOver
}
/// <summary>
/// Material types for fort blocks
/// </summary>
public enum BlockMaterial
{
Cardboard,
Metal,
Glass
}
/// <summary>
/// Size categories for fort blocks
/// </summary>
public enum BlockSize
{
Small,
Medium,
Large
}
/// <summary>
/// Types of projectiles available
/// </summary>
public enum ProjectileType
{
Toaster, // Standard physics projectile
Vacuum, // Heavy, rolls on floor
CeilingFan, // Drops straight down
TrashBag // Explodes on impact
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9891698193c757344bc2f3f26730248a

View File

@@ -0,0 +1,23 @@
using System;
namespace Minigames.FortFight.Data
{
/// <summary>
/// Represents a player in the Fort Fight minigame
/// </summary>
[Serializable]
public class PlayerData
{
public string PlayerName;
public bool IsAI;
public int PlayerIndex; // 0 for Player One, 1 for Player Two/AI
public PlayerData(string name, bool isAI, int playerIndex)
{
PlayerName = name;
IsAI = isAI;
PlayerIndex = playerIndex;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f310a90c43a9b3049b875c84f2d9043a

View File

@@ -0,0 +1,50 @@
using UnityEngine;
namespace Minigames.FortFight.Data
{
/// <summary>
/// ScriptableObject defining a projectile type for the ammunition system.
/// Only stores prefab reference and UI data - stats come from the prefab's ProjectileBase component.
/// </summary>
[CreateAssetMenu(fileName = "ProjectileData", menuName = "AppleHills/Fort Fight/Projectile Data", order = 1)]
public class ProjectileData : ScriptableObject
{
[Header("Prefab")]
[Tooltip("Prefab for this projectile (should have ProjectileBase component)")]
public GameObject prefab;
[Header("Ammunition System")]
[Tooltip("Cooldown time in seconds after use")]
public float cooldownTime = 5f;
[Header("UI")]
[Tooltip("Icon sprite for ammunition UI")]
public Sprite icon;
[Tooltip("Display name for this projectile type")]
public string displayName;
[Tooltip("Description of projectile behavior (for tutorial/UI)")]
[TextArea(2, 4)]
public string description;
/// <summary>
/// Get the ProjectileBase component from the prefab (for reading stats)
/// </summary>
public Projectiles.ProjectileBase GetProjectileComponent()
{
if (prefab == null) return null;
return prefab.GetComponent<Projectiles.ProjectileBase>();
}
/// <summary>
/// Get damage value from prefab
/// </summary>
public float GetDamage()
{
var component = GetProjectileComponent();
return component != null ? component.Damage : 0f;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 973f02063e5446598db4cbffdbf8f113
timeCreated: 1764682274

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5434add8e2e43cabd0ce4283636ca83
timeCreated: 1764591745

View File

@@ -0,0 +1,383 @@
using System;
using Core;
using Core.Lifecycle;
using UnityEngine;
using Minigames.FortFight.Data;
namespace Minigames.FortFight.Fort
{
/// <summary>
/// Individual fort block with HP, material properties, and physics.
/// Component attached to each block GameObject in a fort prefab.
/// </summary>
[RequireComponent(typeof(Rigidbody2D), typeof(Collider2D))]
public class FortBlock : ManagedBehaviour
{
#region Inspector Properties
[Header("Block Configuration")]
[SerializeField] private BlockMaterial material = BlockMaterial.Cardboard;
[SerializeField] private BlockSize size = BlockSize.Medium;
[SerializeField] private bool isWeakPoint = false;
[Header("Weak Point Settings (if applicable)")]
[Tooltip("Visual indicator shown in editor/game for weak points")]
[SerializeField] private GameObject weakPointVisualIndicator;
[Tooltip("Visual explosion effect prefab")]
[SerializeField] private GameObject explosionEffectPrefab;
[Header("Visual Feedback")]
[SerializeField] private SpriteRenderer spriteRenderer;
#endregion
#region Events
/// <summary>
/// Fired when this block is destroyed. Parameters: (FortBlock block, float damageTaken)
/// </summary>
public event Action<FortBlock, float> OnBlockDestroyed;
/// <summary>
/// Fired when this block takes damage. Parameters: (float currentHP, float maxHP)
/// </summary>
public event Action<float, float> OnBlockDamaged;
#endregion
#region Properties
public BlockMaterial Material => material;
public BlockSize Size => size;
public bool IsWeakPoint => isWeakPoint;
public float CurrentHp => currentHp;
public float MaxHp => maxHp;
public float HpPercentage => maxHp > 0 ? (currentHp / maxHp) * 100f : 0f;
#endregion
#region Private State
private float maxHp;
private float currentHp;
private FortController parentFort;
private Rigidbody2D rb2D;
private Collider2D blockCollider;
private bool isDestroyed = false;
// Cached settings
private AppleHills.Core.Settings.IFortFightSettings _cachedSettings;
private AppleHills.Core.Settings.IFortFightSettings CachedSettings
{
get
{
if (_cachedSettings == null)
{
_cachedSettings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
}
return _cachedSettings;
}
}
#endregion
#region Lifecycle
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe events
OnBlockDestroyed = null;
OnBlockDamaged = null;
}
#endregion
#region Initialization
/// <summary>
/// Initialize this block. Called explicitly by parent FortController.
/// DO NOT call from Awake/Start - parent controls initialization timing.
/// </summary>
public void Initialize()
{
// Cache components
rb2D = GetComponent<Rigidbody2D>();
blockCollider = GetComponent<Collider2D>();
if (spriteRenderer == null)
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
if (isDestroyed)
{
Logging.Warning($"[FortBlock] Cannot initialize destroyed block {gameObject.name}");
return;
}
// Calculate HP based on material and size
CalculateHp();
// Configure physics properties
ConfigurePhysics();
// Show/hide weak point indicator
if (weakPointVisualIndicator != null)
{
weakPointVisualIndicator.SetActive(isWeakPoint);
}
Logging.Debug($"[FortBlock] {gameObject.name} initialized: {material} {size}, HP: {maxHp}");
}
#endregion
#region HP Calculation
private void CalculateHp()
{
// Get material config
var materialConfig = CachedSettings.GetMaterialConfig(material);
float baseMaterialHp = materialConfig?.baseHp ?? 20f;
// Get size config
var sizeConfig = CachedSettings.GetSizeConfig(size);
float sizeMultiplier = sizeConfig?.hpMultiplier ?? 1f;
maxHp = baseMaterialHp * sizeMultiplier;
currentHp = maxHp;
Logging.Debug($"[FortBlock] {gameObject.name} initialized: {material} {size}, HP: {maxHp}");
}
#endregion
#region Physics Configuration
private void ConfigurePhysics()
{
if (rb2D == null) return;
// Get material config
var materialConfig = CachedSettings.GetMaterialConfig(material);
float baseMass = materialConfig?.baseMass ?? 1f;
// Get size config
var sizeConfig = CachedSettings.GetSizeConfig(size);
float sizeMultiplier = sizeConfig?.massMultiplier ?? 1f;
rb2D.mass = baseMass * sizeMultiplier;
rb2D.gravityScale = CachedSettings.PhysicsGravityScale;
rb2D.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
}
#endregion
#region Damage System
/// <summary>
/// Apply damage to this block
/// </summary>
public void TakeDamage(float damage)
{
if (isDestroyed) return;
currentHp -= damage;
currentHp = Mathf.Max(0f, currentHp);
Logging.Debug($"[FortBlock] {gameObject.name} took {damage} damage. HP: {currentHp}/{maxHp} ({HpPercentage:F1}%)");
OnBlockDamaged?.Invoke(currentHp, maxHp);
// Visual feedback
UpdateVisualDamage();
// Check if destroyed
if (currentHp <= 0f)
{
DestroyBlock();
}
}
private void UpdateVisualDamage()
{
if (spriteRenderer == null) return;
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
Color targetColor = settings?.DamageColorTint ?? new Color(0.5f, 0.5f, 0.5f);
// Darken sprite based on damage
float damagePercent = 1f - (currentHp / maxHp);
Color damageColor = Color.Lerp(Color.white, targetColor, damagePercent);
spriteRenderer.color = damageColor;
}
#endregion
#region Destruction
private void DestroyBlock()
{
if (isDestroyed) return;
isDestroyed = true;
Logging.Debug($"[FortBlock] {gameObject.name} destroyed! Weak point: {isWeakPoint}");
// Trigger explosion if weak point
if (isWeakPoint)
{
TriggerWeakPointExplosion();
}
// Notify listeners
OnBlockDestroyed?.Invoke(this, maxHp);
// Spawn destruction effects (placeholder)
SpawnDestructionEffect();
// Destroy GameObject
Destroy(gameObject);
}
private void TriggerWeakPointExplosion()
{
float explosionRadius = CachedSettings.WeakPointExplosionRadius;
float explosionDamage = CachedSettings.WeakPointExplosionDamage;
float explosionForce = CachedSettings.WeakPointExplosionForce;
Logging.Debug($"[FortBlock] ========================================");
Logging.Debug($"[FortBlock] 💥 WEAK POINT EXPLOSION TRIGGERED!");
Logging.Debug($"[FortBlock] Position: {transform.position}");
Logging.Debug($"[FortBlock] Explosion Radius: {explosionRadius}");
Logging.Debug($"[FortBlock] Explosion Damage: {explosionDamage}");
Logging.Debug($"[FortBlock] Explosion Force: {explosionForce}");
Logging.Debug($"[FortBlock] ========================================");
// Spawn explosion effect
if (explosionEffectPrefab != null)
{
Logging.Debug($"[FortBlock] Spawning explosion effect prefab");
GameObject explosion = Instantiate(explosionEffectPrefab, transform.position, Quaternion.identity);
Destroy(explosion, 3f); // Auto-cleanup after 3 seconds
}
else
{
Logging.Debug($"[FortBlock] No explosion effect prefab (visual only)");
}
// Find nearby blocks and damage them
Collider2D[] nearbyColliders = Physics2D.OverlapCircleAll(transform.position, explosionRadius);
Logging.Debug($"[FortBlock] Physics2D.OverlapCircleAll found {nearbyColliders.Length} colliders");
if (nearbyColliders.Length <= 1)
{
Logging.Warning($"[FortBlock] ⚠️ Only found {nearbyColliders.Length} colliders! Other blocks need BoxCollider2D with 'Is Trigger' UNCHECKED");
}
int blocksHit = 0;
foreach (Collider2D col in nearbyColliders)
{
if (col.gameObject == gameObject)
{
Logging.Debug($"[FortBlock] Skipping self");
continue; // Skip self
}
Logging.Debug($"[FortBlock] Checking collider: {col.gameObject.name}");
FortBlock nearbyBlock = col.GetComponent<FortBlock>();
if (nearbyBlock != null && !nearbyBlock.isDestroyed)
{
Vector2 explosionCenter = transform.position;
float distance = Vector2.Distance(explosionCenter, nearbyBlock.transform.position);
// Calculate damage with falloff
float damageFalloff = 1f - (distance / explosionRadius);
float actualDamage = explosionDamage * damageFalloff;
// Apply damage
nearbyBlock.TakeDamage(actualDamage);
// Apply explosion force (2D equivalent of AddExplosionForce)
Rigidbody2D nearbyRb = nearbyBlock.GetComponent<Rigidbody2D>();
if (nearbyRb != null)
{
ApplyExplosionForce2D(nearbyRb, explosionForce, explosionCenter, explosionRadius);
Logging.Debug($"[FortBlock] ✓ HIT: {nearbyBlock.gameObject.name} - Damage: {actualDamage:F1}, Force applied from center");
}
else
{
Logging.Debug($"[FortBlock] ✓ HIT: {nearbyBlock.gameObject.name} - Damage: {actualDamage:F1} (no Rigidbody2D)");
}
blocksHit++;
}
else if (nearbyBlock == null)
{
Logging.Debug($"[FortBlock] × MISS: {col.gameObject.name} has no FortBlock component");
}
else
{
Logging.Debug($"[FortBlock] × SKIP: {col.gameObject.name} already destroyed");
}
}
Logging.Debug($"[FortBlock] Explosion complete. Damaged {blocksHit} blocks");
Logging.Debug($"[FortBlock] ========================================");
// TODO: Add screen shake effect
// TODO: Play explosion sound via AudioManager
}
/// <summary>
/// Apply explosion force to a Rigidbody2D (2D equivalent of Rigidbody.AddExplosionForce).
/// Force decreases with distance from explosion center.
/// </summary>
private void ApplyExplosionForce2D(Rigidbody2D rb, float force, Vector2 center, float radius)
{
Vector2 direction = (rb.position - center);
float distance = direction.magnitude;
if (distance == 0f) return; // Avoid division by zero
// Normalize direction
direction /= distance;
// Calculate force with linear falloff (like Unity's 3D AddExplosionForce)
float forceMagnitude = force * (1f - (distance / radius));
// Apply force as impulse
rb.AddForce(direction * forceMagnitude, ForceMode2D.Impulse);
}
private void SpawnDestructionEffect()
{
// Placeholder for destruction particles
// TODO: Create material-specific destruction effects
Logging.Debug($"[FortBlock] Spawning destruction effect for {material} block");
}
#endregion
#region Debug Helpers
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (isWeakPoint)
{
// Draw explosion radius in editor using settings
float radius = AppleHills.SettingsAccess.GetWeakPointExplosionRadius();
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ace8ce8bea324389a9955e63081ccff7
timeCreated: 1764591745

View File

@@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Core;
using Core.Lifecycle;
using UnityEngine;
using AppleHills.Core.Settings;
namespace Minigames.FortFight.Fort
{
/// <summary>
/// Root component of fort prefabs. Manages collection of child FortBlocks and tracks total HP.
/// </summary>
public class FortController : ManagedBehaviour
{
#region Inspector Properties
[Header("Fort Configuration")]
[SerializeField] private string fortName = "Unnamed Fort";
[Header("Debug")]
[SerializeField] private bool showDebugInfo = true;
#endregion
#region Events
/// <summary>
/// Fired when fort takes damage. Parameters: (float damage, float hpPercentage)
/// </summary>
public event Action<float, float> OnFortDamaged;
/// <summary>
/// Fired when fort is defeated (HP < 30%)
/// </summary>
public event Action OnFortDefeated;
/// <summary>
/// Fired when a block is destroyed. Parameters: (FortBlock block)
/// </summary>
public event Action<FortBlock> OnBlockDestroyed;
#endregion
#region Properties
public string FortName => fortName;
public float MaxFortHp => maxFortHp;
public float CurrentFortHp => currentFortHp;
public float HpPercentage => maxFortHp > 0 ? (currentFortHp / maxFortHp) * 100f : 0f;
public int TotalBlockCount => blocks.Count;
public int InitialBlockCount => initialBlockCount;
public bool IsDefeated { get; private set; }
#endregion
#region Private State
private List<FortBlock> blocks = new List<FortBlock>();
private float maxFortHp = 0f;
private float currentFortHp = 0f;
private int initialBlockCount = 0;
private bool isInitialized = false;
// Cached settings
private IFortFightSettings _cachedSettings;
private IFortFightSettings CachedSettings
{
get
{
if (_cachedSettings == null)
{
_cachedSettings = GameManager.GetSettingsObject<IFortFightSettings>();
}
return _cachedSettings;
}
}
#endregion
#region Lifecycle
internal override void OnManagedStart()
{
base.OnManagedStart();
// Self-initialize: discover blocks, register with manager
InitializeFort();
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from all block events
foreach (FortBlock block in blocks)
{
if (block != null)
{
block.OnBlockDestroyed -= HandleBlockDestroyed;
block.OnBlockDamaged -= HandleBlockDamaged;
}
}
blocks.Clear();
// Clear events
OnFortDamaged = null;
OnFortDefeated = null;
OnBlockDestroyed = null;
}
#endregion
#region Initialization
/// <summary>
/// Initialize fort: discover child blocks, calculate HP, register with manager.
/// Called automatically in Start() - no external calls needed.
/// </summary>
private void InitializeFort()
{
if (isInitialized)
{
Logging.Warning($"[FortController] {fortName} already initialized!");
return;
}
Logging.Debug($"[FortController] {fortName} - Starting self-initialization");
// Step 1: Discover and register child blocks
DiscoverAndRegisterBlocks();
// Step 2: Register with central manager
RegisterWithManager();
isInitialized = true;
Logging.Debug($"[FortController] {fortName} - Initialization complete");
}
/// <summary>
/// Discover, initialize, and register all child blocks.
/// This ensures deterministic initialization order.
/// </summary>
private void DiscoverAndRegisterBlocks()
{
FortBlock[] childBlocks = GetComponentsInChildren<FortBlock>();
if (childBlocks.Length == 0)
{
Logging.Error($"[FortController] {fortName} has no blocks!");
return;
}
if (childBlocks.Length > 10)
{
Logging.Warning($"[FortController] {fortName} has {childBlocks.Length} blocks (max 10 recommended)");
}
Logging.Debug($"[FortController] {fortName} - Discovered {childBlocks.Length} blocks, initializing...");
// Step 1: Initialize each block (calculate HP, configure physics)
foreach (FortBlock block in childBlocks)
{
block.Initialize();
}
// Step 2: Register each block (subscribe to events, track HP)
foreach (FortBlock block in childBlocks)
{
RegisterBlock(block);
}
initialBlockCount = blocks.Count;
Logging.Debug($"[FortController] {fortName} - Initialized and registered {blocks.Count} blocks, Total HP: {maxFortHp:F0}");
}
/// <summary>
/// Register this fort with the central FortManager.
/// Manager determines if player/enemy and handles UI binding.
/// </summary>
private void RegisterWithManager()
{
Core.FortManager manager = FindFirstObjectByType<Core.FortManager>();
if (manager == null)
{
Logging.Error($"[FortController] {fortName} - FortManager not found! Cannot complete initialization.");
return;
}
manager.RegisterFort(this);
Logging.Debug($"[FortController] {fortName} - Registered with FortManager");
}
#endregion
#region Block Management
/// <summary>
/// Register a block with this fort controller. Called by FortBlock on start.
/// </summary>
public void RegisterBlock(FortBlock block)
{
if (block == null) return;
if (!blocks.Contains(block))
{
blocks.Add(block);
maxFortHp += block.MaxHp;
currentFortHp += block.MaxHp;
// Subscribe to block events
block.OnBlockDestroyed += HandleBlockDestroyed;
block.OnBlockDamaged += HandleBlockDamaged;
if (showDebugInfo)
{
Logging.Debug($"[FortController] Registered block: {block.gameObject.name} ({block.Material} {block.Size}, HP: {block.MaxHp})");
}
}
}
/// <summary>
/// Get all blocks marked as weak points
/// </summary>
public List<FortBlock> GetWeakPoints()
{
return blocks.Where(b => b != null && b.IsWeakPoint).ToList();
}
/// <summary>
/// Get all remaining blocks
/// </summary>
public List<FortBlock> GetAllBlocks()
{
return new List<FortBlock>(blocks);
}
/// <summary>
/// Get a random block (for AI targeting)
/// </summary>
public FortBlock GetRandomBlock()
{
if (blocks.Count == 0) return null;
return blocks[UnityEngine.Random.Range(0, blocks.Count)];
}
#endregion
#region Event Handlers
private void HandleBlockDestroyed(FortBlock block, float blockMaxHp)
{
if (block == null) return;
Logging.Debug($"[FortController] {fortName} - Block destroyed: {block.gameObject.name}");
// Remove from list
blocks.Remove(block);
// Update current HP
currentFortHp -= blockMaxHp;
currentFortHp = Mathf.Max(0f, currentFortHp);
// Notify listeners
OnBlockDestroyed?.Invoke(block);
OnFortDamaged?.Invoke(blockMaxHp, HpPercentage);
// Check defeat condition
CheckDefeatCondition();
if (showDebugInfo)
{
Logging.Debug($"[FortController] {fortName} - HP: {currentFortHp:F0}/{maxFortHp:F0} ({HpPercentage:F1}%), Blocks: {blocks.Count}/{initialBlockCount}");
}
}
private void HandleBlockDamaged(float currentBlockHp, float maxBlockHp)
{
// Block damaged but not destroyed
// Could add visual feedback here if needed
}
#endregion
#region Defeat Condition
private void CheckDefeatCondition()
{
if (IsDefeated) return;
float defeatThreshold = CachedSettings?.FortDefeatThreshold ?? 0.3f;
float defeatThresholdPercent = defeatThreshold * 100f;
// Defeat if HP below threshold
if (HpPercentage < defeatThresholdPercent)
{
IsDefeated = true;
Logging.Debug($"[FortController] {fortName} DEFEATED! Final HP: {HpPercentage:F1}% (threshold: {defeatThresholdPercent:F1}%)");
OnFortDefeated?.Invoke();
}
}
#endregion
#region Debug Helpers
private void OnGUI()
{
if (!showDebugInfo || !Application.isPlaying) return;
// Display fort HP in scene view (for testing)
Vector3 screenPos = Camera.main.WorldToScreenPoint(transform.position + Vector3.up * 2f);
if (screenPos.z > 0)
{
GUI.color = IsDefeated ? Color.red : Color.white;
GUI.Label(new Rect(screenPos.x - 50, Screen.height - screenPos.y, 100, 30),
$"{fortName}\nHP: {HpPercentage:F0}%");
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 05031222348c421ab564757f52f24952
timeCreated: 1764591745

View File

@@ -0,0 +1,139 @@
using Core;
using UnityEngine;
using Core.Lifecycle;
using Minigames.FortFight.Core;
using Minigames.FortFight.Data;
namespace Minigames.FortFight
{
/// <summary>
/// Debug script to test Phase 1 implementation without full UI setup.
/// Attach to a GameObject in the scene to run automated tests.
/// </summary>
public class FortFightPhase1Tester : ManagedBehaviour
{
[Header("Test Settings")]
[SerializeField] private bool runAutomatedTest = true;
[SerializeField] private FortFightGameMode testMode = FortFightGameMode.SinglePlayer;
[SerializeField] private int numberOfTurnsToTest = 6;
private FortFightGameManager gameManager;
private TurnManager turnManager;
private int playerActionsExecuted = 0;
internal override void OnManagedStart()
{
base.OnManagedStart();
if (!runAutomatedTest)
{
Logging.Debug("[FortFightPhase1Tester] Automated test disabled");
return;
}
// Find managers
gameManager = FindFirstObjectByType<FortFightGameManager>();
turnManager = FindFirstObjectByType<TurnManager>();
if (gameManager == null)
{
Logging.Error("[FortFightPhase1Tester] FortFightGameManager not found!");
return;
}
if (turnManager == null)
{
Logging.Error("[FortFightPhase1Tester] TurnManager not found!");
return;
}
// Subscribe to events for testing
gameManager.OnGameModeSelected += OnGameModeSelected;
gameManager.OnGameStarted += OnGameStarted;
turnManager.OnTurnStarted += OnTurnStarted;
turnManager.OnTurnEnded += OnTurnEnded;
// Start test
Logging.Debug("=== STARTING PHASE 1 AUTOMATED TEST ===");
Logging.Debug($"Testing mode: {testMode}");
Logging.Debug($"Will execute {numberOfTurnsToTest} turns total");
// Simulate mode selection
gameManager.SelectGameMode(testMode);
}
private void OnGameModeSelected(FortFightGameMode mode)
{
Logging.Debug($"[TEST] Game mode selected: {mode}");
}
private void OnGameStarted()
{
Logging.Debug("[TEST] Game started!");
}
private void OnTurnStarted(PlayerData currentPlayer, TurnState turnState)
{
Logging.Debug($"[TEST] Turn started - Player: {currentPlayer.PlayerName}, State: {turnState}, Turn #: {turnManager.TurnCount + 1}");
// If it's a human player's turn (not AI), simulate taking action after a delay
if (turnState == TurnState.PlayerOneTurn || turnState == TurnState.PlayerTwoTurn)
{
Invoke(nameof(SimulatePlayerAction), 0.5f);
}
// Check if we've reached the test limit
if (turnManager.TurnCount >= numberOfTurnsToTest)
{
Logging.Debug("=== TEST COMPLETE ===");
Logging.Debug($"Successfully completed {numberOfTurnsToTest} turns!");
Logging.Debug($"Player actions executed: {playerActionsExecuted}");
// End the game
gameManager.EndGame();
}
}
private void OnTurnEnded(PlayerData player)
{
Logging.Debug($"[TEST] Turn ended for {player.PlayerName}");
}
private void SimulatePlayerAction()
{
if (turnManager == null) return;
TurnState state = turnManager.CurrentTurnState;
// Only simulate if it's still a player turn (not transitioned yet)
if (state == TurnState.PlayerOneTurn || state == TurnState.PlayerTwoTurn)
{
PlayerData currentPlayer = turnManager.CurrentPlayer;
Logging.Debug($"[TEST] Simulating action for {currentPlayer.PlayerName}");
playerActionsExecuted++;
// End the turn
turnManager.EndTurn();
}
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from events
if (gameManager != null)
{
gameManager.OnGameModeSelected -= OnGameModeSelected;
gameManager.OnGameStarted -= OnGameStarted;
}
if (turnManager != null)
{
turnManager.OnTurnStarted -= OnTurnStarted;
turnManager.OnTurnEnded -= OnTurnEnded;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 756ba6afb97885c43a3d0eac023b3797

View File

@@ -0,0 +1,184 @@
using Core;
using UnityEngine;
using UnityEngine.InputSystem;
using Core.Lifecycle;
using Minigames.FortFight.Core;
using Minigames.FortFight.Fort;
namespace Minigames.FortFight
{
/// <summary>
/// Debug script to test Phase 2 fort system.
/// Attach to a GameObject in the scene to manually trigger fort damage for testing.
/// </summary>
public class FortFightPhase2Tester : ManagedBehaviour
{
[Header("Test Settings")]
[SerializeField] private bool enableKeyboardControls = true;
[SerializeField] private float testDamageAmount = 25f;
[SerializeField] private bool autoTestOnStart = false;
[SerializeField] private float autoTestDelay = 2f;
private FortManager fortManager;
private FortController playerFort;
private FortController enemyFort;
internal override void OnManagedStart()
{
base.OnManagedStart();
// Find fort manager
fortManager = FindFirstObjectByType<FortManager>();
if (fortManager == null)
{
Logging.Error("[FortFightPhase2Tester] FortManager not found!");
return;
}
// Subscribe to fort spawn events
fortManager.OnPlayerFortSpawned += OnPlayerFortSpawned;
fortManager.OnEnemyFortSpawned += OnEnemyFortSpawned;
Logging.Debug("=== PHASE 2 TESTING CONTROLS ===");
Logging.Debug("Press '1' to damage random PLAYER fort block");
Logging.Debug("Press '2' to damage random ENEMY fort block");
Logging.Debug("Press '3' to damage PLAYER fort weak point");
Logging.Debug("Press '4' to damage ENEMY fort weak point");
Logging.Debug("Press '5' to destroy random PLAYER block");
Logging.Debug("Press '6' to destroy random ENEMY block");
Logging.Debug("================================");
if (autoTestOnStart)
{
InvokeRepeating(nameof(AutoTest), autoTestDelay, autoTestDelay);
}
}
private void OnPlayerFortSpawned(FortController fort)
{
playerFort = fort;
Logging.Debug($"[Phase2Tester] Player fort spawned: {fort.FortName}");
}
private void OnEnemyFortSpawned(FortController fort)
{
enemyFort = fort;
Logging.Debug($"[Phase2Tester] Enemy fort spawned: {fort.FortName}");
}
private void Update()
{
if (!enableKeyboardControls) return;
if (Keyboard.current == null) return;
// Test controls
if (Keyboard.current.digit1Key.wasPressedThisFrame)
{
DamageRandomBlock(playerFort, testDamageAmount);
}
if (Keyboard.current.digit2Key.wasPressedThisFrame)
{
DamageRandomBlock(enemyFort, testDamageAmount);
}
if (Keyboard.current.digit3Key.wasPressedThisFrame)
{
DamageWeakPoint(playerFort);
}
if (Keyboard.current.digit4Key.wasPressedThisFrame)
{
DamageWeakPoint(enemyFort);
}
if (Keyboard.current.digit5Key.wasPressedThisFrame)
{
DestroyRandomBlock(playerFort);
}
if (Keyboard.current.digit6Key.wasPressedThisFrame)
{
DestroyRandomBlock(enemyFort);
}
}
private void AutoTest()
{
// Alternate between player and enemy
if (Random.value > 0.5f)
{
DamageRandomBlock(playerFort, testDamageAmount);
}
else
{
DamageRandomBlock(enemyFort, testDamageAmount);
}
}
private void DamageRandomBlock(FortController fort, float damage)
{
if (fort == null || fort.TotalBlockCount == 0)
{
Logging.Warning("[Phase2Tester] No fort or blocks available to damage!");
return;
}
FortBlock randomBlock = fort.GetRandomBlock();
if (randomBlock != null)
{
Logging.Debug($"[Phase2Tester] Damaging {fort.FortName} block '{randomBlock.gameObject.name}' for {damage} damage");
randomBlock.TakeDamage(damage);
}
}
private void DamageWeakPoint(FortController fort)
{
if (fort == null)
{
Logging.Warning("[Phase2Tester] Fort is null!");
return;
}
var weakPoints = fort.GetWeakPoints();
if (weakPoints.Count == 0)
{
Logging.Warning($"[Phase2Tester] {fort.FortName} has no weak points!");
return;
}
FortBlock weakPoint = weakPoints[Random.Range(0, weakPoints.Count)];
Logging.Debug($"[Phase2Tester] Destroying {fort.FortName} WEAK POINT '{weakPoint.gameObject.name}'");
weakPoint.TakeDamage(999f); // Instant destruction
}
private void DestroyRandomBlock(FortController fort)
{
if (fort == null || fort.TotalBlockCount == 0)
{
Logging.Warning("[Phase2Tester] No fort or blocks available to destroy!");
return;
}
FortBlock randomBlock = fort.GetRandomBlock();
if (randomBlock != null)
{
Logging.Debug($"[Phase2Tester] DESTROYING {fort.FortName} block '{randomBlock.gameObject.name}'");
randomBlock.TakeDamage(999f); // Instant destruction
}
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
if (fortManager != null)
{
fortManager.OnPlayerFortSpawned -= OnPlayerFortSpawned;
fortManager.OnEnemyFortSpawned -= OnEnemyFortSpawned;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 74f26236978245cdaf33909a7c242cbf
timeCreated: 1764592117

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f2ab5d80875486aa447f9c4afcbdec1
timeCreated: 1764682302

View File

@@ -0,0 +1,52 @@
using System.Collections;
using Core;
using UnityEngine;
namespace Minigames.FortFight.Projectiles
{
/// <summary>
/// Ceiling Fan projectile - drops straight down when ability is activated.
/// Player taps screen mid-flight to activate drop.
/// </summary>
public class CeilingFanProjectile : ProjectileBase
{
[Header("Ceiling Fan Specific")]
[Tooltip("Speed of downward drop")]
[SerializeField] private float dropSpeed = 20f;
[Tooltip("Delay before dropping")]
[SerializeField] private float dropDelay = 0.2f;
public override void ActivateAbility()
{
base.ActivateAbility();
if (AbilityActivated)
{
Logging.Debug("[CeilingFanProjectile] Ability activated - dropping straight down");
StartCoroutine(DropCoroutine());
}
}
private IEnumerator DropCoroutine()
{
// Stop all velocity
if (rb2D != null)
{
rb2D.linearVelocity = Vector2.zero;
rb2D.angularVelocity = 0f;
}
// Wait brief moment
yield return new WaitForSeconds(dropDelay);
// Drop straight down
if (rb2D != null)
{
rb2D.linearVelocity = Vector2.down * dropSpeed;
Logging.Debug($"[CeilingFanProjectile] Dropping with velocity: {rb2D.linearVelocity}");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e10ba9bd4bcd40da87ecb3efe5b78467
timeCreated: 1764682337

View File

@@ -0,0 +1,250 @@
using System;
using Core;
using Core.Lifecycle;
using Minigames.FortFight.Fort;
using UnityEngine;
namespace Minigames.FortFight.Projectiles
{
/// <summary>
/// Base class for all projectile types in Fort Fight.
/// Handles physics, collision, and basic damage dealing.
/// Subclasses override ActivateAbility() and OnHit() for unique behaviors.
/// </summary>
[RequireComponent(typeof(Rigidbody2D), typeof(Collider2D))]
public abstract class ProjectileBase : ManagedBehaviour
{
#region Inspector Properties
[Header("Projectile Stats")]
[Tooltip("Base damage dealt on impact")]
[SerializeField] protected float damage = 20f;
[Tooltip("Mass for physics (affects trajectory)")]
[SerializeField] protected float mass = 1f;
[Header("Visuals")]
[Tooltip("Sprite renderer for projectile")]
[SerializeField] protected SpriteRenderer spriteRenderer;
[Header("Effects")]
[Tooltip("Particle effect on impact (optional)")]
[SerializeField] protected GameObject impactEffectPrefab;
#endregion
#region Events
/// <summary>
/// Fired when projectile is launched. Parameters: (ProjectileBase projectile)
/// </summary>
public event Action<ProjectileBase> OnLaunched;
/// <summary>
/// Fired when projectile hits something. Parameters: (ProjectileBase projectile, Collider2D hit)
/// </summary>
public event Action<ProjectileBase, Collider2D> OnImpact;
/// <summary>
/// Fired when projectile is destroyed. Parameters: (ProjectileBase projectile)
/// </summary>
public event Action<ProjectileBase> OnDestroyed;
#endregion
#region Properties
public float Damage => damage;
public bool IsLaunched { get; protected set; }
public bool AbilityActivated { get; protected set; }
public Vector2 LaunchDirection { get; protected set; }
public float LaunchForce { get; protected set; }
#endregion
#region Components
protected Rigidbody2D rb2D;
protected Collider2D projectileCollider;
#endregion
#region Lifecycle
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Cache components
rb2D = GetComponent<Rigidbody2D>();
projectileCollider = GetComponent<Collider2D>();
if (spriteRenderer == null)
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
// Configure rigidbody
if (rb2D != null)
{
rb2D.mass = mass;
rb2D.gravityScale = 1f;
rb2D.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
}
}
#endregion
#region Launch
/// <summary>
/// Launch the projectile with given direction and force.
/// Called by SlingshotController.
/// </summary>
public virtual void Launch(Vector2 direction, float force)
{
if (IsLaunched)
{
Logging.Warning($"[ProjectileBase] {gameObject.name} already launched!");
return;
}
LaunchDirection = direction.normalized;
LaunchForce = force;
IsLaunched = true;
// Apply physics impulse
if (rb2D != null)
{
rb2D.AddForce(LaunchDirection * LaunchForce, ForceMode2D.Impulse);
Logging.Debug($"[ProjectileBase] Launched {gameObject.name} with force {LaunchForce} in direction {LaunchDirection}");
}
// Fire event
OnLaunched?.Invoke(this);
}
#endregion
#region Ability
/// <summary>
/// Activate projectile's special ability (mid-flight).
/// Override in subclasses for unique behaviors.
/// Called when player taps screen during flight.
/// </summary>
public virtual void ActivateAbility()
{
if (!IsLaunched)
{
Logging.Warning($"[ProjectileBase] Cannot activate ability - projectile not launched yet!");
return;
}
if (AbilityActivated)
{
Logging.Warning($"[ProjectileBase] Ability already activated!");
return;
}
AbilityActivated = true;
Logging.Debug($"[ProjectileBase] {gameObject.name} ability activated");
// Subclasses override this for special behavior
}
#endregion
#region Collision
private void OnCollisionEnter2D(Collision2D collision)
{
if (!IsLaunched) return;
Logging.Debug($"[ProjectileBase] {gameObject.name} hit {collision.gameObject.name}");
// Check if hit a fort block
FortBlock block = collision.gameObject.GetComponent<FortBlock>();
if (block != null)
{
// Deal damage to block
block.TakeDamage(damage);
Logging.Debug($"[ProjectileBase] Dealt {damage} damage to {block.gameObject.name}");
}
// Spawn impact effect
SpawnImpactEffect(collision.contacts[0].point);
// Fire impact event
OnImpact?.Invoke(this, collision.collider);
// Call subclass-specific hit behavior
OnHit(collision.collider);
// Destroy projectile after hit (subclasses can override)
DestroyProjectile();
}
/// <summary>
/// Called when projectile hits something.
/// Override in subclasses for projectile-specific behavior.
/// </summary>
protected virtual void OnHit(Collider2D hit)
{
// Subclasses override for special behavior
// e.g., Vacuum continues sliding, TrashBag splits, etc.
}
#endregion
#region Effects
/// <summary>
/// Spawn impact particle effect
/// </summary>
protected void SpawnImpactEffect(Vector2 position)
{
if (impactEffectPrefab != null)
{
GameObject effect = Instantiate(impactEffectPrefab, position, Quaternion.identity);
Destroy(effect, 2f); // Auto-cleanup
}
}
#endregion
#region Destruction
/// <summary>
/// Destroy the projectile.
/// Can be overridden by subclasses for delayed destruction.
/// </summary>
protected virtual void DestroyProjectile()
{
Logging.Debug($"[ProjectileBase] Destroying {gameObject.name}");
// Fire destroyed event
OnDestroyed?.Invoke(this);
// Destroy GameObject
Destroy(gameObject);
}
#endregion
#region Debug
private void OnDrawGizmos()
{
if (IsLaunched && Application.isPlaying)
{
// Draw launch direction
Gizmos.color = Color.yellow;
Gizmos.DrawLine(transform.position, transform.position + (Vector3)(LaunchDirection * 2f));
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 70f37c48406847cdabd5589910220fdf
timeCreated: 1764682302

View File

@@ -0,0 +1,22 @@
using Core;
using UnityEngine;
namespace Minigames.FortFight.Projectiles
{
/// <summary>
/// Standard projectile - no special ability.
/// Moderate damage, standard physics arc.
/// </summary>
public class ToasterProjectile : ProjectileBase
{
// Toaster is the basic projectile - uses base class behavior
// No special ability needed
public override void ActivateAbility()
{
// Toaster has no special ability
Logging.Debug("[ToasterProjectile] Toaster has no special ability");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6ecf658b5965496abda845de1a28e227
timeCreated: 1764682312

View File

@@ -0,0 +1,120 @@
using Core;
using UnityEngine;
namespace Minigames.FortFight.Projectiles
{
/// <summary>
/// Trash Bag projectile - splits into multiple smaller pieces on impact.
/// Deals AOE damage in a forward cone.
/// </summary>
public class TrashBagProjectile : ProjectileBase
{
[Header("Trash Bag Specific")]
[Tooltip("Number of trash pieces to spawn on impact")]
[SerializeField] private int trashPieceCount = 6;
[Tooltip("Prefab for individual trash pieces")]
[SerializeField] private GameObject trashPiecePrefab;
[Tooltip("Force applied to each trash piece")]
[SerializeField] private float pieceForce = 8f;
[Tooltip("Spread angle for trash pieces (degrees)")]
[SerializeField] private float spreadAngle = 30f;
[Tooltip("Damage dealt by each trash piece")]
[SerializeField] private float pieceDamage = 8f;
protected override void OnHit(Collider2D hit)
{
base.OnHit(hit);
Logging.Debug($"[TrashBagProjectile] Splitting into {trashPieceCount} pieces");
SpawnTrashPieces(hit.transform.position);
}
/// <summary>
/// Spawn multiple trash pieces in a forward cone
/// </summary>
private void SpawnTrashPieces(Vector2 impactPoint)
{
if (trashPiecePrefab == null)
{
Logging.Warning("[TrashBagProjectile] No trash piece prefab assigned!");
return;
}
// Calculate forward direction from velocity
Vector2 forwardDirection = rb2D.linearVelocity.normalized;
if (forwardDirection == Vector2.zero)
{
forwardDirection = LaunchDirection;
}
// Spawn pieces in a cone
for (int i = 0; i < trashPieceCount; i++)
{
// Calculate angle for this piece
float angleOffset = Mathf.Lerp(-spreadAngle, spreadAngle, i / (float)(trashPieceCount - 1));
float angleRadians = Mathf.Atan2(forwardDirection.y, forwardDirection.x) + angleOffset * Mathf.Deg2Rad;
Vector2 pieceDirection = new Vector2(Mathf.Cos(angleRadians), Mathf.Sin(angleRadians));
// Spawn trash piece
GameObject piece = Instantiate(trashPiecePrefab, impactPoint, Quaternion.identity);
// Setup trash piece physics
Rigidbody2D pieceRb = piece.GetComponent<Rigidbody2D>();
if (pieceRb != null)
{
pieceRb.AddForce(pieceDirection * pieceForce, ForceMode2D.Impulse);
}
// Setup trash piece damage (if it has a component)
TrashPiece trashPieceComponent = piece.GetComponent<TrashPiece>();
if (trashPieceComponent != null)
{
trashPieceComponent.Initialize(pieceDamage);
}
// Auto-destroy after 3 seconds
Destroy(piece, 3f);
Logging.Debug($"[TrashBagProjectile] Spawned trash piece {i} in direction {pieceDirection}");
}
}
}
/// <summary>
/// Component for individual trash pieces spawned by TrashBagProjectile.
/// Deals damage on collision.
/// </summary>
public class TrashPiece : MonoBehaviour
{
private float damage;
private bool hasHit = false;
public void Initialize(float pieceDamage)
{
this.damage = pieceDamage;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (hasHit) return;
hasHit = true;
// Check if hit a fort block
var block = collision.gameObject.GetComponent<Fort.FortBlock>();
if (block != null)
{
block.TakeDamage(damage);
Logging.Debug($"[TrashPiece] Dealt {damage} damage to {block.gameObject.name}");
}
// Destroy this piece
Destroy(gameObject, 0.1f);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0996e59b91e48f8a542ab9294b11a74
timeCreated: 1764682467

View File

@@ -0,0 +1,82 @@
using System.Collections;
using Core;
using UnityEngine;
namespace Minigames.FortFight.Projectiles
{
/// <summary>
/// Vacuum projectile - high mass, slides along ground after landing.
/// On ground impact: disables gravity, moves horizontally for 2 seconds.
/// </summary>
public class VacuumProjectile : ProjectileBase
{
[Header("Vacuum Specific")]
[Tooltip("Speed when sliding on ground")]
[SerializeField] private float slideSpeed = 10f;
[Tooltip("How long to slide before destroying")]
[SerializeField] private float slideDuration = 2f;
[Tooltip("Layer mask for ground detection")]
[SerializeField] private LayerMask groundLayer = 1; // Default layer
private bool isSliding = false;
protected override void OnHit(Collider2D hit)
{
// Check if hit ground
if (((1 << hit.gameObject.layer) & groundLayer) != 0)
{
Logging.Debug("[VacuumProjectile] Hit ground - starting slide");
StartSliding();
}
// If hit wall or fort block, destroy immediately (handled by base class)
}
/// <summary>
/// Start sliding behavior after hitting ground
/// </summary>
private void StartSliding()
{
if (isSliding) return;
isSliding = true;
// Disable gravity
if (rb2D != null)
{
rb2D.gravityScale = 0f;
// Set velocity to horizontal only (preserve direction)
Vector2 horizontalVelocity = new Vector2(rb2D.linearVelocity.x, 0f).normalized * slideSpeed;
rb2D.linearVelocity = horizontalVelocity;
Logging.Debug($"[VacuumProjectile] Sliding with velocity: {horizontalVelocity}");
}
// Destroy after slide duration
StartCoroutine(SlideCoroutine());
}
private IEnumerator SlideCoroutine()
{
yield return new WaitForSeconds(slideDuration);
Logging.Debug("[VacuumProjectile] Slide duration ended - destroying");
DestroyProjectile();
}
/// <summary>
/// Override to NOT destroy immediately on ground hit
/// </summary>
protected override void DestroyProjectile()
{
// Only destroy if we're done sliding or hit a wall
if (!isSliding || rb2D.linearVelocity.magnitude < 1f)
{
base.DestroyProjectile();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb85d181808c411b8bd1335aa7d35257
timeCreated: 1764682326

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 90c9b2a87d284c5ba24cce60865c31a4
timeCreated: 1764669813

View File

@@ -0,0 +1,39 @@
using System;
using Minigames.FortFight.Data;
using UnityEngine;
namespace Minigames.FortFight.Settings
{
/// <summary>
/// Configuration for a specific block material (Cardboard, Metal, Glass).
/// </summary>
[Serializable]
public class BlockMaterialConfig
{
[Tooltip("The material type this configuration applies to")]
public BlockMaterial material;
[Tooltip("Base HP for this material before size multiplier")]
public float baseHp;
[Tooltip("Base mass for physics simulation before size multiplier")]
public float baseMass;
}
/// <summary>
/// Configuration for a specific block size (Small, Medium, Large).
/// </summary>
[Serializable]
public class BlockSizeConfig
{
[Tooltip("The size type this configuration applies to")]
public BlockSize size;
[Tooltip("HP multiplier applied to base material HP")]
public float hpMultiplier;
[Tooltip("Mass multiplier applied to base material mass")]
public float massMultiplier;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 731e8965ca0149d79420ee0b15a4e94f
timeCreated: 1764669813

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d97440ab83b1f824e81ff627610e13d0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,198 @@
using Core;
using Core.Lifecycle;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Minigames.FortFight.Fort;
namespace Minigames.FortFight.UI
{
/// <summary>
/// Displays HP bar and percentage for a fort
/// </summary>
public class FortHealthUI : ManagedBehaviour
{
#region Inspector Properties
[Header("UI References")]
[SerializeField] private Slider hpSlider;
[SerializeField] private TextMeshProUGUI hpPercentageText;
[SerializeField] private TextMeshProUGUI fortNameText;
[Header("Visual Feedback")]
[SerializeField] private Image fillImage;
[SerializeField] private Color healthyColor = Color.green;
[SerializeField] private Color damagedColor = Color.yellow;
[SerializeField] private Color criticalColor = Color.red;
[Header("Auto-Binding (for Dynamic Spawning)")]
[SerializeField] private bool autoBindToFort = true;
[SerializeField] private bool isPlayerFort = true;
[Tooltip("Leave empty to auto-find")]
[SerializeField] private Core.FortManager fortManager;
#endregion
#region Private State
private FortController trackedFort;
#endregion
#region Lifecycle
internal override void OnManagedStart()
{
base.OnManagedStart();
// Auto-bind to dynamically spawned forts
if (autoBindToFort)
{
SetupAutoBinding();
}
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from fort events
if (trackedFort != null)
{
trackedFort.OnFortDamaged -= OnFortDamaged;
}
// Unsubscribe from fort manager
if (fortManager != null)
{
fortManager.OnPlayerFortSpawned -= OnFortSpawned;
fortManager.OnEnemyFortSpawned -= OnFortSpawned;
}
}
#endregion
#region Auto-Binding
private void SetupAutoBinding()
{
// Find FortManager if not assigned
if (fortManager == null)
{
fortManager = FindFirstObjectByType<Core.FortManager>();
}
if (fortManager == null)
{
Logging.Warning($"[FortHealthUI] Cannot auto-bind: FortManager not found. HP UI will not update.");
return;
}
// Subscribe to appropriate spawn event
if (isPlayerFort)
{
fortManager.OnPlayerFortSpawned += OnFortSpawned;
Logging.Debug($"[FortHealthUI] Subscribed to PLAYER fort spawn");
}
else
{
fortManager.OnEnemyFortSpawned += OnFortSpawned;
Logging.Debug($"[FortHealthUI] Subscribed to ENEMY fort spawn");
}
}
private void OnFortSpawned(FortController fort)
{
BindToFort(fort);
}
#endregion
#region Setup
/// <summary>
/// Bind this UI to a specific fort
/// </summary>
public void BindToFort(FortController fort)
{
if (fort == null)
{
Logging.Warning("[FortHealthUI] Cannot bind to null fort!");
return;
}
// Unsubscribe from previous fort
if (trackedFort != null)
{
trackedFort.OnFortDamaged -= OnFortDamaged;
}
trackedFort = fort;
// Subscribe to fort events
trackedFort.OnFortDamaged += OnFortDamaged;
// Initialize UI
if (fortNameText != null)
{
fortNameText.text = fort.FortName;
}
UpdateDisplay();
Logging.Debug($"[FortHealthUI] Bound to fort: {fort.FortName}");
}
#endregion
#region Event Handlers
private void OnFortDamaged(float damage, float hpPercentage)
{
UpdateDisplay();
}
#endregion
#region Display Update
private void UpdateDisplay()
{
if (trackedFort == null) return;
float hpPercent = trackedFort.HpPercentage;
// Update slider
if (hpSlider != null)
{
hpSlider.value = hpPercent / 100f;
}
// Update text
if (hpPercentageText != null)
{
hpPercentageText.text = $"{hpPercent:F0}%";
}
// Update color based on HP
if (fillImage != null)
{
if (hpPercent > 60f)
{
fillImage.color = healthyColor;
}
else if (hpPercent > 30f)
{
fillImage.color = damagedColor;
}
else
{
fillImage.color = criticalColor;
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ed9e5253aa2a40c9a9028767466456b1
timeCreated: 1764592117

View File

@@ -0,0 +1,266 @@
using Core;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UI.Core;
using Minigames.FortFight.Core;
using Minigames.FortFight.Data;
using Pixelplacement;
namespace Minigames.FortFight.UI
{
/// <summary>
/// Main gameplay UI page for Fort Fight minigame.
/// Displays turn info and allows player to take actions (stubbed for Phase 1).
/// </summary>
public class GameplayPage : UIPage
{
[Header("UI Elements")]
[SerializeField] private TextMeshProUGUI turnIndicatorText;
[SerializeField] private TextMeshProUGUI currentPlayerText;
[SerializeField] private Button takeActionButton;
[SerializeField] private TextMeshProUGUI actionButtonText;
[Header("Optional Visual Elements")]
[SerializeField] private CanvasGroup canvasGroup;
[SerializeField] private GameObject playerActionPanel;
[SerializeField] private GameObject aiTurnPanel;
private TurnManager turnManager;
#region Initialization
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Validate references
ValidateReferences();
// Set up button
if (takeActionButton != null)
{
takeActionButton.onClick.AddListener(OnTakeActionClicked);
}
// Set up canvas group
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
}
}
internal override void OnManagedStart()
{
base.OnManagedStart();
// Get turn manager reference
turnManager = FindObjectOfType<TurnManager>();
if (turnManager != null)
{
turnManager.OnTurnStarted += OnTurnStarted;
turnManager.OnTurnEnded += OnTurnEnded;
}
else
{
Logging.Error("[GameplayPage] TurnManager not found!");
}
}
private void ValidateReferences()
{
if (turnIndicatorText == null)
{
Logging.Warning("[GameplayPage] Turn indicator text not assigned!");
}
if (currentPlayerText == null)
{
Logging.Warning("[GameplayPage] Current player text not assigned!");
}
if (takeActionButton == null)
{
Logging.Error("[GameplayPage] Take action button not assigned!");
}
if (actionButtonText == null)
{
Logging.Warning("[GameplayPage] Action button text not assigned!");
}
}
#endregion
#region Turn Events
/// <summary>
/// Called when a new turn starts
/// </summary>
private void OnTurnStarted(PlayerData currentPlayer, TurnState turnState)
{
Logging.Debug($"[GameplayPage] Turn started - Player: {currentPlayer.PlayerName}, State: {turnState}");
UpdateTurnUI(currentPlayer, turnState);
}
/// <summary>
/// Called when the current turn ends
/// </summary>
private void OnTurnEnded(PlayerData player)
{
Logging.Debug($"[GameplayPage] Turn ended for {player.PlayerName}");
}
#endregion
#region UI Updates
/// <summary>
/// Update the UI to reflect current turn state
/// </summary>
private void UpdateTurnUI(PlayerData currentPlayer, TurnState turnState)
{
// Update turn counter
if (turnIndicatorText != null && turnManager != null)
{
turnIndicatorText.text = $"Turn {turnManager.TurnCount + 1}";
}
// Update current player display
if (currentPlayerText != null)
{
currentPlayerText.text = $"{currentPlayer.PlayerName}'s Turn";
}
// Show/hide appropriate panels based on turn state
if (turnState == TurnState.AITurn)
{
// AI turn - hide player controls
if (playerActionPanel != null)
{
playerActionPanel.SetActive(false);
}
if (aiTurnPanel != null)
{
aiTurnPanel.SetActive(true);
}
if (takeActionButton != null)
{
takeActionButton.gameObject.SetActive(false);
}
}
else if (turnState == TurnState.PlayerOneTurn || turnState == TurnState.PlayerTwoTurn)
{
// Player turn - show controls
if (playerActionPanel != null)
{
playerActionPanel.SetActive(true);
}
if (aiTurnPanel != null)
{
aiTurnPanel.SetActive(false);
}
if (takeActionButton != null)
{
takeActionButton.gameObject.SetActive(true);
}
if (actionButtonText != null)
{
actionButtonText.text = "Take Action (Stubbed)";
}
}
}
#endregion
#region Button Callbacks
/// <summary>
/// Called when player clicks the "Take Action" button
/// STUBBED for Phase 1 - just logs and ends turn
/// </summary>
private void OnTakeActionClicked()
{
if (turnManager == null)
{
Logging.Error("[GameplayPage] Cannot take action - TurnManager not found!");
return;
}
PlayerData currentPlayer = turnManager.CurrentPlayer;
// STUBBED: Log the action
Logging.Debug($"[GameplayPage] {currentPlayer.PlayerName} takes action! (STUBBED - no actual combat yet)");
// TODO Phase 3: Replace with actual slingshot/shooting interface
// End the turn
turnManager.EndTurn();
}
#endregion
#region Transitions
protected override void DoTransitionIn(System.Action onComplete)
{
// Simple fade in if canvas group is available
if (canvasGroup != null)
{
canvasGroup.alpha = 0f;
Tween.CanvasGroupAlpha(canvasGroup, 1f, transitionDuration, 0f, Tween.EaseOut,
completeCallback: () => onComplete?.Invoke());
}
else
{
onComplete?.Invoke();
}
}
protected override void DoTransitionOut(System.Action onComplete)
{
// Simple fade out if canvas group is available
if (canvasGroup != null)
{
Tween.CanvasGroupAlpha(canvasGroup, 0f, transitionDuration, 0f, Tween.EaseIn,
completeCallback: () => onComplete?.Invoke());
}
else
{
onComplete?.Invoke();
}
}
#endregion
#region Cleanup
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from turn manager events
if (turnManager != null)
{
turnManager.OnTurnStarted -= OnTurnStarted;
turnManager.OnTurnEnded -= OnTurnEnded;
}
// Unsubscribe from button
if (takeActionButton != null)
{
takeActionButton.onClick.RemoveListener(OnTakeActionClicked);
}
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e22e2729514a5ee40bb2d4c23fbeb75f

View File

@@ -0,0 +1,150 @@
using Core;
using UnityEngine;
using UnityEngine.UI;
using UI.Core;
using Minigames.FortFight.Core;
using Minigames.FortFight.Data;
using Pixelplacement;
namespace Minigames.FortFight.UI
{
/// <summary>
/// UI page for selecting single-player or two-player mode
/// </summary>
public class ModeSelectionPage : UIPage
{
[Header("Mode Selection Buttons")]
[SerializeField] private Button singlePlayerButton;
[SerializeField] private Button twoPlayerButton;
[Header("Optional Visual Elements")]
[SerializeField] private GameObject titleText;
[SerializeField] private CanvasGroup canvasGroup;
#region Initialization
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Validate button references
if (singlePlayerButton == null)
{
Logging.Error("[ModeSelectionPage] Single player button not assigned!");
}
else
{
singlePlayerButton.onClick.AddListener(OnSinglePlayerSelected);
}
if (twoPlayerButton == null)
{
Logging.Error("[ModeSelectionPage] Two player button not assigned!");
}
else
{
twoPlayerButton.onClick.AddListener(OnTwoPlayerSelected);
}
// Set up canvas group if available
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
}
}
#endregion
#region Button Callbacks
/// <summary>
/// Called when single player button is clicked
/// </summary>
private void OnSinglePlayerSelected()
{
Logging.Debug("[ModeSelectionPage] Single player mode selected");
if (FortFightGameManager.Instance != null)
{
FortFightGameManager.Instance.SelectGameMode(FortFightGameMode.SinglePlayer);
}
else
{
Logging.Error("[ModeSelectionPage] FortFightGameManager instance not found!");
}
}
/// <summary>
/// Called when two player button is clicked
/// </summary>
private void OnTwoPlayerSelected()
{
Logging.Debug("[ModeSelectionPage] Two player mode selected");
if (FortFightGameManager.Instance != null)
{
FortFightGameManager.Instance.SelectGameMode(FortFightGameMode.TwoPlayer);
}
else
{
Logging.Error("[ModeSelectionPage] FortFightGameManager instance not found!");
}
}
#endregion
#region Transitions
protected override void DoTransitionIn(System.Action onComplete)
{
// Simple fade in if canvas group is available
if (canvasGroup != null)
{
canvasGroup.alpha = 0f;
Tween.CanvasGroupAlpha(canvasGroup, 1f, transitionDuration, 0f, Tween.EaseOut,
completeCallback: () => onComplete?.Invoke());
}
else
{
onComplete?.Invoke();
}
}
protected override void DoTransitionOut(System.Action onComplete)
{
// Simple fade out if canvas group is available
if (canvasGroup != null)
{
Tween.CanvasGroupAlpha(canvasGroup, 0f, transitionDuration, 0f, Tween.EaseIn,
completeCallback: () => onComplete?.Invoke());
}
else
{
onComplete?.Invoke();
}
}
#endregion
#region Cleanup
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
// Unsubscribe from button events
if (singlePlayerButton != null)
{
singlePlayerButton.onClick.RemoveListener(OnSinglePlayerSelected);
}
if (twoPlayerButton != null)
{
twoPlayerButton.onClick.RemoveListener(OnTwoPlayerSelected);
}
}
#endregion
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3c1ab5687204db54eaa4ea7f812b3c06

View File

@@ -236,7 +236,7 @@ namespace UI
case "Quarry":
currentUIMode = UIMode.Puzzle;
break;
case "DivingForPictures" or "CardQualityControl" or "BirdPoop":
case "DivingForPictures" or "CardQualityControl" or "BirdPoop" or "FortFight":
currentUIMode = UIMode.Minigame;
break;
case "StatueDecoration":

View File

@@ -16,7 +16,7 @@ MonoBehaviour:
pauseTimeOnPauseGame: 0
useSaveLoadSystem: 1
autoClearSaves: 0
dontSaveOnQuit: 1
dontSaveOnQuit: 0
bootstrapLogVerbosity: 0
settingsLogVerbosity: 0
gameManagerLogVerbosity: 0

View File

@@ -0,0 +1,40 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: eaaa527529c5438f80d27ff95c7c7930, type: 3}
m_Name: FortFightSettings
m_EditorClassIdentifier: AppleHillsScripts::Minigames.FortFight.Core.FortFightSettings
materialConfigs:
- material: 0
baseHp: 20
baseMass: 0.5
- material: 1
baseHp: 50
baseMass: 2
- material: 2
baseHp: 15
baseMass: 0.8
sizeConfigs:
- size: 0
hpMultiplier: 1
massMultiplier: 0.5
- size: 1
hpMultiplier: 1.5
massMultiplier: 1
- size: 2
hpMultiplier: 2
massMultiplier: 2
weakPointExplosionRadius: 5
weakPointExplosionDamage: 50
weakPointExplosionForce: 50
fortDefeatThreshold: 0.3
physicsGravityScale: 1
damageColorTint: {r: 1, g: 0, b: 0, a: 1}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 60eb7d51afc72e845836d38e77462855
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: