diff --git a/.gitignore b/.gitignore
index 6582eaf..faf6bd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Visual Studio 2015 user specific files
.vs/
+.idea/
# Compiled Object files
*.slo
diff --git a/.idea/.idea.TurnBased/.idea/.gitignore b/.idea/.idea.TurnBased/.idea/.gitignore
new file mode 100644
index 0000000..bdaabaf
--- /dev/null
+++ b/.idea/.idea.TurnBased/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/contentModel.xml
+/projectSettingsUpdater.xml
+/.idea.TurnBased.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.TurnBased/.idea/.name b/.idea/.idea.TurnBased/.idea/.name
new file mode 100644
index 0000000..496e6cf
--- /dev/null
+++ b/.idea/.idea.TurnBased/.idea/.name
@@ -0,0 +1 @@
+TurnBased
\ No newline at end of file
diff --git a/.idea/.idea.TurnBased/.idea/encodings.xml b/.idea/.idea.TurnBased/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.TurnBased/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.TurnBased/.idea/indexLayout.xml b/.idea/.idea.TurnBased/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.TurnBased/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.TurnBased/.idea/vcs.xml b/.idea/.idea.TurnBased/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.TurnBased/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini
index e69de29..8b13789 100644
--- a/Config/DefaultEditor.ini
+++ b/Config/DefaultEditor.ini
@@ -0,0 +1 @@
+
diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini
index 50c64f0..57ead6b 100644
--- a/Config/DefaultEngine.ini
+++ b/Config/DefaultEngine.ini
@@ -1,7 +1,7 @@
[/Script/EngineSettings.GameMapsSettings]
-EditorStartupMap=/Game/BattleField/BattleFieldMap.BattleFieldMap
+EditorStartupMap=/Game/MainMenu/MainMenuLevel.MainMenuLevel
LocalMapOptions=
TransitionMap=None
bUseSplitscreen=True
@@ -12,7 +12,7 @@ bOffsetPlayerGamepadIds=False
GameInstanceClass=/Script/Engine.GameInstance
GameDefaultMap=/Game/MainMenu/MainMenuLevel.MainMenuLevel
ServerDefaultMap=/Engine/Maps/Entry.Entry
-GlobalDefaultGameMode=/Script/TurnBasedTutorial.MyGameMode
+GlobalDefaultGameMode=/Script/Engine.GameMode
GlobalDefaultServerGameMode=None
[/Script/HardwareTargeting.HardwareTargetingSettings]
@@ -26,3 +26,30 @@ AppliedDefaultGraphicsPerformance=Maximum
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/TurnBasedTutorial")
+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="TurnBasedTutorialGameModeBase")
+[CoreRedirects]
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.OnPlayersSide",NewName="/Script/TurnBasedTutorial.Trooper.bOnPlayersSide")
++FunctionRedirects=(OldName="/Script/TurnBasedTutorial.MyPlayerController.MoveHero",NewName="/Script/TurnBasedTutorial.MyPlayerController.MoveTropper")
++FunctionRedirects=(OldName="/Script/TurnBasedTutorial.MyPlayerController.MoveTropper",NewName="/Script/TurnBasedTutorial.MyPlayerController.MoveTrooper")
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.ManageSquadTrooper.IndexInSquad",NewName="/Script/TurnBasedTutorial.ManageSquadTrooper.Index")
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.ManageSquadTrooper.IndexInSquadOrSample",NewName="/Script/TurnBasedTutorial.ManageSquadTrooper.Index")
++FunctionRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.GetActionCost",NewName="/Script/TurnBasedTutorial.Trooper.GetActionRadius")
++FunctionRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.GetActionCost",NewName="/Script/TurnBasedTutorial.Trooper.GetActionRadius")
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.RedMaterialInterface",NewName="/Script/TurnBasedTutorial.Trooper.RedMaterial")
++FunctionRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.HighlightEnemy",NewName="/Script/TurnBasedTutorial.Trooper.HighlightAsEnemy")
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.bIsDying",NewName="/Script/TurnBasedTutorial.Trooper.bIsDead")
++PropertyRedirects=(OldName="/Script/TurnBasedTutorial.Trooper.bIsDying",NewName="/Script/TurnBasedTutorial.Trooper.bIsDead")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyGameInstanceSubsystem",NewName="/Script/TurnBasedTutorial.SessionsGameInstanceSubsystem")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyMainMenu",NewName="/Script/TurnBasedTutorial.MainMenuWidget")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MySessionListEntryWidget",NewName="/Script/TurnBasedTutorial.SessionListEntryWidget")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MySessionListWidget",NewName="/Script/TurnBasedTutorial.SessionListWidget")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyGameMode",NewName="/Script/TurnBasedTutorial.BattleGameMode")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyGameState",NewName="/Script/TurnBasedTutorial.BattleGameState")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyPawn",NewName="/Script/TurnBasedTutorial.BattlePawn")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyPlayerController",NewName="/Script/TurnBasedTutorial.BattlePlayerController")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyPlayerStart",NewName="/Script/TurnBasedTutorial.BattlePlayerStart")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyPlayerState",NewName="/Script/TurnBasedTutorial.BattlePlayerState")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyExplosion",NewName="/Script/TurnBasedTutorial.Explosion")
++ClassRedirects=(OldName="/Script/TurnBasedTutorial.MyProjectile",NewName="/Script/TurnBasedTutorial.Projectile")
+
+[OnlineSubsystem]
+DefaultPlatformService=Null
diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini
index 07e992c..3d5b79e 100644
--- a/Config/DefaultInput.ini
+++ b/Config/DefaultInput.ini
@@ -78,6 +78,8 @@ DefaultViewportMouseLockMode=LockOnCapture
FOVScale=0.011110
DoubleClickTime=0.200000
+ActionMappings=(ActionName="MyAction",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton)
++AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W)
++AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S)
DefaultPlayerInputClass=/Script/Engine.PlayerInput
DefaultInputComponentClass=/Script/Engine.InputComponent
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
diff --git a/Content/Animations/BP_TrooperAnimations.uasset b/Content/Animations/BP_TrooperAnimations.uasset
new file mode 100644
index 0000000..1dda306
Binary files /dev/null and b/Content/Animations/BP_TrooperAnimations.uasset differ
diff --git a/Content/BP_MyGameMode.uasset b/Content/BP_MyGameMode.uasset
new file mode 100644
index 0000000..204d94f
Binary files /dev/null and b/Content/BP_MyGameMode.uasset differ
diff --git a/Content/BattleField/BP_BattlePawn.uasset b/Content/BattleField/BP_BattlePawn.uasset
new file mode 100644
index 0000000..a02872f
Binary files /dev/null and b/Content/BattleField/BP_BattlePawn.uasset differ
diff --git a/Content/BattleField/BP_BattlePlayerController.uasset b/Content/BattleField/BP_BattlePlayerController.uasset
new file mode 100644
index 0000000..5f65d44
Binary files /dev/null and b/Content/BattleField/BP_BattlePlayerController.uasset differ
diff --git a/Content/BattleField/BP_BattlePlayerState.uasset b/Content/BattleField/BP_BattlePlayerState.uasset
new file mode 100644
index 0000000..f9d5f91
Binary files /dev/null and b/Content/BattleField/BP_BattlePlayerState.uasset differ
diff --git a/Content/BattleField/BP_BattleUI.uasset b/Content/BattleField/BP_BattleUI.uasset
new file mode 100644
index 0000000..8c09052
Binary files /dev/null and b/Content/BattleField/BP_BattleUI.uasset differ
diff --git a/Content/BattleField/BP_EndOfGameWidget.uasset b/Content/BattleField/BP_EndOfGameWidget.uasset
new file mode 100644
index 0000000..0cdda72
Binary files /dev/null and b/Content/BattleField/BP_EndOfGameWidget.uasset differ
diff --git a/Content/BattleField/BP_InventoryUI.uasset b/Content/BattleField/BP_InventoryUI.uasset
new file mode 100644
index 0000000..21470fe
Binary files /dev/null and b/Content/BattleField/BP_InventoryUI.uasset differ
diff --git a/Content/BattleField/BP_MyGameMode.uasset b/Content/BattleField/BP_MyGameMode.uasset
new file mode 100644
index 0000000..ef3b99a
Binary files /dev/null and b/Content/BattleField/BP_MyGameMode.uasset differ
diff --git a/Content/BattleField/Multiplayer/BP_BattleGameMode.uasset b/Content/BattleField/Multiplayer/BP_BattleGameMode.uasset
new file mode 100644
index 0000000..ce38b8d
Binary files /dev/null and b/Content/BattleField/Multiplayer/BP_BattleGameMode.uasset differ
diff --git a/Content/BattleField/Multiplayer/BP_BattleGameState.uasset b/Content/BattleField/Multiplayer/BP_BattleGameState.uasset
new file mode 100644
index 0000000..fc721a6
Binary files /dev/null and b/Content/BattleField/Multiplayer/BP_BattleGameState.uasset differ
diff --git a/Content/BattleField/Multiplayer/BP_MyGameMode.uasset b/Content/BattleField/Multiplayer/BP_MyGameMode.uasset
new file mode 100644
index 0000000..2ac5118
Binary files /dev/null and b/Content/BattleField/Multiplayer/BP_MyGameMode.uasset differ
diff --git a/Content/BattleField/BattleFieldMap.umap b/Content/BattleField/Multiplayer/BattleFieldMap.umap
similarity index 94%
rename from Content/BattleField/BattleFieldMap.umap
rename to Content/BattleField/Multiplayer/BattleFieldMap.umap
index 8f7cb0e..9d0bed5 100644
Binary files a/Content/BattleField/BattleFieldMap.umap and b/Content/BattleField/Multiplayer/BattleFieldMap.umap differ
diff --git a/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset b/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset
new file mode 100644
index 0000000..0a6b17d
Binary files /dev/null and b/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset differ
diff --git a/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset
new file mode 100644
index 0000000..31f2b76
Binary files /dev/null and b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset differ
diff --git a/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap b/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap
new file mode 100644
index 0000000..a70b900
Binary files /dev/null and b/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap differ
diff --git a/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset b/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset
new file mode 100644
index 0000000..4046bae
Binary files /dev/null and b/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset differ
diff --git a/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset b/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset
new file mode 100644
index 0000000..1fe4cee
Binary files /dev/null and b/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset differ
diff --git a/Content/BattleField/UI_images/explosion-clip-art-33.uasset b/Content/BattleField/UI_images/explosion-clip-art-33.uasset
new file mode 100644
index 0000000..8c4fda6
Binary files /dev/null and b/Content/BattleField/UI_images/explosion-clip-art-33.uasset differ
diff --git a/Content/BattleField/UI_images/img_525444.uasset b/Content/BattleField/UI_images/img_525444.uasset
new file mode 100644
index 0000000..7007f6d
Binary files /dev/null and b/Content/BattleField/UI_images/img_525444.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/ArcherBow_Inst.uasset b/Content/CityofBrass_Enemies/Materials/ArcherBow_Inst.uasset
new file mode 100644
index 0000000..ef3317e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/ArcherBow_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Archer_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Archer_Inst.uasset
new file mode 100644
index 0000000..b648fed
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Archer_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/BossAxe_Inst.uasset b/Content/CityofBrass_Enemies/Materials/BossAxe_Inst.uasset
new file mode 100644
index 0000000..38c3693
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/BossAxe_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Character_Substance_Invisible_Material.uasset b/Content/CityofBrass_Enemies/Materials/Character_Substance_Invisible_Material.uasset
new file mode 100644
index 0000000..211ca9f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Character_Substance_Invisible_Material.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Character_Substance_Layers_Material.uasset b/Content/CityofBrass_Enemies/Materials/Character_Substance_Layers_Material.uasset
new file mode 100644
index 0000000..c137e6e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Character_Substance_Layers_Material.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/CorpseSpear_Inst.uasset b/Content/CityofBrass_Enemies/Materials/CorpseSpear_Inst.uasset
new file mode 100644
index 0000000..136c19a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/CorpseSpear_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Body_Inst2.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Body_Inst2.uasset
new file mode 100644
index 0000000..ba92e26
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Body_Inst2.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Helmet_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Helmet_Inst.uasset
new file mode 100644
index 0000000..4b62a53
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Helmet_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Inst.uasset
new file mode 100644
index 0000000..8535327
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Charger_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Clothes_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Clothes_Inst.uasset
new file mode 100644
index 0000000..7c1df12
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Clothes_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Fez_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Fez_Inst.uasset
new file mode 100644
index 0000000..a9771ad
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Fez_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Corpse_Melee_Dress_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Corpse_Melee_Dress_Inst.uasset
new file mode 100644
index 0000000..8a85657
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Corpse_Melee_Dress_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Enviro_Substance_Material.uasset b/Content/CityofBrass_Enemies/Materials/Enviro_Substance_Material.uasset
new file mode 100644
index 0000000..be48d80
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Enviro_Substance_Material.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/FermaleWarrior_Inst.uasset b/Content/CityofBrass_Enemies/Materials/FermaleWarrior_Inst.uasset
new file mode 100644
index 0000000..641d9e1
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/FermaleWarrior_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/GrenadierBody_Inst.uasset b/Content/CityofBrass_Enemies/Materials/GrenadierBody_Inst.uasset
new file mode 100644
index 0000000..9627385
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/GrenadierBody_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Grenadier_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Grenadier_Inst.uasset
new file mode 100644
index 0000000..aed3485
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Grenadier_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Hooker_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Hooker_Inst.uasset
new file mode 100644
index 0000000..0ec58f4
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Hooker_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Large_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Large_Inst.uasset
new file mode 100644
index 0000000..d42e8a3
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Large_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Player_Sword_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Player_Sword_Inst.uasset
new file mode 100644
index 0000000..93bb0b7
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Player_Sword_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/SploderBody_Inst1.uasset b/Content/CityofBrass_Enemies/Materials/SploderBody_Inst1.uasset
new file mode 100644
index 0000000..c24dede
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/SploderBody_Inst1.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Sploder_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Sploder_Inst.uasset
new file mode 100644
index 0000000..669c953
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Sploder_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Materials/Wizard_Inst.uasset b/Content/CityofBrass_Enemies/Materials/Wizard_Inst.uasset
new file mode 100644
index 0000000..243f563
Binary files /dev/null and b/Content/CityofBrass_Enemies/Materials/Wizard_Inst.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer.uasset
new file mode 100644
index 0000000..aad61d9
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon.uasset
new file mode 100644
index 0000000..8c0c2fc
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_PhysicsAsset.uasset
new file mode 100644
index 0000000..7dee666
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_Skeleton.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_Skeleton.uasset
new file mode 100644
index 0000000..2002a33
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer_Bow_Weapon_Skeleton.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer__mesh_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer__mesh_PhysicsAsset.uasset
new file mode 100644
index 0000000..7bf75fc
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Archer/Archer__mesh_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Aggro.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Aggro.uasset
new file mode 100644
index 0000000..4eb0659
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Aggro.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Attack_Close.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Attack_Close.uasset
new file mode 100644
index 0000000..39f5e2f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Attack_Close.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeAttack.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeAttack.uasset
new file mode 100644
index 0000000..c591275
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeAttack.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeWall.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeWall.uasset
new file mode 100644
index 0000000..801eac3
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_ChargeWall.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Ilde.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Ilde.uasset
new file mode 100644
index 0000000..130c546
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Ilde.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Sprint.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Sprint.uasset
new file mode 100644
index 0000000..9f0df5b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Sprint.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Stunned.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Stunned.uasset
new file mode 100644
index 0000000..4b8d7e8
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_Stunned.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_run.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_run.uasset
new file mode 100644
index 0000000..a8a9c8d
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Alert_run.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Death.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Death.uasset
new file mode 100644
index 0000000..d0fd0ee
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Death.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Flinch_back.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Flinch_back.uasset
new file mode 100644
index 0000000..9263ca6
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Flinch_back.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_KnockDown.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_KnockDown.uasset
new file mode 100644
index 0000000..a7c43d5
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_KnockDown.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Ilde.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Ilde.uasset
new file mode 100644
index 0000000..c90ffa8
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Ilde.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Walk.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Walk.uasset
new file mode 100644
index 0000000..9e9cd4c
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Animation/Charger_Passive_Walk.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger.uasset
new file mode 100644
index 0000000..86daa92
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_Helmet.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_Helmet.uasset
new file mode 100644
index 0000000..d17b337
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_Helmet.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_PhysicsAsset.uasset
new file mode 100644
index 0000000..ea86520
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Charger/Charger_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Skeleton.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Skeleton.uasset
new file mode 100644
index 0000000..8a2d4fd
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Skeleton.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword.uasset
new file mode 100644
index 0000000..05cae6f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_BaseColor.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_BaseColor.uasset
new file mode 100644
index 0000000..1cd7fa5
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_BaseColor.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_Normal.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_Normal.uasset
new file mode 100644
index 0000000..ef4b7e0
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_Normal.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..9a5e24d
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_PhysicsAsset.uasset
new file mode 100644
index 0000000..b337ea9
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_ShadowPhysics.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_ShadowPhysics.uasset
new file mode 100644
index 0000000..eebc999
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_ShadowPhysics.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Turban.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Turban.uasset
new file mode 100644
index 0000000..be667bd
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/Corpse_Sword_Turban.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Approach.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Approach.uasset
new file mode 100644
index 0000000..c1258cb
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Approach.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic01.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic01.uasset
new file mode 100644
index 0000000..1e9316e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic01.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic02.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic02.uasset
new file mode 100644
index 0000000..c49113f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic02.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic03.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic03.uasset
new file mode 100644
index 0000000..52872ad
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic03.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic04.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic04.uasset
new file mode 100644
index 0000000..9053c82
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Basic04.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast.uasset
new file mode 100644
index 0000000..a0e84be
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast2.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast2.uasset
new file mode 100644
index 0000000..88df60a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Fast2.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Push.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Push.uasset
new file mode 100644
index 0000000..b22f295
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Push.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Shield_Push.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Shield_Push.uasset
new file mode 100644
index 0000000..6b2dd00
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Shield_Push.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Slow.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Slow.uasset
new file mode 100644
index 0000000..b4d6c4a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Attack_Slow.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death.uasset
new file mode 100644
index 0000000..0a67a4e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death2.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death2.uasset
new file mode 100644
index 0000000..c6fefe4
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death2.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death3.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death3.uasset
new file mode 100644
index 0000000..8f2b36b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Death3.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Distracted_Grab.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Distracted_Grab.uasset
new file mode 100644
index 0000000..e37e06b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Distracted_Grab.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stun_Knock_Back.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stun_Knock_Back.uasset
new file mode 100644
index 0000000..4c42817
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stun_Knock_Back.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stunned.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stunned.uasset
new file mode 100644
index 0000000..bc0e24b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Stunned.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Taunt.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Taunt.uasset
new file mode 100644
index 0000000..2398e1f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Taunt.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped.uasset
new file mode 100644
index 0000000..6364d47
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped_Forward.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped_Forward.uasset
new file mode 100644
index 0000000..401b45a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_Tripped_Forward.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_AddBase.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_AddBase.uasset
new file mode 100644
index 0000000..c6fd420
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_AddBase.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_45.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_45.uasset
new file mode 100644
index 0000000..3d917f3
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_45.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_90.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_90.uasset
new file mode 100644
index 0000000..333f9c1
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Left_90.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Middle.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Middle.uasset
new file mode 100644
index 0000000..f4c6a5f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Middle.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_45.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_45.uasset
new file mode 100644
index 0000000..fc8badb
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_45.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_90.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_90.uasset
new file mode 100644
index 0000000..2621300
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Alert_turn_Right_90.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Disarm.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Disarm.uasset
new file mode 100644
index 0000000..8a43aff
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Disarm.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Back.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Back.uasset
new file mode 100644
index 0000000..2d1c951
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Back.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Forward.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Forward.uasset
new file mode 100644
index 0000000..3cb17c4
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Forward.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Left.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Left.uasset
new file mode 100644
index 0000000..41a6c0a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Left.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Right.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Right.uasset
new file mode 100644
index 0000000..b66ddc5
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_Run_Right.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_attack_right.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_attack_right.uasset
new file mode 100644
index 0000000..48c9974
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_attack_right.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_idle.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_idle.uasset
new file mode 100644
index 0000000..114a389
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_alert_idle.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_passive_idle.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_passive_idle.uasset
new file mode 100644
index 0000000..c4272e7
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse/animation/Corpse_passive_idle.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Corpse_Basic.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Corpse_Basic.uasset
new file mode 100644
index 0000000..4a7befe
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Corpse_Basic.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Fez_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Fez_Mesh.uasset
new file mode 100644
index 0000000..45b2fdd
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Basic/Fez_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee.uasset
new file mode 100644
index 0000000..00474d9
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee_PhysicsAsset.uasset
new file mode 100644
index 0000000..29ffd2b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Melee/Corpse_Melee_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Helmet.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Helmet.uasset
new file mode 100644
index 0000000..1718f72
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Helmet.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Mesh.uasset
new file mode 100644
index 0000000..19c1773
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/CorpseSpear_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Large.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Large.uasset
new file mode 100644
index 0000000..ee1a19a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Large.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Small.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Small.uasset
new file mode 100644
index 0000000..3ce2c81
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Corpse_Spear/animation/Spear_Alert_Attack_Small.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Animation/Grenadier_Attack_Throw.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Animation/Grenadier_Attack_Throw.uasset
new file mode 100644
index 0000000..2b0add9
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Animation/Grenadier_Attack_Throw.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Hat.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Hat.uasset
new file mode 100644
index 0000000..3b6ff5c
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Hat.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Mesh.uasset
new file mode 100644
index 0000000..4cc2631
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Grenadier/Grenadier_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Boss_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Boss_Mesh.uasset
new file mode 100644
index 0000000..3e250cb
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Boss_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel.uasset
new file mode 100644
index 0000000..72c4158
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Physics.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Physics.uasset
new file mode 100644
index 0000000..010e136
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Physics.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Skeleton.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Skeleton.uasset
new file mode 100644
index 0000000..60a6444
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Claw_Skel_Skeleton.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Mesh.uasset
new file mode 100644
index 0000000..d85295b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Hooker/Hooker_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_Mesh.uasset
new file mode 100644
index 0000000..523fd03
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_PhysicsAsset.uasset
new file mode 100644
index 0000000..d29a4ad
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Large/Large_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Charger/Large_Charger_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Charger/Large_Charger_Mesh.uasset
new file mode 100644
index 0000000..56f4f22
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Charger/Large_Charger_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Sword/Large_Sword.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Sword/Large_Sword.uasset
new file mode 100644
index 0000000..531a7ea
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Large_Sword/Large_Sword.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_D.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_D.uasset
new file mode 100644
index 0000000..709d7a6
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_M.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_M.uasset
new file mode 100644
index 0000000..af87252
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_N.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_N.uasset
new file mode 100644
index 0000000..1768667
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/SploderBody_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_D.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_D.uasset
new file mode 100644
index 0000000..7515917
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_M.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_M.uasset
new file mode 100644
index 0000000..26037cf
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_Mesh.uasset
new file mode 100644
index 0000000..4394c39
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_N.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_N.uasset
new file mode 100644
index 0000000..f026fde
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sploder/Sploder_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh.uasset
new file mode 100644
index 0000000..5252fe0
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Physics.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Physics.uasset
new file mode 100644
index 0000000..4190ac8
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Physics.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Skeleton.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Skeleton.uasset
new file mode 100644
index 0000000..26a2281
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/Player_Sword_Mesh_Skeleton.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/enemy_Sword_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/enemy_Sword_Mesh.uasset
new file mode 100644
index 0000000..99e7212
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Sword/enemy_Sword_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/FemaleWizard_Mesh_PhysicsAsset.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/FemaleWizard_Mesh_PhysicsAsset.uasset
new file mode 100644
index 0000000..a3eaa42
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/FemaleWizard_Mesh_PhysicsAsset.uasset differ
diff --git a/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/Wizard_Mesh.uasset b/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/Wizard_Mesh.uasset
new file mode 100644
index 0000000..b177008
Binary files /dev/null and b/Content/CityofBrass_Enemies/Meshes/Enemy/Wizard/Wizard_Mesh.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_Bow_D.uasset b/Content/CityofBrass_Enemies/Textures/Archer_Bow_D.uasset
new file mode 100644
index 0000000..4b1706c
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_Bow_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_Bow_M.uasset b/Content/CityofBrass_Enemies/Textures/Archer_Bow_M.uasset
new file mode 100644
index 0000000..c916c50
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_Bow_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_Bow_N.uasset b/Content/CityofBrass_Enemies/Textures/Archer_Bow_N.uasset
new file mode 100644
index 0000000..29b6604
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_Bow_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_D.uasset b/Content/CityofBrass_Enemies/Textures/Archer_D.uasset
new file mode 100644
index 0000000..205fcb0
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_M.uasset b/Content/CityofBrass_Enemies/Textures/Archer_M.uasset
new file mode 100644
index 0000000..ba8cb99
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Archer_N.uasset b/Content/CityofBrass_Enemies/Textures/Archer_N.uasset
new file mode 100644
index 0000000..ffd5f36
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Archer_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Charger_Helmet_D.uasset b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_D.uasset
new file mode 100644
index 0000000..ccb429e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Charger_Helmet_Mask.uasset b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_Mask.uasset
new file mode 100644
index 0000000..c600d75
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_Mask.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Charger_Helmet_N.uasset b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_N.uasset
new file mode 100644
index 0000000..57eafac
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Charger_Helmet_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/CorpseSpear_D.uasset b/Content/CityofBrass_Enemies/Textures/CorpseSpear_D.uasset
new file mode 100644
index 0000000..0472196
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/CorpseSpear_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/CorpseSpear_M.uasset b/Content/CityofBrass_Enemies/Textures/CorpseSpear_M.uasset
new file mode 100644
index 0000000..6102810
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/CorpseSpear_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/CorpseSpear_N.uasset b/Content/CityofBrass_Enemies/Textures/CorpseSpear_N.uasset
new file mode 100644
index 0000000..78e1f7d
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/CorpseSpear_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_BaseColor.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_BaseColor.uasset
new file mode 100644
index 0000000..a6a2e10
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_BaseColor.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_Normal.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_Normal.uasset
new file mode 100644
index 0000000..194c65c
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_Normal.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..b355e5e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Body_Low_green_OcclusionRoughnessMetallic.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_BaseColor.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_BaseColor.uasset
new file mode 100644
index 0000000..7254eca
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_BaseColor.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_Normal.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_Normal.uasset
new file mode 100644
index 0000000..b0c1f85
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_Normal.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_OcclusionRoughnessMetallic.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..38227c0
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_Sword_Clothes_Low_02_-_Default_OcclusionRoughnessMetallic.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_BaseColor.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_BaseColor.uasset
new file mode 100644
index 0000000..f4a0a1b
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_BaseColor.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_Normal.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_Normal.uasset
new file mode 100644
index 0000000..b596051
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_Normal.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_OcclusionRoughnessMetallic.uasset b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..54568e3
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Corpse_body_Complete_10011_OcclusionRoughnessMetallic.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/DiffuseTextureTest.uasset b/Content/CityofBrass_Enemies/Textures/DiffuseTextureTest.uasset
new file mode 100644
index 0000000..0ba4c99
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/DiffuseTextureTest.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/FemaleWarrior_D.uasset b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_D.uasset
new file mode 100644
index 0000000..b27e518
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/FemaleWarrior_M.uasset b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_M.uasset
new file mode 100644
index 0000000..787112e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/FemaleWarrior_N.uasset b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_N.uasset
new file mode 100644
index 0000000..cce1152
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/FemaleWarrior_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Fez_D.uasset b/Content/CityofBrass_Enemies/Textures/Fez_D.uasset
new file mode 100644
index 0000000..a99d182
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Fez_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Fez_Mask.uasset b/Content/CityofBrass_Enemies/Textures/Fez_Mask.uasset
new file mode 100644
index 0000000..686b69a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Fez_Mask.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Fez_N.uasset b/Content/CityofBrass_Enemies/Textures/Fez_N.uasset
new file mode 100644
index 0000000..98374e8
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Fez_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/GrenadierBody_M.uasset b/Content/CityofBrass_Enemies/Textures/GrenadierBody_M.uasset
new file mode 100644
index 0000000..026fb53
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/GrenadierBody_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/GrenadierBody_N.uasset b/Content/CityofBrass_Enemies/Textures/GrenadierBody_N.uasset
new file mode 100644
index 0000000..1254012
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/GrenadierBody_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/GrenadierClothes_D.uasset b/Content/CityofBrass_Enemies/Textures/GrenadierClothes_D.uasset
new file mode 100644
index 0000000..7423be1
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/GrenadierClothes_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Grenadier_D.uasset b/Content/CityofBrass_Enemies/Textures/Grenadier_D.uasset
new file mode 100644
index 0000000..6983eee
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Grenadier_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Grenadier_M.uasset b/Content/CityofBrass_Enemies/Textures/Grenadier_M.uasset
new file mode 100644
index 0000000..6deb413
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Grenadier_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Grenadier_N.uasset b/Content/CityofBrass_Enemies/Textures/Grenadier_N.uasset
new file mode 100644
index 0000000..f5a05b9
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Grenadier_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Hooker_D.uasset b/Content/CityofBrass_Enemies/Textures/Hooker_D.uasset
new file mode 100644
index 0000000..24c3c15
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Hooker_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Hooker_M.uasset b/Content/CityofBrass_Enemies/Textures/Hooker_M.uasset
new file mode 100644
index 0000000..2d1a5ea
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Hooker_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Hooker_N.uasset b/Content/CityofBrass_Enemies/Textures/Hooker_N.uasset
new file mode 100644
index 0000000..e6deaae
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Hooker_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Hooker_ygui.uasset b/Content/CityofBrass_Enemies/Textures/Hooker_ygui.uasset
new file mode 100644
index 0000000..b68f382
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Hooker_ygui.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Large_D.uasset b/Content/CityofBrass_Enemies/Textures/Large_D.uasset
new file mode 100644
index 0000000..771e1d4
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Large_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Large_M.uasset b/Content/CityofBrass_Enemies/Textures/Large_M.uasset
new file mode 100644
index 0000000..0a52856
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Large_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Large_N.uasset b/Content/CityofBrass_Enemies/Textures/Large_N.uasset
new file mode 100644
index 0000000..8b014b5
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Large_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_D.uasset b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_D.uasset
new file mode 100644
index 0000000..0d848d6
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_M.uasset b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_M.uasset
new file mode 100644
index 0000000..2fde1c8
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_Normal.uasset b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_Normal.uasset
new file mode 100644
index 0000000..0edde7c
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Melee_Clothes_low_Corpse_Melee_Normal.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Stall01_D.uasset b/Content/CityofBrass_Enemies/Textures/Stall01_D.uasset
new file mode 100644
index 0000000..71dda17
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Stall01_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Stall01_M.uasset b/Content/CityofBrass_Enemies/Textures/Stall01_M.uasset
new file mode 100644
index 0000000..a02d486
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Stall01_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Stall01_N.uasset b/Content/CityofBrass_Enemies/Textures/Stall01_N.uasset
new file mode 100644
index 0000000..3b3f20f
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Stall01_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Sword_D.uasset b/Content/CityofBrass_Enemies/Textures/Sword_D.uasset
new file mode 100644
index 0000000..1b9d04a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Sword_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Sword_M.uasset b/Content/CityofBrass_Enemies/Textures/Sword_M.uasset
new file mode 100644
index 0000000..cd2f716
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Sword_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Sword_N.uasset b/Content/CityofBrass_Enemies/Textures/Sword_N.uasset
new file mode 100644
index 0000000..3dbdd5e
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Sword_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Wizard_D.uasset b/Content/CityofBrass_Enemies/Textures/Wizard_D.uasset
new file mode 100644
index 0000000..bc59a4a
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Wizard_D.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Wizard_M.uasset b/Content/CityofBrass_Enemies/Textures/Wizard_M.uasset
new file mode 100644
index 0000000..a4f4157
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Wizard_M.uasset differ
diff --git a/Content/CityofBrass_Enemies/Textures/Wizard_N.uasset b/Content/CityofBrass_Enemies/Textures/Wizard_N.uasset
new file mode 100644
index 0000000..724cb73
Binary files /dev/null and b/Content/CityofBrass_Enemies/Textures/Wizard_N.uasset differ
diff --git a/Content/CityofBrass_Enemies/maps/Overview.umap b/Content/CityofBrass_Enemies/maps/Overview.umap
new file mode 100644
index 0000000..d9007cc
Binary files /dev/null and b/Content/CityofBrass_Enemies/maps/Overview.umap differ
diff --git a/Content/MainMenu/BP_MainMenuGameMode.uasset b/Content/MainMenu/BP_MainMenuGameMode.uasset
new file mode 100644
index 0000000..e269c55
Binary files /dev/null and b/Content/MainMenu/BP_MainMenuGameMode.uasset differ
diff --git a/Content/MainMenu/BP_MainMenuPlayerController.uasset b/Content/MainMenu/BP_MainMenuPlayerController.uasset
new file mode 100644
index 0000000..a99d706
Binary files /dev/null and b/Content/MainMenu/BP_MainMenuPlayerController.uasset differ
diff --git a/Content/MainMenu/BP_MainMenuWidget.uasset b/Content/MainMenu/BP_MainMenuWidget.uasset
new file mode 100644
index 0000000..dc53399
Binary files /dev/null and b/Content/MainMenu/BP_MainMenuWidget.uasset differ
diff --git a/Content/MainMenu/BP_SessionListEntry.uasset b/Content/MainMenu/BP_SessionListEntry.uasset
new file mode 100644
index 0000000..36dc2c5
Binary files /dev/null and b/Content/MainMenu/BP_SessionListEntry.uasset differ
diff --git a/Content/MainMenu/BP_SessionListMenu.uasset b/Content/MainMenu/BP_SessionListMenu.uasset
new file mode 100644
index 0000000..0f1b13f
Binary files /dev/null and b/Content/MainMenu/BP_SessionListMenu.uasset differ
diff --git a/Content/MainMenu/MainMenu.uasset b/Content/MainMenu/MainMenu.uasset
deleted file mode 100644
index aa2d07e..0000000
Binary files a/Content/MainMenu/MainMenu.uasset and /dev/null differ
diff --git a/Content/MainMenu/MainMenuLevel.umap b/Content/MainMenu/MainMenuLevel.umap
index dbee8ab..d882ad5 100644
Binary files a/Content/MainMenu/MainMenuLevel.umap and b/Content/MainMenu/MainMenuLevel.umap differ
diff --git a/Content/MainMenu/fotor-ai-2023050222244.uasset b/Content/MainMenu/fotor-ai-2023050222244.uasset
new file mode 100644
index 0000000..7145ff0
Binary files /dev/null and b/Content/MainMenu/fotor-ai-2023050222244.uasset differ
diff --git a/Content/ManageSquadMenu/BP_ManageSquadTrooper.uasset b/Content/ManageSquadMenu/BP_ManageSquadTrooper.uasset
new file mode 100644
index 0000000..4595318
Binary files /dev/null and b/Content/ManageSquadMenu/BP_ManageSquadTrooper.uasset differ
diff --git a/Content/ManageSquadMenu/ManageSquadLevel.umap b/Content/ManageSquadMenu/ManageSquadLevel.umap
new file mode 100644
index 0000000..fca80b0
Binary files /dev/null and b/Content/ManageSquadMenu/ManageSquadLevel.umap differ
diff --git a/Content/StarterContent/Architecture/Floor_400x400.uasset b/Content/StarterContent/Architecture/Floor_400x400.uasset
index 7ec619f..a08b055 100644
Binary files a/Content/StarterContent/Architecture/Floor_400x400.uasset and b/Content/StarterContent/Architecture/Floor_400x400.uasset differ
diff --git a/Content/StarterContent/Maps/Minimal_Default.umap b/Content/StarterContent/Maps/Minimal_Default.umap
index 7b52307..e056d46 100644
Binary files a/Content/StarterContent/Maps/Minimal_Default.umap and b/Content/StarterContent/Maps/Minimal_Default.umap differ
diff --git a/Content/StarterContent/Particles/Materials/M_Burst.uasset b/Content/StarterContent/Particles/Materials/M_Burst.uasset
index 1bc05f7..a48d10c 100644
Binary files a/Content/StarterContent/Particles/Materials/M_Burst.uasset and b/Content/StarterContent/Particles/Materials/M_Burst.uasset differ
diff --git a/Content/StarterContent/Particles/Materials/M_Dust_Particle.uasset b/Content/StarterContent/Particles/Materials/M_Dust_Particle.uasset
index 9af46f1..1bc4633 100644
Binary files a/Content/StarterContent/Particles/Materials/M_Dust_Particle.uasset and b/Content/StarterContent/Particles/Materials/M_Dust_Particle.uasset differ
diff --git a/Content/StarterContent/Particles/Materials/M_Fire_SubUV.uasset b/Content/StarterContent/Particles/Materials/M_Fire_SubUV.uasset
index d0f7225..4b2548d 100644
Binary files a/Content/StarterContent/Particles/Materials/M_Fire_SubUV.uasset and b/Content/StarterContent/Particles/Materials/M_Fire_SubUV.uasset differ
diff --git a/Content/StarterContent/Particles/Materials/M_Radial_Gradient.uasset b/Content/StarterContent/Particles/Materials/M_Radial_Gradient.uasset
index a2dbd9e..2cc7e28 100644
Binary files a/Content/StarterContent/Particles/Materials/M_Radial_Gradient.uasset and b/Content/StarterContent/Particles/Materials/M_Radial_Gradient.uasset differ
diff --git a/Content/StarterContent/Particles/Materials/M_explosion_subUV.uasset b/Content/StarterContent/Particles/Materials/M_explosion_subUV.uasset
index 9a9041a..15cf20c 100644
Binary files a/Content/StarterContent/Particles/Materials/M_explosion_subUV.uasset and b/Content/StarterContent/Particles/Materials/M_explosion_subUV.uasset differ
diff --git a/Content/StarterContent/Particles/Materials/M_smoke_subUV.uasset b/Content/StarterContent/Particles/Materials/M_smoke_subUV.uasset
index d96330b..8a89654 100644
Binary files a/Content/StarterContent/Particles/Materials/M_smoke_subUV.uasset and b/Content/StarterContent/Particles/Materials/M_smoke_subUV.uasset differ
diff --git a/Content/StarterContent/Props/Materials/M_MaterialSphere.uasset b/Content/StarterContent/Props/Materials/M_MaterialSphere.uasset
index 377dd95..f0101ee 100644
Binary files a/Content/StarterContent/Props/Materials/M_MaterialSphere.uasset and b/Content/StarterContent/Props/Materials/M_MaterialSphere.uasset differ
diff --git a/Content/StarterContent/Props/Materials/M_MaterialSphere_Plain.uasset b/Content/StarterContent/Props/Materials/M_MaterialSphere_Plain.uasset
index cfbfba2..7d6bfe5 100644
Binary files a/Content/StarterContent/Props/Materials/M_MaterialSphere_Plain.uasset and b/Content/StarterContent/Props/Materials/M_MaterialSphere_Plain.uasset differ
diff --git a/Content/Troopers/BP_Trooper.uasset b/Content/Troopers/BP_Trooper.uasset
new file mode 100644
index 0000000..ae5af02
Binary files /dev/null and b/Content/Troopers/BP_Trooper.uasset differ
diff --git a/Content/Troopers/MGreenRing.uasset b/Content/Troopers/MGreenRing.uasset
new file mode 100644
index 0000000..68f7ea2
Binary files /dev/null and b/Content/Troopers/MGreenRing.uasset differ
diff --git a/Content/Troopers/MRedRing.uasset b/Content/Troopers/MRedRing.uasset
new file mode 100644
index 0000000..5f3d9ee
Binary files /dev/null and b/Content/Troopers/MRedRing.uasset differ
diff --git a/Content/Troopers/RingMaterial.uasset b/Content/Troopers/RingMaterial.uasset
new file mode 100644
index 0000000..1d53f42
Binary files /dev/null and b/Content/Troopers/RingMaterial.uasset differ
diff --git a/Content/Troopers/TrooperSkeletonMelee.uasset b/Content/Troopers/TrooperSkeletonMelee.uasset
new file mode 100644
index 0000000..1b5a4c6
Binary files /dev/null and b/Content/Troopers/TrooperSkeletonMelee.uasset differ
diff --git a/Content/Troopers/TrooperWizard.uasset b/Content/Troopers/TrooperWizard.uasset
new file mode 100644
index 0000000..fee6f8c
Binary files /dev/null and b/Content/Troopers/TrooperWizard.uasset differ
diff --git a/Content/Troopers/WBP_HealthBar.uasset b/Content/Troopers/WBP_HealthBar.uasset
new file mode 100644
index 0000000..2c2d067
Binary files /dev/null and b/Content/Troopers/WBP_HealthBar.uasset differ
diff --git a/Content/Troopers/projectiles/BP_DefaultProjectile.uasset b/Content/Troopers/projectiles/BP_DefaultProjectile.uasset
new file mode 100644
index 0000000..18875aa
Binary files /dev/null and b/Content/Troopers/projectiles/BP_DefaultProjectile.uasset differ
diff --git a/Content/Troopers/projectiles/BP_Fireball.uasset b/Content/Troopers/projectiles/BP_Fireball.uasset
new file mode 100644
index 0000000..3a5feb6
Binary files /dev/null and b/Content/Troopers/projectiles/BP_Fireball.uasset differ
diff --git a/Content/Troopers/projectiles/BP_MyExplosion.uasset b/Content/Troopers/projectiles/BP_MyExplosion.uasset
new file mode 100644
index 0000000..06ea15c
Binary files /dev/null and b/Content/Troopers/projectiles/BP_MyExplosion.uasset differ
diff --git a/Content/Troopers/projectiles/Materials/Fire_Material.uasset b/Content/Troopers/projectiles/Materials/Fire_Material.uasset
new file mode 100644
index 0000000..86dede3
Binary files /dev/null and b/Content/Troopers/projectiles/Materials/Fire_Material.uasset differ
diff --git a/Content/Troopers/projectiles/MyMyExplosion.uasset b/Content/Troopers/projectiles/MyMyExplosion.uasset
new file mode 100644
index 0000000..52cfe7a
Binary files /dev/null and b/Content/Troopers/projectiles/MyMyExplosion.uasset differ
diff --git a/Source/TurnBasedTutorial/BattleMode/BattleGameMode.cpp b/Source/TurnBasedTutorial/BattleMode/BattleGameMode.cpp
new file mode 100644
index 0000000..016aff9
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattleGameMode.cpp
@@ -0,0 +1,214 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "BattleGameMode.h"
+#include "Kismet/GameplayStatics.h"
+#include "BattlePawn.h"
+#include "BattleGameState.h"
+#include "BattlePlayerController.h"
+#include "BattlePlayerState.h"
+#include "Trooper/Trooper.h"
+
+ABattleGameMode::ABattleGameMode()
+ : Super() {
+ UE_LOG(LogTemp, Warning, TEXT("GameMode Constructor"));
+ GameStateClass = ABattleGameState::StaticClass();
+ PlayerControllerClass = ABattlePlayerController::StaticClass();
+ PlayerStateClass = ABattlePlayerState::StaticClass();
+ DefaultPawnClass = ABattlePawn::StaticClass();
+}
+
+void ABattleGameMode::BeginPlay() {
+ Super::BeginPlay();
+}
+
+auto ABattleGameMode::GetMyGameState() const {
+ return GetGameState();
+}
+
+
+void ABattleGameMode::InitializeBattleField_Implementation() {
+ UE_LOG(LogTemp, Warning, TEXT("InitializeBattleField"));
+ FVector Location(2000.0f, -1000.0f, 0.0f);
+ FRotator Rotation(0.0f, 180.0f, 0.0f);
+
+ uint8 TrooperCount = 0;
+
+ TArray bpPaths{
+ TEXT(
+ "Blueprint'/Game/Troopers/TrooperSkeletonMelee.TrooperSkeletonMelee_C'"
+ ),
+ TEXT("Blueprint'/Game/Troopers/TrooperWizard.TrooperWizard_C'")
+ };
+ // TArray LoadedBpAssets;
+ for (int i = 0; i < bpPaths.Num(); ++i) {
+ TSoftClassPtr ActorBpClass = TSoftClassPtr(
+ FSoftObjectPath(bpPaths[i])
+ );
+ LoadedBpAssets.Push(ActorBpClass.LoadSynchronous());
+ }
+
+ for (int i = 0; i < 5; ++i) {
+ FTransform SpawnLocationAndRotation(Rotation);
+ SpawnLocationAndRotation.SetLocation(Location);
+ AActor *Spawned = GetWorld()->SpawnActorDeferred(
+ LoadedBpAssets[i % 2], SpawnLocationAndRotation);
+ // AActor *Spawned = GetWorld()->SpawnActorDeferred(
+ // ATrooper::StaticClass(), SpawnLocationAndRotation);
+ Cast(Spawned)->Initialize(
+ 0, Location, TrooperCount++);
+ Spawned->FinishSpawning(SpawnLocationAndRotation);
+ Spawned->SetActorLocation(Location);
+ GetMyGameState()->AddTrooper(Cast(Spawned));
+ Location += {0.f, 500.f, 0.0f};
+ }
+ if (bIsMultiplayer) {
+ Location = {-2000.0f, -1000.0f, 0.0f};
+ Rotation = {0.0f, 0.0f, 0.0f};
+ for (int i = 0; i < 5; ++i) {
+ FTransform SpawnLocationAndRotation(Rotation);
+ SpawnLocationAndRotation.SetLocation(Location);
+ AActor *Spawned = GetWorld()->SpawnActorDeferred(
+ LoadedBpAssets[i % 2], SpawnLocationAndRotation);
+ // AActor *Spawned = GetWorld()->SpawnActorDeferred(
+ // ATrooper::StaticClass(), SpawnLocationAndRotation);
+ Cast(Spawned)->Initialize(
+ 1, Location, TrooperCount++);
+ Spawned->FinishSpawning(SpawnLocationAndRotation);
+ Spawned->SetActorLocation(Location);
+ GetMyGameState()->AddTrooper(Cast(Spawned));
+ Location += {0.f, 500.f, 0.0f};
+ }
+ } else {
+ // Cast(GetMyGameState())->GetEnemyAIController()->
+ // SetTrooperAssetsAndSpawn(
+ // LoadedBpAssets,
+ // TrooperCount);
+ }
+}
+
+AActor *ABattleGameMode::ChoosePlayerStart_Implementation(AController *Player) {
+ UE_LOG(LogTemp, Warning, TEXT("GameMode ChoosePlayerStart %d"),
+ GetNumPlayers());
+ InitializeSpawnPointsIfNeeded(Player);
+ const auto CurrentPlayerStart = *SpawnPoints.Find(GetNumPlayers());
+ UE_LOG(LogTemp, Warning, TEXT("GameMode ChoosePlayerStart end %d"),
+ CurrentPlayerStart->GetPlayerIndex());
+ return CurrentPlayerStart;
+}
+
+void ABattleGameMode::InitializeSpawnPointsIfNeeded(AController *Player) {
+ UE_LOG(LogTemp, Warning, TEXT("InitializeSpawnPointsIfNeeded"));
+ if (SpawnPoints.Num() != 0) {
+ UE_LOG(LogTemp, Warning, TEXT("InitializeSpawnPointsIfNeeded Exit %d"),
+ SpawnPoints.Num());
+ return;
+ }
+ UE_LOG(LogTemp, Warning, TEXT("Rebuilding spawnpoints"));
+ const auto World = GetWorld();
+ for (TActorIterator PlayerStartIterator(GetWorld());
+ PlayerStartIterator; ++PlayerStartIterator) {
+ const auto PlayerStart = *PlayerStartIterator;
+ const UClass *PawnClass = GetDefaultPawnClassForController(Player);
+ const APawn *PawnToFit = PawnClass
+ ? PawnClass->GetDefaultObject()
+ : nullptr;
+ const FVector ActorLocation = PlayerStart->GetActorLocation();
+ const FRotator ActorRotation = PlayerStart->GetActorRotation();
+ UE_LOG(LogTemp, Warning, TEXT("PlayerStart iterator %d"),
+ PlayerStartIterator->GetPlayerIndex());
+ if (!World->EncroachingBlockingGeometry(PawnToFit, ActorLocation,
+ ActorRotation)) {
+ SpawnPoints.Add(PlayerStartIterator->GetPlayerIndex(),
+ *PlayerStartIterator);
+ UE_LOG(LogTemp, Warning, TEXT("PlayerStart unoccupied iterator %d"),
+ PlayerStartIterator->GetPlayerIndex());
+ }
+ }
+}
+
+void ABattleGameMode::PostLogin(APlayerController *NewPlayer) {
+ Super::PostLogin(NewPlayer);
+ NewPlayer->SetShowMouseCursor(true);
+ UE_LOG(LogTemp, Warning, TEXT("PostLogin"));
+ // PlayerControllers.Add(dynamic_cast(NewPlayer));
+ // const auto World = GetWorld();
+ const auto CurrentNumberOfPlayers = GetNumPlayers();
+
+ // 0-indexation
+ Cast(NewPlayer)->SetPlayerIndex(
+ CurrentNumberOfPlayers - 1);
+ UE_LOG(LogTemp, Warning, TEXT("%d"), CurrentNumberOfPlayers);
+ if (CurrentNumberOfPlayers == 2) {
+ UE_LOG(LogTemp, Warning, TEXT("Game Start"));
+ // start the game
+ // dynamic_cast(
+ // GetWorld()->GetGameState())->StartGame();
+ // InitializeBattleField();
+ StartGame();
+ // GetMyGameState()->PlayerInTurn()->SetEnemySelection();
+ // GetMyGameState()->PlayerNotInTurn()->SetEnemySelection();
+ } else {
+ // delay the game
+ UE_LOG(LogTemp, Warning, TEXT("Game Delay"));
+ }
+}
+
+void ABattleGameMode::StartGame_Implementation() {
+ InitializeBattleField();
+ // PlayerNotInTurn()->SetEnemySelection(Troopers);
+ // PlayerInTurn()->SetEnemySelection(Troopers);
+
+ // PlayerInTurn()->StartTurn();
+ GetMyGameState()->StartGame();
+}
+
+
+// void AMyGameMode::StartGame() {
+// InitializeBattleField();
+// // PlayerNotInTurn()->SetEnemySelection(Troopers);
+// // PlayerInTurn()->SetEnemySelection(Troopers);
+//
+// // PlayerInTurn()->StartTurn();
+// GetMyGameState()->StartGame();
+// }
+
+
+// AMyPlayerController *AMyGameMode::PlayerInTurn() const {
+// return GetMyPlayerController(CurrentPlayerTurn);
+// }
+
+// AMyPlayerController *AMyGameMode::PlayerNotInTurn() const {
+// // uint8 PlayerControllerIndexNotInTurn;
+// // if (CurrentPlayerTurn == 0) {
+// // PlayerControllerIndexNotInTurn = 1;
+// // } else {
+// // PlayerControllerIndexNotInTurn = 0;
+// // }
+// // return GetMyPlayerController(PlayerControllerIndexNotInTurn);
+// return GetMyPlayerController(!CurrentPlayerTurn);
+// }
+
+// void AMyGameMode::CycleTurns() {
+// if (!this)
+// return;
+// // PlayerInTurn()->EndTurn();
+// for (const auto Trooper : Troopers) {
+// if (Trooper != nullptr) {
+// Trooper->ResetActionPoints();
+// }
+// }
+// CurrentPlayerTurn = !CurrentPlayerTurn;
+// // if (CurrentPlayerTurn == 0) {
+// // CurrentPlayerTurn = 1;
+// // } else {
+// // CurrentPlayerTurn = 0;
+// // }
+// PlayerInTurn()->StartTurn();
+// }
+
+
+// AMyPlayerController *AMyGameMode::GetMyPlayerController(
+// uint8 const PlayerIndex) const {
+// return Cast(
+// UGameplayStatics::GetPlayerController(GetWorld(), PlayerIndex));
+// }
diff --git a/Source/TurnBasedTutorial/BattleMode/BattleGameMode.h b/Source/TurnBasedTutorial/BattleMode/BattleGameMode.h
new file mode 100644
index 0000000..c411706
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattleGameMode.h
@@ -0,0 +1,64 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "EngineUtils.h"
+#include "BattlePlayerStart.h"
+#include "GameFramework/GameMode.h"
+#include "BattleGameMode.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattleGameMode : public AGameMode {
+ GENERATED_BODY()
+
+public:
+ ABattleGameMode();
+
+ virtual AActor *
+ ChoosePlayerStart_Implementation(AController *Player) override;
+
+ virtual void PostLogin(APlayerController *NewPlayer) override;
+
+ virtual void BeginPlay() override;
+
+ //
+ // UFUNCTION(BlueprintCallable)
+ // void CycleTurns();
+
+protected:
+ UPROPERTY()
+ TArray LoadedBpAssets;
+
+ UPROPERTY()
+ bool bIsMultiplayer = true;
+
+ void InitializeSpawnPointsIfNeeded(AController *Player);
+
+ UFUNCTION(Server, Reliable)
+ void InitializeBattleField();
+
+ UPROPERTY()
+ TMap SpawnPoints{};
+
+ // UPROPERTY()
+ // mutable TArray Troopers;
+
+ UFUNCTION(Server, Reliable)
+ void StartGame();
+
+
+ // UFUNCTION(BlueprintPure)
+ // AMyPlayerController *PlayerInTurn() const;
+ //
+ // UFUNCTION(BlueprintPure)
+ // AMyPlayerController *PlayerNotInTurn() const;
+
+ // UPROPERTY()
+ // uint8 CurrentPlayerTurn{0};
+
+ // UFUNCTION()
+ // AMyPlayerController *GetMyPlayerController(uint8 const PlayerIndex) const;
+
+ auto GetMyGameState() const;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/BattleGameState.cpp b/Source/TurnBasedTutorial/BattleMode/BattleGameState.cpp
new file mode 100644
index 0000000..0c71a63
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattleGameState.cpp
@@ -0,0 +1,121 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "BattleGameState.h"
+
+// #include "GameOverWidget.h"
+#include "BattlePlayerState.h"
+#include "Trooper/Trooper.h"
+#include "Blueprint/UserWidget.h"
+#include "Net/UnrealNetwork.h"
+
+auto ABattleGameState::GetMyPlayerState(uint8 PlayerIndex) const {
+ return Cast(PlayerArray[PlayerIndex]);
+}
+
+ABattleGameState::ABattleGameState()
+ : Super() {
+}
+
+void ABattleGameState::BeginPlay() {
+ Super::BeginPlay();
+ LivingTroopersCount.SetNum(2);
+}
+
+void ABattleGameState::AddTrooper_Implementation(ATrooper *Trooper) {
+ if (Trooper->GetPlayerIndex() >= 0 && Trooper->GetPlayerIndex() <=
+ LivingTroopersCount.Num()) {
+ if (LivingTroopersCount.Num() < 2) {
+ LivingTroopersCount.SetNum(2);
+ }
+ LivingTroopersCount[Trooper->GetPlayerIndex()]++;
+ }
+ Troopers.Add(Trooper);
+}
+
+void ABattleGameState::StartGame_Implementation() {
+ // PlayerNotInTurn()->SetEnemySelection();
+ PlayerInTurn()->SetEnemySelection();
+ bGameStarted = true;
+ PlayerInTurn()->StartTurn();
+}
+
+void ABattleGameState::CycleTurns_Implementation() {
+ PlayerInTurn()->EndTurn();
+ for (const auto Trooper : Troopers) {
+ if (Trooper != nullptr) {
+ Trooper->ResetActionPoints();
+ }
+ }
+ CurrentPlayerTurn = !CurrentPlayerTurn;
+ PlayerInTurn()->StartTurn();
+}
+
+// void AMyGameState::CycleTurns(uint8 CurrentPlayerIndex) {
+// if (CurrentPlayerTurn == CurrentPlayerIndex) {
+// PlayerInTurn()->EndTurn();
+// for (const auto Trooper : Troopers) {
+// if (Trooper != nullptr) {
+// Trooper->ResetActionPoints();
+// }
+// }
+// CurrentPlayerTurn = !CurrentPlayerTurn;
+// PlayerInTurn()->StartTurn();
+// }
+// }
+
+ABattlePlayerState *ABattleGameState::PlayerInTurn() const {
+ return GetMyPlayerState(CurrentPlayerTurn);
+}
+
+ABattlePlayerState *ABattleGameState::PlayerNotInTurn() const {
+ return GetMyPlayerState(!CurrentPlayerTurn);
+}
+
+TArray ABattleGameState::GetTroopers() const {
+ return Troopers;
+}
+
+bool ABattleGameState::IsInTurn(uint8 PlayerIndex) const {
+ return PlayerIndex == CurrentPlayerTurn;
+}
+
+bool ABattleGameState::IsGameStarted() const {
+ return bGameStarted;
+}
+
+void ABattleGameState::DecreaseLivingTroopers(int PlayerIndex) {
+ if (bGameIsOver)
+ return;
+ LivingTroopersCount[PlayerIndex]--;
+ if (!bIsMultiplayer && PlayerIndex == 1) {
+ return;
+ }
+ if (LivingTroopersCount[PlayerIndex] <= 0) {
+ UE_LOG(LogTemp, Warning, TEXT("Player %d lose!"), PlayerIndex);
+ bGameIsOver = true;
+ GameOver(PlayerIndex);
+ }
+}
+
+void ABattleGameState::GameOver(int PlayerIndexLose) const {
+ Cast(PlayerArray[0])->GameOver(PlayerIndexLose);
+ if (bIsMultiplayer) {
+ Cast(PlayerArray[1])->GameOver(PlayerIndexLose);
+ }
+}
+
+// void AMyGameState::GameOver_Implementation(int PlayerIndexLose) {
+// UGameOverWidget *CreatedWidget = CreateWidget(
+// GetWorld(), GameOverWidgetClass);
+// CreatedWidget->AddToViewport();
+// CreatedWidget->SetWidgetText(PlayerIndexLose != );
+// }
+
+
+void ABattleGameState::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(ABattleGameState, Troopers);
+ DOREPLIFETIME(ABattleGameState, CurrentPlayerTurn);
+ DOREPLIFETIME(ABattleGameState, bGameStarted);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/BattleGameState.h b/Source/TurnBasedTutorial/BattleMode/BattleGameState.h
new file mode 100644
index 0000000..bc40ed8
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattleGameState.h
@@ -0,0 +1,75 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BattlePlayerState.h"
+#include "GameFramework/GameState.h"
+#include "BattleGameState.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattleGameState : public AGameState {
+ GENERATED_BODY()
+
+public:
+ ABattleGameState();
+
+ virtual void BeginPlay() override;
+
+ UFUNCTION(Server, Reliable)
+ virtual void AddTrooper(ATrooper *Trooper);
+
+ UFUNCTION(Server, Reliable)
+ void StartGame();
+
+ UFUNCTION(BlueprintCallable, Server, Reliable)
+ virtual void CycleTurns();
+
+ UFUNCTION(BlueprintPure)
+ ABattlePlayerState *PlayerInTurn() const;
+
+ UFUNCTION(BlueprintPure)
+ ABattlePlayerState *PlayerNotInTurn() const;
+
+ UFUNCTION()
+ TArray GetTroopers() const;
+
+ UFUNCTION()
+ bool IsInTurn(uint8 PlayerIndex) const;
+
+ UFUNCTION()
+ bool IsGameStarted() const;
+
+ UFUNCTION()
+ void DecreaseLivingTroopers(int PlayerIndex);
+
+ UFUNCTION()
+ void GameOver(int PlayerIndexLose) const;
+
+protected:
+ // UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ // TSubclassOf GameOverWidgetClass;
+
+ UPROPERTY()
+ bool bIsMultiplayer = true;
+
+ UPROPERTY(Replicated)
+ bool bGameIsOver = false;
+
+ UPROPERTY(Replicated)
+ TArray Troopers;
+
+ UPROPERTY(Replicated)
+ TArray LivingTroopersCount;
+
+ UPROPERTY(Replicated)
+ bool bGameStarted = false;
+
+ UPROPERTY(Replicated)
+ uint8 CurrentPlayerTurn{0};
+
+ auto GetMyPlayerState(uint8 PlayerIndex) const;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePawn.cpp b/Source/TurnBasedTutorial/BattleMode/BattlePawn.cpp
new file mode 100644
index 0000000..5c4a650
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePawn.cpp
@@ -0,0 +1,34 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "BattlePawn.h"
+
+
+// Sets default values
+ABattlePawn::ABattlePawn() {
+ // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
+ PrimaryActorTick.bCanEverTick = true;
+}
+
+// Called when the game starts or when spawned
+void ABattlePawn::BeginPlay() {
+ Super::BeginPlay();
+}
+
+void ABattlePawn::MoveForward(float Val) {
+ if (Val != 0.f) {
+ if (Controller) {
+ FRotator ControlSpaceRot = Controller->GetControlRotation();
+ ControlSpaceRot.Pitch = 0;
+
+ // transform to world space and add it
+ AddMovementInput(
+ FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::X), Val);
+ }
+ }
+}
+
+// Called to bind functionality to input
+void ABattlePawn::SetupPlayerInputComponent(UInputComponent *PlayerInputComponent) {
+ Super::SetupPlayerInputComponent(PlayerInputComponent);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePawn.h b/Source/TurnBasedTutorial/BattleMode/BattlePawn.h
new file mode 100644
index 0000000..de42606
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePawn.h
@@ -0,0 +1,27 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/SpectatorPawn.h"
+#include "BattlePawn.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattlePawn : public ASpectatorPawn {
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this pawn's properties
+ ABattlePawn();
+
+protected:
+ // Called when the game starts or when spawned
+ virtual void BeginPlay() override;
+
+ virtual void MoveForward(float Val) override;
+
+public:
+ // Called to bind functionality to input
+ virtual void SetupPlayerInputComponent(
+ class UInputComponent *PlayerInputComponent) override;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.cpp b/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.cpp
new file mode 100644
index 0000000..aa67c9f
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.cpp
@@ -0,0 +1,317 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "BattlePlayerController.h"
+#include "Kismet/GameplayStatics.h"
+#include "BattleGameMode.h"
+#include "BattleGameState.h"
+#include "BattlePlayerState.h"
+#include "Blueprint/UserWidget.h"
+#include "Net/UnrealNetwork.h"
+
+ABattlePlayerController::ABattlePlayerController()
+ : Super()/*, bIsMyTurn(false), SelectedTrooper(nullptr)*/ {
+ UE_LOG(LogTemp, Warning, TEXT("Player controller created"));
+ SetShowMouseCursor(true);
+ PlayerIndex = 0;
+}
+
+void ABattlePlayerController::BeginPlay() {
+ Super::BeginPlay();
+ UUserWidget *CreatedWidget = CreateWidget(
+ GetWorld(), WidgetClass);
+ CreatedWidget->AddToViewport();
+}
+
+void ABattlePlayerController::SetupInputComponent() {
+ Super::SetupInputComponent();
+ InputComponent->BindAction("MyAction", IE_Pressed, this,
+ &ABattlePlayerController::OnLeftMouseClick);
+}
+
+// void AMyPlayerController::SetMyTurn(bool bMyTurn) {
+// bIsMyTurn = bMyTurn;
+// if (bIsMyTurn) {
+// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green,
+// FString::Printf(
+// TEXT("CURRENT TURN: %d"),
+// PlayerIndex));
+// }
+// OnMyTurnChanged.ExecuteIfBound(bIsMyTurn);
+// }
+
+// void AMyPlayerController::StartTurn_Implementation() {
+// SetMyTurn(true);
+// UE_LOG(LogTemp, Warning, TEXT("Your turn, %d"), PlayerIndex);
+// }
+
+auto ABattlePlayerController::GetMyGameState() const {
+ return Cast(UGameplayStatics::GetGameState(GetWorld()));
+}
+
+auto ABattlePlayerController::GetMyPlayerState() const {
+ return GetPlayerState();
+}
+
+
+auto ABattlePlayerController::GetMyGameMode() const {
+ return Cast(
+ UGameplayStatics::GetGameMode(GetWorld()));
+}
+
+
+void ABattlePlayerController::EndTurn_Implementation() {
+ // if (GetMyPlayerState()->IsMyTurn()) {
+ // GetMyGameState()->CycleTurns();
+ // }
+ // GetMyPlayerState()->CycleTurns();
+ if (GetMyGameState()->IsInTurn(PlayerIndex))
+ GetMyGameState()->CycleTurns();
+}
+
+// void AMyPlayerController::EndTurn_Implementation() {
+// GetMyPlayerState()->EndTurn();
+// }
+
+
+// if (bIsMyTurn) {
+// UE_LOG(LogTemp, Warning, TEXT("End Turn from player %d"), PlayerIndex);
+// SetMyTurn(false);
+// if (SelectedTrooper) {
+// SelectedTrooper->SetSelection(false);
+// SelectedTrooper = nullptr;
+// }
+// UE_LOG(LogTemp, Warning, TEXT("Not your turn, %d"), PlayerIndex);
+// AMyGameMode *gameMode = GetMyGameMode();
+// gameMode->CycleTurns();
+// }
+// }
+
+// AMyGameState *AMyPlayerController::GetMyGameState() const {
+// return dynamic_cast(
+// GetWorld()->GetGameState());
+// }
+
+// void AMyPlayerController::MoveTrooper_Implementation(
+// ATrooper *Trooper,
+// FVector Location) {
+// if (Trooper->CheckMoveCorrectness(Location)) {
+// Trooper->MoveTrooper(Location);
+// // GetMyGameMode()->CycleTurns();
+// } else {
+// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
+// FString::Printf(
+// TEXT("Out of move radius!")));
+// }
+// }
+
+// void AMyPlayerController::AttackTrooper_Implementation(
+// ATrooper *Attacker,
+// ATrooper *Victim) {
+// if (Attacker->CheckAttackCorrectness(Victim->GetLocation())) {
+// Attacker->Attack();
+// // GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red,
+// // FString::Printf(
+// // TEXT("ATTACK!! %d attacked %d"),
+// // Attacker->GetId(),
+// // Victim->GetId()));
+// // GetMyGameMode()->CycleTurns();
+// } else {
+// GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red,
+// FString::Printf(
+// TEXT(
+// "Attack failed! Out of attack radius!")));
+// }
+// }
+
+
+// void AMyPlayerController::Attack_Implementation(ATrooper *Attacker,
+// FVector Location,
+// int ActionIndex) {
+// if (Attacker && CurrentAction >= 1 && CurrentAction <= 2 &&
+// Attacker->CheckAttackCorrectness(Location, CurrentAction)) {
+// Attacker->Attack(CurrentAction);
+// } else {
+// UE_LOG(LogTemp, Warning,
+// TEXT("Out of radius or not enough Action Points"));
+// }
+// }
+
+void ABattlePlayerController::SetPlayerIndex(uint8 NewPlayerIndex) {
+ PlayerIndex = NewPlayerIndex;
+
+ GetMyPlayerState()->SetPlayerIndex(NewPlayerIndex);
+ // GetMyPlayerState()->PlayerIndex = NewPlayerIndex;
+}
+
+uint8 ABattlePlayerController::GetPlayerIndex() const {
+ return PlayerIndex;
+}
+
+// float AMyPlayerController::SetCurrentActionAndReturnRadius(int action) {
+// return GetMyPlayerState()->SetCurrentActionAndReturnRadius(action);
+//
+// // CurrentAction = action;
+// // UE_LOG(LogTemp, Warning, TEXT("SetCurrentAction: %d on Player Controller "
+// // "with index %d"), CurrentAction, PlayerIndex);
+// // if (SelectedTrooper) {
+// // return SelectedTrooper->GetActionRadius(CurrentAction);
+// // }
+// // return 0.0f;
+// }
+
+
+// void AMyPlayerController::SetEnemySelection_Implementation(
+// const TArray &Troopers) const {
+// for (const auto Trooper : Troopers) {
+// if (Trooper != nullptr && Trooper->GetPlayerIndex() != PlayerIndex) {
+// Trooper->HighlightAsEnemy();
+// }
+// }
+// }
+
+
+// void AMyPlayerController::SetEnemySelection_Implementation() {
+// if (!GetMyGameMode()) {
+// UE_LOG(LogTemp, Warning, TEXT("Failed to get Game mode, Index: %d"), PlayerIndex);
+// return;
+// }
+// const auto &Troopers = GetMyGameMode()->GetTroopers();
+// for (const auto Trooper : Troopers) {
+// if (Trooper != nullptr && Trooper->GetPlayerIndex() != PlayerIndex) {
+// Trooper->HighlightAsEnemy();
+// }
+// }
+// }
+
+// void AMyPlayerController::SetEnemySelection(TArray &Troopers) const {
+// for (const auto Trooper : Troopers) {
+// if (Trooper->GetPlayerIndex() != PlayerIndex) {
+// Trooper->HighlightAsEnemy();
+// }
+// }
+// }
+
+void ABattlePlayerController::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(ABattlePlayerController, PlayerIndex);
+ // DOREPLIFETIME(AMyPlayerController, CurrentAction);
+ // DOREPLIFETIME(AMyPlayerController, bIsMyTurn);
+ // DOREPLIFETIME(AMyPlayerController, SelectedTrooper);
+}
+
+void ABattlePlayerController::OnLeftMouseClick() {
+ if (!GetMyPlayerState()->IsMyTurn()) {
+ return;
+ }
+ UE_LOG(LogTemp, Warning, TEXT("Mouse clicked"));
+ // UE_LOG(LogTemp, Warning, TEXT("Current action: %d"), CurrentAction);
+ FHitResult HitResult;
+ bool const IsHitResult = GetHitResultUnderCursorForObjects(
+ TArray>{ObjectTypeQuery1}, false,
+ HitResult);
+ // GetHitResultUnderCursorForObjects();
+ // bool const IsHitResult = GetHitResultUnderCursorByChannel(
+ // TraceTypeQuery1, false, HitResult);
+ if (!IsHitResult)
+ return;
+ UE_LOG(LogTemp, Warning, TEXT("Got hit result"));
+ GetMyPlayerState()->OnPlayerAction(HitResult);
+ // auto const NewlySelectedLocation = HitResult.Location;
+ // ATrooper *NewlySelectedTrooper = Cast(
+ // HitResult.GetActor());
+ //
+ // // skip re-selection
+ // if (SelectedTrooper == NewlySelectedTrooper) {
+ // UE_LOG(LogTemp, Warning, TEXT("Skip reselection"));
+ // return;
+ // }
+ //
+ // if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper->
+ // IsValidLowLevel() || NewlySelectedTrooper->GetPlayerIndex() !=
+ // PlayerIndex) {
+ // // we selected something that is not a trooper (or trooper in shitty state...)
+ // // probably we should move to it if we can...
+ //
+ // // UE_LOG(LogTemp, Warning, TEXT("Not a trooper"));
+ //
+ // // if initial trooper is valid...
+ // if (SelectedTrooper != nullptr && SelectedTrooper->IsValidLowLevel()) {
+ // switch (CurrentAction) {
+ // case 0:
+ // UE_LOG(LogTemp, Warning, TEXT("Do move"));
+ // // move this mf
+ // MoveTrooper(SelectedTrooper, NewlySelectedLocation);
+ // // and reset the selection....
+ // SelectedTrooper->SetSelection(false);
+ // SelectedTrooper = nullptr;
+ // break;
+ // default:
+ // // ATTACK! ATTACK!
+ // UE_LOG(LogTemp, Warning, TEXT("Do attack"));
+ // Attack(SelectedTrooper, NewlySelectedLocation,
+ // CurrentAction);
+ // SelectedTrooper->SetSelection(false);
+ // SelectedTrooper = nullptr;
+ // break;
+ // }
+ // }
+ // } else if (NewlySelectedTrooper != nullptr && NewlySelectedTrooper->
+ // IsValidLowLevel() && NewlySelectedTrooper->GetPlayerIndex() ==
+ // PlayerIndex) {
+ // UE_LOG(LogTemp, Warning, TEXT("Do reselect"));
+ // // our move, selection
+ // if (SelectedTrooper) {
+ // SelectedTrooper->SetSelection(false);
+ // }
+ // SelectedTrooper = NewlySelectedTrooper;
+ // SelectedTrooper->SetSelection(true);
+ // }
+
+ //
+ // if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper->
+ // IsValidLowLevel()) {
+ // // we selected something that is not a trooper (or trooper in shitty state...)
+ // // probably we should move to it if we can...
+ //
+ // UE_LOG(LogTemp, Warning, TEXT("Not a trooper"));
+ //
+ // // if initial trooper is valid...
+ // if (SelectedTrooper != nullptr && SelectedTrooper->IsValidLowLevel()) {
+ // UE_LOG(LogTemp, Warning, TEXT("Do move"));
+ // // move this mf
+ // MoveTrooper(SelectedTrooper, NewlySelectedLocation);
+ // // and reset the selection....
+ // SelectedTrooper->SetSelection(false);
+ // SelectedTrooper = nullptr;
+ // }
+ // return;
+ // }
+ // UE_LOG(LogTemp, Warning, TEXT("New Selected Player Index %d"),
+ // NewlySelectedTrooper->GetPlayerIndex());
+ // // skip re-selection
+ // if (SelectedTrooper == NewlySelectedTrooper) {
+ // UE_LOG(LogTemp, Warning, TEXT("Skip reselection"));
+ // return;
+ // }
+ // we selected valid trooper...
+ // if (NewlySelectedTrooper->GetPlayerIndex() == PlayerIndex) {
+ // UE_LOG(LogTemp, Warning, TEXT("Do reselect"));
+ // // our move, selection
+ // if (SelectedTrooper) {
+ // SelectedTrooper->SetSelection(false);
+ // }
+ // SelectedTrooper = NewlySelectedTrooper;
+ // SelectedTrooper->SetSelection(true);
+ // } else {
+ // UE_LOG(LogTemp, Warning, TEXT("Attack or skip..."));
+ // // maybe selected trooper had gone crazy...
+ // if (SelectedTrooper == nullptr || !SelectedTrooper->IsValidLowLevel())
+ // return;
+ // UE_LOG(LogTemp, Warning, TEXT("Do attack"));
+ // // ATTACK!!! ATTACK!!!!!!
+ // AttackTrooper(SelectedTrooper, NewlySelectedTrooper);
+ // SelectedTrooper->SetSelection(false);
+ // SelectedTrooper = nullptr;
+ // }
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.h b/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.h
new file mode 100644
index 0000000..c5e090d
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerController.h
@@ -0,0 +1,79 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/PlayerController.h"
+#include "BattlePlayerController.generated.h"
+
+// DECLARE_DYNAMIC_DELEGATE_OneParam(FOnMyTurnChangedDelegate, bool, bMyTurn);
+
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattlePlayerController : public APlayerController {
+ GENERATED_BODY()
+
+public:
+ virtual void BeginPlay() override;
+
+ // FOnMyTurnChangedDelegate OnMyTurnChanged;
+
+ virtual void SetupInputComponent() override;
+
+ ABattlePlayerController();
+
+ UFUNCTION(BlueprintCallable, Server, Reliable)
+ void EndTurn();
+
+ // UFUNCTION(Client, Reliable)
+ // void StartTurn();
+
+ // UFUNCTION(Client, Reliable, BlueprintCallable)
+ // void EndTurn();
+ //
+ // UFUNCTION(Server, Reliable)
+ // void MoveTrooper(ATrooper *Trooper, FVector Location);
+ //
+ // // // UFUNCTION(Server, Reliable)
+ // // // void AttackTrooper(ATrooper *Attacker, ATrooper *Victim);
+ //
+ // UFUNCTION(Server, Reliable)
+ // void Attack(ATrooper *Attacker, FVector Location, int ActionIndex);
+
+ UFUNCTION()
+ void SetPlayerIndex(uint8 NewPlayerIndex);
+
+ UFUNCTION()
+ uint8 GetPlayerIndex() const;
+
+ // UFUNCTION(BlueprintCallable)
+ // float SetCurrentActionAndReturnRadius(int action);
+
+ // UFUNCTION(Client, Reliable)
+ // void SetEnemySelection(const TArray &Troopers) const;
+
+private:
+ UPROPERTY(EditAnywhere)
+ TSubclassOf WidgetClass;
+
+ // UPROPERTY(Replicated)
+ // bool bIsMyTurn;
+ //
+ // UPROPERTY(Replicated)
+ // int CurrentAction = 0;
+ //
+ UPROPERTY(Replicated)
+ uint8 PlayerIndex;
+ //
+ // UPROPERTY(Replicated)
+ // ATrooper *SelectedTrooper;
+
+ UFUNCTION()
+ void OnLeftMouseClick();
+
+ // void SetMyTurn(bool bMyTurn);
+
+ auto GetMyGameMode() const;
+
+ auto GetMyGameState() const;
+
+ auto GetMyPlayerState() const;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.cpp b/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.cpp
new file mode 100644
index 0000000..bf290d5
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.cpp
@@ -0,0 +1,8 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "BattlePlayerStart.h"
+
+uint8 ABattlePlayerStart::GetPlayerIndex() const {
+ return PlayerIndex;
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.h b/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.h
new file mode 100644
index 0000000..3c43457
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerStart.h
@@ -0,0 +1,23 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/PlayerStart.h"
+#include "BattlePlayerStart.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattlePlayerStart : public APlayerStart {
+ GENERATED_BODY()
+
+public:
+ uint8 GetPlayerIndex() const;
+
+protected:
+private:
+ UPROPERTY(EditAnywhere, Category="Spawn Info")
+ uint8 PlayerIndex = 0;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.cpp b/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.cpp
new file mode 100644
index 0000000..6c0928c
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.cpp
@@ -0,0 +1,219 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "BattlePlayerState.h"
+
+#include "GameOverWidget.h"
+#include "BattleGameState.h"
+#include "Kismet/GameplayStatics.h"
+#include "Net/UnrealNetwork.h"
+
+ABattlePlayerState::ABattlePlayerState()
+ : Super(), bIsMyTurn(false), SelectedTrooper(nullptr) {
+ // PrimaryActorTick.bCanEverTick = true;
+}
+
+void ABattlePlayerState::BeginPlay() {
+ Super::BeginPlay();
+}
+
+auto ABattlePlayerState::GetMyGameState() const {
+ return Cast(GetWorld()->GetGameState());
+}
+
+// void AMyPlayerState::Tick(float DeltaSeconds) {
+// Super::Tick(DeltaSeconds);
+// if (GetMyGameState() && GetMyGameState()->IsGameStarted()) {
+// // for (const auto Actor : Troopers) {
+// // const auto Trooper = Cast(Actor);
+// // if (Trooper != nullptr && Trooper->GetPlayerIndex() !=
+// // PlayerIndex) {
+// // Trooper->HighlightAsEnemy(PlayerIndex);
+// // }
+// // }
+// bIsSelectionInitialized = true;
+// SetActorTickEnabled(false);
+// }
+// }
+
+void ABattlePlayerState::SetPlayerIndex(uint8 NewPlayerIndex) {
+ PlayerIndex = NewPlayerIndex;
+}
+
+void ABattlePlayerState::GameOver_Implementation(int PlayerLoseIndex) {
+ UGameOverWidget *CreatedWidget = CreateWidget(
+ GetWorld(), GameOverWidgetClass);
+ CreatedWidget->AddToViewport();
+ CreatedWidget->SetWidgetText(PlayerLoseIndex != PlayerIndex);
+}
+
+void ABattlePlayerState::SetEnemySelection_Implementation(
+ /*const TArray &Troopers*/) const {
+ TArray Troopers;
+ UGameplayStatics::GetAllActorsOfClass(GetWorld(),
+ ATrooper::StaticClass(),
+ Troopers);
+ for (const auto Actor : Troopers) {
+ const auto Trooper = Cast(Actor);
+ if (Trooper != nullptr) {
+ Trooper->HighlightAsEnemy(PlayerIndex);
+ }
+ }
+}
+
+void ABattlePlayerState::MoveTrooper_Implementation(ATrooper *Trooper,
+ FVector Location) {
+ Location.Z = 0.0f;
+ if (Trooper->CheckMoveCorrectness(Location)) {
+ Trooper->MoveTrooper(Location);
+ // GetMyGameMode()->CycleTurns();
+ } else {
+ GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
+ FString::Printf(
+ TEXT("Out of move radius!")));
+ }
+}
+
+// void AMyPlayerState::Attack_Implementation(ATrooper *Attacker,
+// FVector Location,
+// int ActionIndex) {
+// if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
+// Attacker->Attack(ActionIndex);
+// } else {
+// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
+// FString::Printf(
+// TEXT(
+// "Out of radius or not enough Action Points!")));
+// }
+// }
+
+void ABattlePlayerState::Attack_Implementation(ATrooper *Attacker,
+ FVector Location,
+ int ActionIndex) {
+ if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
+ Attacker->Attack(ActionIndex, Location);
+ // for (const auto Trooper : Troopers) {
+ // if (Attacker->GetPlayerIndex() != Trooper->GetPlayerIndex()) {
+ // Trooper->TakeDamage(Attacker->GetAbility(ActionIndex)->Damage);
+ // }
+ // }
+ } else {
+ GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
+ FString::Printf(
+ TEXT(
+ "Out of radius or not enough Action Points!")));
+ }
+}
+
+// void AMyPlayerState::CycleTurns_Implementation() const {
+// if (bIsMyTurn) {
+// GetMyGameState()->CycleTurns();
+// }
+// }
+
+bool ABattlePlayerState::IsMyTurn() const {
+ return bIsMyTurn;
+}
+
+void ABattlePlayerState::SetMyTurn(bool bMyTurn) {
+ bIsMyTurn = bMyTurn;
+ if (bIsMyTurn) {
+ GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green,
+ FString::Printf(
+ TEXT("CURRENT TURN: %d"),
+ PlayerIndex));
+ }
+}
+
+void ABattlePlayerState::StartTurn_Implementation() {
+ SetMyTurn(true);
+ UE_LOG(LogTemp, Warning, TEXT("Your turn, %d"), PlayerIndex);
+}
+
+void ABattlePlayerState::EndTurn_Implementation() {
+ if (bIsMyTurn) {
+ UE_LOG(LogTemp, Warning, TEXT("End Turn from player %d"),
+ PlayerIndex);
+ SetMyTurn(false);
+ if (SelectedTrooper) {
+ SelectedTrooper->SetSelection(false, CurrentAction);
+ SelectedTrooper = nullptr;
+ }
+ UE_LOG(LogTemp, Warning, TEXT("Not your turn, %d"), PlayerIndex);
+ // AMyGameMode *gameMode = GetMyGameMode();
+ // gameMode->CycleTurns();
+ // Cast(GetWorld()->GetGameState())->CycleTurns();
+ }
+}
+
+void ABattlePlayerState::OnPlayerAction(const FHitResult &HitResult) {
+ auto const NewlySelectedLocation = HitResult.Location;
+ ATrooper *NewlySelectedTrooper = Cast(
+ HitResult.GetActor());
+
+ // skip re-selection
+ if (SelectedTrooper == NewlySelectedTrooper) {
+ UE_LOG(LogTemp, Warning, TEXT("Skip reselection"));
+ return;
+ }
+
+ if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper->
+ IsValidLowLevel() || NewlySelectedTrooper->GetPlayerIndex() !=
+ PlayerIndex) {
+ if (SelectedTrooper != nullptr && SelectedTrooper->
+ IsValidLowLevel()) {
+ switch (CurrentAction) {
+ case 0:
+ UE_LOG(LogTemp, Warning, TEXT("Do move"));
+ // move this mf
+ MoveTrooper(SelectedTrooper, NewlySelectedLocation);
+ // and reset the selection....
+ SelectedTrooper->SetSelection(false, CurrentAction);
+ SelectedTrooper = nullptr;
+ break;
+ default:
+ // ATTACK! ATTACK!
+ UE_LOG(LogTemp, Warning, TEXT("Do attack"));
+ Attack(SelectedTrooper, NewlySelectedLocation,
+ CurrentAction);
+ SelectedTrooper->SetSelection(false, CurrentAction);
+ SelectedTrooper = nullptr;
+ break;
+ }
+ }
+ } else if (NewlySelectedTrooper != nullptr && NewlySelectedTrooper->
+ IsValidLowLevel() && NewlySelectedTrooper->GetPlayerIndex()
+ ==
+ PlayerIndex) {
+ UE_LOG(LogTemp, Warning, TEXT("Do reselect"));
+ // our move, selection
+ if (SelectedTrooper) {
+ SelectedTrooper->SetSelection(false, CurrentAction);
+ }
+ SelectedTrooper = NewlySelectedTrooper;
+ SelectedTrooper->SetSelection(true, CurrentAction);
+ }
+}
+
+void ABattlePlayerState::SetCurrentAction_Implementation(int Action) {
+ CurrentAction = Action;
+ if (SelectedTrooper) {
+ SelectedTrooper->UpdateSelectionRadius(Action);
+ }
+ UE_LOG(LogTemp, Warning,
+ TEXT("SetCurrentAction: %d on Player Controller "
+ "with index %d"), CurrentAction, PlayerIndex);
+}
+
+uint8 ABattlePlayerState::GetPlayerIndex() const {
+ return PlayerIndex;
+}
+
+void ABattlePlayerState::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(ABattlePlayerState, PlayerIndex);
+ DOREPLIFETIME(ABattlePlayerState, CurrentAction);
+ DOREPLIFETIME(ABattlePlayerState, bIsMyTurn);
+ DOREPLIFETIME(ABattlePlayerState, SelectedTrooper);
+ DOREPLIFETIME(ABattlePlayerState, bIsSelectionInitialized);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.h b/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.h
new file mode 100644
index 0000000..a68163b
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/BattlePlayerState.h
@@ -0,0 +1,85 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "Trooper/Trooper.h"
+#include "CoreMinimal.h"
+#include "GameFramework/PlayerState.h"
+#include "BattlePlayerState.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API ABattlePlayerState : public APlayerState {
+ GENERATED_BODY()
+
+public:
+ ABattlePlayerState();
+
+ virtual void BeginPlay() override;
+
+ // virtual void Tick(float DeltaSeconds) override;
+
+ UFUNCTION(Client, Reliable)
+ void StartTurn();
+
+ UFUNCTION(Client, Reliable, BlueprintCallable)
+ void EndTurn();
+
+ UFUNCTION(Server, Reliable)
+ void MoveTrooper(ATrooper *Trooper, FVector Location);
+
+ UFUNCTION(Server, Reliable)
+ void Attack(ATrooper *Attacker,
+ FVector Location,
+ int ActionIndex);
+
+ // UFUNCTION(Client, Reliable)
+ // void CycleTurns() const;
+
+ UFUNCTION(BlueprintCallable)
+ bool IsMyTurn() const;
+
+ UFUNCTION()
+ void OnPlayerAction(const FHitResult &HitResult);
+
+ UFUNCTION(BlueprintCallable, Client, Reliable)
+ void SetCurrentAction(int Action);
+
+ UFUNCTION(BlueprintCallable)
+ uint8 GetPlayerIndex() const;
+
+ UFUNCTION()
+ void SetPlayerIndex(uint8 NewPlayerIndex);
+
+ UFUNCTION(Client, Reliable)
+ void SetEnemySelection(/*const TArray &Troopers*/) const;
+
+ UFUNCTION(Client, Reliable)
+ void GameOver(int PlayerLoseIndex);
+
+protected:
+ UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ TSubclassOf GameOverWidgetClass;
+
+ UPROPERTY(Replicated)
+ bool bIsSelectionInitialized = false;
+
+ UPROPERTY(Replicated)
+ uint8 PlayerIndex;
+
+ UFUNCTION()
+ void SetMyTurn(bool bMyTurn);
+
+ UPROPERTY(Replicated)
+ bool bIsMyTurn;
+
+ UPROPERTY(Replicated)
+ int CurrentAction = 0;
+
+ UPROPERTY(Replicated)
+ ATrooper *SelectedTrooper;
+
+ auto GetMyGameState() const;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/GameOverWidget.cpp b/Source/TurnBasedTutorial/BattleMode/GameOverWidget.cpp
new file mode 100644
index 0000000..4e25a46
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/GameOverWidget.cpp
@@ -0,0 +1,29 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "GameOverWidget.h"
+#include "../SessionsGameInstanceSubsystem.h"
+#include "Components/Button.h"
+#include "Components/TextBlock.h"
+#include "Kismet/GameplayStatics.h"
+
+
+void UGameOverWidget::NativeConstruct() {
+ ButtonToMenu->OnClicked.AddDynamic(this, &ThisClass::QuitCurrentSession);
+}
+
+void UGameOverWidget::QuitCurrentSession() {
+ const UGameInstance *GameInstance = UGameplayStatics::GetGameInstance(
+ GetWorld());
+ USessionsGameInstanceSubsystem *GameInstanceSubsystem = GameInstance->GetSubsystem
+ ();
+ GameInstanceSubsystem->QuitCurrentSession();
+}
+
+
+void UGameOverWidget::SetWidgetText_Implementation(bool HasWon) {
+ if (HasWon) {
+ GameOverText->SetText(FText::FromString("You won!"));
+ } else {
+ GameOverText->SetText(FText::FromString("You lose!"));
+ }
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/GameOverWidget.h b/Source/TurnBasedTutorial/BattleMode/GameOverWidget.h
new file mode 100644
index 0000000..82402fe
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/GameOverWidget.h
@@ -0,0 +1,31 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Blueprint/UserWidget.h"
+#include "GameOverWidget.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API UGameOverWidget : public UUserWidget {
+ GENERATED_BODY()
+
+public:
+ virtual void NativeConstruct() override;
+
+ UFUNCTION(Client, Reliable)
+ void SetWidgetText(bool HasWon);
+
+ UFUNCTION()
+ void QuitCurrentSession();
+
+protected:
+ UPROPERTY(meta = (BindWidget))
+ class UButton *ButtonToMenu;
+
+ UPROPERTY(meta = (BindWidget))
+ class UTextBlock *GameOverText;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.cpp b/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.cpp
new file mode 100644
index 0000000..68fcde5
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.cpp
@@ -0,0 +1,226 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "EnemyAIController.h"
+#include "SinglePlayerGS.h"
+#include "Kismet/GameplayStatics.h"
+#include "GenericPlatform/GenericPlatformMath.h"
+
+AEnemyAIController::AEnemyAIController()
+ : Super() {
+ PrimaryActorTick.bCanEverTick = true;
+ SetActorTickInterval(2.0f);
+}
+
+void AEnemyAIController::Tick(float DeltaSeconds) {
+ Super::Tick(DeltaSeconds);
+ if (bIsAITurn && !bIsActing) {
+ if (TroopersCursor >= PossessedTroopers.Num()) {
+ EndTurn();
+ } else {
+ MakeMove();
+ }
+ }
+}
+
+
+void AEnemyAIController::StartTurn() {
+ UE_LOG(LogTemp, Warning, TEXT("Enemy AI StartTurn"));
+ bIsAITurn = true;
+ bIsActing = false;
+ // MakeMove();
+}
+
+void AEnemyAIController::EndTurn() {
+ bIsAITurn = false;
+ TroopersCursor = 0;
+ bIsEnded = true;
+}
+
+void AEnemyAIController::ActionDone() {
+ bIsActing = false;
+}
+
+bool AEnemyAIController::IsAITurn() const {
+ return bIsAITurn;
+}
+
+void AEnemyAIController::SpawnIfNeeded() {
+ RemoveDeadTroopers();
+ while (PossessedTroopers.Num() < MAX_TROOPERS_COUNT) {
+ const FVector Location = GetFreeLocation();
+ const FVector Rotation = {0.0f, 0.0f, 0.0f};
+ FTransform SpawnLocationAndRotation(Rotation);
+ SpawnLocationAndRotation.SetLocation(Location);
+ const TArray> &LoadedTroopersAssets = GetWorld()->
+ GetGameState()->GetTroopersAssets();
+ AActor *Spawned = GetWorld()->SpawnActorDeferred(
+ LoadedTroopersAssets[FMath::RandRange(
+ 0, LoadedTroopersAssets.Num() - 1)],
+ SpawnLocationAndRotation);
+ Cast(Spawned)->Initialize(
+ 1, Location, TroopersCount++);
+ Spawned->FinishSpawning(SpawnLocationAndRotation);
+ Spawned->SetActorLocation(Location);
+ ATrooper *Trooper = Cast(Spawned);
+ GetWorld()->GetGameState()->AddTrooper(Trooper);
+ PossessedTroopers.Add(Trooper);
+ Trooper->SetAIPossession(this);
+ Trooper->HighlightAsEnemy(PLAYER_INDEX);
+ }
+}
+
+// void AEnemyAIController::SetTrooperAssetsAndSpawn(
+// TArray TrooperAssets,
+// int TrooperCount) {
+// LoadedTrooperAssets = MoveTemp(TrooperAssets);
+// TroopersCount = TrooperCount;
+// SpawnIfNeeded();
+// }
+
+void AEnemyAIController::RemoveDeadTroopers() {
+ for (int index = 0; index < PossessedTroopers.Num(); ++index) {
+ if (!PossessedTroopers[index] || !PossessedTroopers[index]->
+ IsValidLowLevel() ||
+ PossessedTroopers[index]->IsDead()) {
+ PossessedTroopers.RemoveAtSwap(index);
+ index--;
+ }
+ }
+}
+
+FVector AEnemyAIController::GetFreeLocation() const {
+ for (const auto Location : SpawnPoints) {
+ bool bIsClose = false;
+ for (const auto Trooper : PossessedTroopers) {
+ if ((Location - Trooper->GetLocation()).Size() < 100.0f) {
+ bIsClose = true;
+ break;
+ }
+ }
+ if (bIsClose) {
+ continue;
+ }
+ return Location;
+ }
+ return {-2000.0f, FMath::RandRange(-2000.0f, 2000.0f), 0.0f};
+}
+
+void AEnemyAIController::MakeMove() {
+ while (TroopersCursor < PossessedTroopers.Num()) {
+ while (TroopersCursor < PossessedTroopers.Num() && (
+ !PossessedTroopers[TroopersCursor] ||
+ !PossessedTroopers[TroopersCursor]->IsValidLowLevel()
+ || PossessedTroopers[TroopersCursor]->IsDead())) {
+ ++TroopersCursor;
+ }
+ if (TroopersCursor >= PossessedTroopers.Num()) {
+ return;
+ }
+ const int Index = GetClosestTrooper();
+ if (Index == -1) {
+ return;
+ }
+ bool failed;
+ if (!IsCloseEnough(Index)) {
+ failed = MoveTo(Index);
+ } else {
+ failed = TryAttack(Index);
+ }
+ if (failed) {
+ TroopersCursor++;
+ } else {
+ return;
+ }
+ }
+}
+
+bool AEnemyAIController::IsCloseEnough(int TrooperIndex) const {
+ const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor];
+ const ATrooper *OtherTrooper = PlayerTroopers[TrooperIndex];
+ return (CurrentTrooper->GetLocation() - OtherTrooper->GetLocation()).Size()
+ <= CurrentTrooper->GetRealActionRadius(1);
+}
+
+bool AEnemyAIController::TryAttack(int TrooperIndex) {
+ ATrooper *Attacker = PossessedTroopers[TroopersCursor];
+ const auto Location = PlayerTroopers[TrooperIndex]->GetLocation();
+ constexpr int ActionIndex = 1;
+ if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
+ bIsActing = true;
+ Attacker->Attack(ActionIndex, Location);
+ return false;
+ }
+ return true;
+}
+
+bool AEnemyAIController::MoveTo(int TrooperIndex) {
+ ATrooper *Trooper = PossessedTroopers[TroopersCursor];
+ const FVector CurrentLocation = Trooper->GetLocation();
+ const FVector Destination = PlayerTroopers[TrooperIndex]->GetLocation();
+ constexpr int ActionIndex = 1;
+ constexpr int MoveActionIndex = 0;
+ const float AttackRadius = Trooper->GetRealActionRadius(ActionIndex);
+ const FVector Vector = Destination - CurrentLocation;
+ const float MaxLength = Trooper->GetRealActionRadius(MoveActionIndex);
+ float PathLength = Vector.Size() - AttackRadius + 10.f;
+ PathLength = FMath::Min(PathLength, MaxLength);
+ FVector Location = CurrentLocation + Vector.GetSafeNormal2D() *
+ PathLength;
+ Location.Z = 0.0f;
+ if (PathLength > 1.0f && Trooper->CheckMoveCorrectness(Location)) {
+ bIsActing = true;
+ Trooper->MoveTrooper(Location);
+ return false;
+ }
+ return true;
+}
+
+void AEnemyAIController::InitializeSpawnPoints() {
+ SpawnPoints.Add(FVector{
+ -2000.0f,
+ -(MAX_TROOPERS_COUNT - 1) * TROOPERS_DISTANCE.Y / 2,
+ 0.0f
+ });
+ for (int index = 1; index < MAX_TROOPERS_COUNT; ++index) {
+ SpawnPoints.Add(SpawnPoints[SpawnPoints.Num() - 1] + TROOPERS_DISTANCE);
+ }
+}
+
+int AEnemyAIController::GetClosestTrooper() const {
+ float minDistance = 1000000.0f;
+ int minIndex = -1;
+ const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor];
+ for (int index = 0; index < PlayerTroopers.Num(); ++index) {
+ const ATrooper *OtherTrooper = PlayerTroopers[index];
+ if (OtherTrooper->IsValidLowLevel() && !OtherTrooper->IsDead()) {
+ const float distance = (
+ CurrentTrooper->GetLocation() - OtherTrooper->
+ GetLocation()).Size();
+ if (distance < minDistance) {
+ minDistance = distance;
+ minIndex = index;
+ }
+ }
+ }
+ return minIndex;
+}
+
+void AEnemyAIController::InitializeTroopers(
+ const TArray &Troopers) {
+ for (const auto Trooper : Troopers) {
+ if (Trooper != nullptr) {
+ if (Trooper->GetPlayerIndex() == AI_INDEX) {
+ Trooper->SetAIPossession(this);
+ PossessedTroopers.Add(Trooper);
+ } else {
+ PlayerTroopers.Add(Trooper);
+ }
+ }
+ }
+ InitializeSpawnPoints();
+ SpawnIfNeeded();
+}
+
+void AEnemyAIController::BeginPlay() {
+ Super::BeginPlay();
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.h b/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.h
new file mode 100644
index 0000000..01d9a5d
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/EnemyAIController.h
@@ -0,0 +1,95 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "EnemyAIController.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API AEnemyAIController : public AActor {
+ GENERATED_BODY()
+
+public:
+ AEnemyAIController();
+
+ UFUNCTION()
+ void StartTurn();
+
+ UFUNCTION()
+ void EndTurn();
+
+ UFUNCTION()
+ void ActionDone();
+
+ UFUNCTION()
+ void InitializeTroopers(const TArray &Troopers);
+
+ virtual void BeginPlay() override;
+
+ virtual void Tick(float DeltaSeconds) override;
+
+ UPROPERTY()
+ bool bIsEnded = false;
+
+ UFUNCTION()
+ bool IsAITurn() const;
+
+ UFUNCTION()
+ void SpawnIfNeeded();
+
+ // UFUNCTION()
+ // void SetTrooperAssetsAndSpawn(TArray TrooperAssets, int TrooperCount);
+
+private:
+ static constexpr int AI_INDEX = 1;
+
+ static constexpr int PLAYER_INDEX = 0;
+
+ static constexpr int MAX_TROOPERS_COUNT = 3;
+
+ UPROPERTY()
+ int TroopersCount = 5;
+
+ // UPROPERTY()
+ // TArray LoadedTrooperAssets;
+
+ UFUNCTION()
+ void RemoveDeadTroopers();
+
+ FVector GetFreeLocation() const;
+
+ UFUNCTION()
+ void MakeMove();
+
+ UPROPERTY()
+ int TroopersCursor = 0;
+
+ int GetClosestTrooper() const;
+
+ bool IsCloseEnough(int TrooperIndex) const;
+
+ bool TryAttack(int TrooperIndex);
+
+ bool MoveTo(int TrooperIndex);
+
+ void InitializeSpawnPoints();
+
+ UPROPERTY()
+ bool bIsAITurn = false;
+
+ UPROPERTY()
+ bool bIsActing = false;
+
+ UPROPERTY()
+ TArray PossessedTroopers;
+
+ UPROPERTY()
+ TArray PlayerTroopers;
+
+ TArray SpawnPoints;
+
+ const FVector TROOPERS_DISTANCE = {0.0f, 1000.0f, 0.0f};
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.cpp b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.cpp
new file mode 100644
index 0000000..64d327a
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.cpp
@@ -0,0 +1,21 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "SinglePlayerGM.h"
+#include "SinglePlayerGS.h"
+
+ASinglePlayerGM::ASinglePlayerGM()
+ : Super() {
+ GameStateClass = ASinglePlayerGS::StaticClass();
+ bIsMultiplayer = false;
+}
+
+void ASinglePlayerGM::BeginPlay() {
+ Super::BeginPlay();
+ UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameMode BeginPlay"));
+ GameStateClass = ASinglePlayerGS::StaticClass();
+ StartGame();
+}
+
+void ASinglePlayerGM::PostLogin(APlayerController *NewPlayer) {
+ AGameMode::PostLogin(NewPlayer);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.h b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.h
new file mode 100644
index 0000000..08d73ab
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGM.h
@@ -0,0 +1,22 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "../BattleGameMode.h"
+#include "SinglePlayerGM.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API ASinglePlayerGM : public ABattleGameMode {
+ GENERATED_BODY()
+
+public:
+ ASinglePlayerGM();
+
+ virtual void BeginPlay() override;
+
+ virtual void PostLogin(APlayerController *NewPlayer) override;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.cpp b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.cpp
new file mode 100644
index 0000000..e272f81
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.cpp
@@ -0,0 +1,66 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "SinglePlayerGS.h"
+#include "Net/UnrealNetwork.h"
+
+ASinglePlayerGS::ASinglePlayerGS()
+ : Super() {
+ bIsMultiplayer = false;
+ PrimaryActorTick.bCanEverTick = true;
+ SetActorTickInterval(0.5f);
+}
+
+void ASinglePlayerGS::BeginPlay() {
+ Super::BeginPlay();
+ UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameState BeginPlay"));
+ if (LivingTroopersCount.Num() < 2) {
+ LivingTroopersCount.SetNum(2);
+ }
+ EnemyAiManager = GetWorld()->SpawnActor(
+ AEnemyAIController::StaticClass(), FVector(0.0f, 0.0f, 1000.0f),
+ FRotator(0.0f, 0.0f, 0.0f), FActorSpawnParameters());
+ EnemyAiManager->InitializeTroopers(Troopers);
+}
+
+void ASinglePlayerGS::CycleTurns() {
+ if (CurrentPlayerTurn == 0) {
+ PlayerInTurn()->EndTurn();
+ }
+ for (const auto Trooper : Troopers) {
+ if (Trooper != nullptr) {
+ Trooper->ResetActionPoints();
+ }
+ }
+ CurrentPlayerTurn = !CurrentPlayerTurn;
+ if (CurrentPlayerTurn == 0) {
+ PlayerInTurn()->StartTurn();
+ } else {
+ EnemyAiManager->StartTurn();
+ }
+}
+
+void ASinglePlayerGS::Tick(float DeltaSeconds) {
+ Super::Tick(DeltaSeconds);
+ if (EnemyAiManager->bIsEnded) {
+ EnemyAiManager->bIsEnded = false;
+ EnemyAiManager->SpawnIfNeeded();
+ CycleTurns();
+ } else if (CurrentPlayerTurn == 1 && !EnemyAiManager->IsAITurn()) {
+ CycleTurns();
+ }
+}
+
+AEnemyAIController *ASinglePlayerGS::GetEnemyAIController() const {
+ return EnemyAiManager;
+}
+
+const TArray> &ASinglePlayerGS::
+GetTroopersAssets() const {
+ return TrooperBpAssets;
+}
+
+void ASinglePlayerGS::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(ASinglePlayerGS, EnemyAiManager);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.h b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.h
new file mode 100644
index 0000000..1ca3171
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Singleplayer/SinglePlayerGS.h
@@ -0,0 +1,36 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "EnemyAIController.h"
+#include "../BattleGameState.h"
+#include "SinglePlayerGS.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API ASinglePlayerGS : public ABattleGameState {
+ GENERATED_BODY()
+
+public:
+ ASinglePlayerGS();
+
+ virtual void BeginPlay() override;
+
+ virtual void CycleTurns() override;
+
+ virtual void Tick(float DeltaSeconds) override;
+
+ AEnemyAIController *GetEnemyAIController() const;
+
+ const TArray> &GetTroopersAssets() const;
+
+protected:
+ UPROPERTY(Replicated)
+ AEnemyAIController *EnemyAiManager = nullptr;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ TArray> TrooperBpAssets;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.cpp b/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.cpp
new file mode 100644
index 0000000..d35f5df
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.cpp
@@ -0,0 +1,20 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "Ability.h"
+
+// Sets default values for this component's properties
+UAbility::UAbility() {
+ PrimaryComponentTick.bCanEverTick = false;
+}
+
+// Called when the game starts
+void UAbility::BeginPlay() {
+ Super::BeginPlay();
+}
+
+// void UAbility::TickComponent(float DeltaTime,
+// ELevelTick TickType,
+// FActorComponentTickFunction *ThisTickFunction) {
+// Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+//
+// }
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.h b/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.h
new file mode 100644
index 0000000..079f2b6
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Ability.h
@@ -0,0 +1,44 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Components/ActorComponent.h"
+#include "Ability.generated.h"
+
+
+UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
+class TURNBASEDTUTORIAL_API UAbility : public UActorComponent {
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this component's properties
+ UAbility();
+
+ // Called when the game starts
+ virtual void BeginPlay() override;
+
+ UPROPERTY(EditAnywhere)
+ float ActionCost = 100.0f;
+
+ UPROPERTY(EditAnywhere)
+ float Damage = 50.0f;
+
+ UPROPERTY(EditAnywhere)
+ float ActionRadius = 1000.0f;
+
+ UPROPERTY(EditAnywhere)
+ float SplashRadius = 100.0f;
+
+ UPROPERTY(EditAnywhere)
+ float LinearWidth = 50.0f;
+
+ UPROPERTY(EditAnywhere)
+ float Speed = 1500.0f;
+
+ // Called every frame
+ // virtual void TickComponent(float DeltaTime,
+ // ELevelTick TickType,
+ // FActorComponentTickFunction *
+ // ThisTickFunction) override;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.cpp b/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.cpp
new file mode 100644
index 0000000..fa3484f
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.cpp
@@ -0,0 +1,66 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Explosion.h"
+#include "Trooper.h"
+#include "Net/UnrealNetwork.h"
+#include "Particles/ParticleSystemComponent.h"
+
+AExplosion::AExplosion() {
+ // if (!CollisionComponent) {
+ // CollisionComponent = CreateDefaultSubobject(
+ // TEXT("SphereComponent"));
+ // RootComponent = CollisionComponent;
+ // }
+ if (!ParticleSystemComponent) {
+ ParticleSystemComponent = CreateDefaultSubobject<
+ UParticleSystemComponent>(
+ TEXT("ParticleSystemComponent"));
+ RootComponent = ParticleSystemComponent;
+ }
+ InitialLifeSpan = 1.0f;
+}
+
+void AExplosion::Initialize(float damage,
+ float splashRadius,
+ uint8 playerIndex) {
+ Damage = damage;
+ PlayerIndex = playerIndex;
+ float Scale = splashRadius / 50;
+ // CollisionComponent->SetWorldScale3D({Scale, Scale, Scale});
+ if (ParticleSystemComponent && ParticleSystemComponent->IsValidLowLevel()) {
+ ParticleSystemComponent->SetWorldScale3D({Scale, Scale, Scale});
+ }
+}
+
+void AExplosion::BeginPlay() {
+ Super::BeginPlay();
+}
+
+void AExplosion::NotifyActorBeginOverlap(AActor *OtherActor) {
+ Super::NotifyActorBeginOverlap(OtherActor);
+ ATrooper *OtherTrooper = Cast(OtherActor);
+ if (OtherTrooper) {
+ UE_LOG(LogTemp, Warning,
+ TEXT(
+ "Begin explosion overlap: id: %d, index: %d, damage: %f, my index: %d"
+ ),
+ OtherTrooper->GetId(), OtherTrooper->GetPlayerIndex(), Damage,
+ PlayerIndex);
+ if (PlayerIndex != -1 && PlayerIndex != OtherTrooper->
+ GetPlayerIndex()) {
+ OtherTrooper->TrooperTakeDamage(Damage);
+ }
+ } else {
+ UE_LOG(LogTemp, Warning, TEXT("Overlapped not a trooper"));
+ }
+}
+
+void AExplosion::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(AExplosion, Damage);
+ DOREPLIFETIME(AExplosion, PlayerIndex);
+ // DOREPLIFETIME(AMyExplosion, CollisionComponent);
+ DOREPLIFETIME(AExplosion, ParticleSystemComponent);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.h b/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.h
new file mode 100644
index 0000000..e72f780
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Explosion.h
@@ -0,0 +1,36 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/Actor.h"
+#include "Explosion.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API AExplosion : public AActor {
+ GENERATED_BODY()
+
+public:
+ AExplosion();
+
+ void Initialize(float damage,
+ float splashRadius,
+ uint8 playerIndex);
+
+protected:
+ virtual void BeginPlay() override;
+
+ virtual void NotifyActorBeginOverlap(AActor *OtherActor) override;
+
+ UPROPERTY(Replicated)
+ float Damage;
+
+ UPROPERTY(Replicated)
+ int8 PlayerIndex = -1;
+
+ // UPROPERTY(EditAnywhere, Replicated)
+ // USphereComponent *CollisionComponent;
+
+ UPROPERTY(EditAnywhere, Replicated)
+ UParticleSystemComponent *ParticleSystemComponent;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.cpp b/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.cpp
new file mode 100644
index 0000000..9a7256e
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.cpp
@@ -0,0 +1,19 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "HealthBar.h"
+
+#include "Components/ProgressBar.h"
+
+void UHealthBar::SetOwnerTrooper(ATrooper *Trooper) {
+ OwnerTrooper = Trooper;
+}
+
+void UHealthBar::NativeTick(const FGeometry &MyGeometry, float InDeltaTime) {
+ Super::NativeTick(MyGeometry, InDeltaTime);
+ if (!OwnerTrooper.IsValid())
+ return;
+
+ HealthBar->SetPercent(
+ OwnerTrooper->GetHitPoints() / OwnerTrooper->GetMaxHitPoints());
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.h b/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.h
new file mode 100644
index 0000000..af06f73
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/HealthBar.h
@@ -0,0 +1,28 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Trooper.h"
+#include "Blueprint/UserWidget.h"
+#include "HealthBar.generated.h"
+
+/**
+ *
+ */
+UCLASS(Abstract)
+class TURNBASEDTUTORIAL_API UHealthBar : public UUserWidget {
+ GENERATED_BODY()
+
+public:
+ void SetOwnerTrooper(ATrooper *Trooper);
+
+protected:
+ TWeakObjectPtr OwnerTrooper;
+
+ UPROPERTY(meta=(BindWidget))
+ class UProgressBar *HealthBar;
+
+ virtual void
+ NativeTick(const FGeometry &MyGeometry, float InDeltaTime) override;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.cpp b/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.cpp
new file mode 100644
index 0000000..f05d525
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.cpp
@@ -0,0 +1,139 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Projectile.h"
+
+#include "Trooper.h"
+#include "Net/UnrealNetwork.h"
+
+AProjectile::AProjectile() {
+ // if (!RootComponent) {
+ // RootComponent = CreateDefaultSubobject(
+ // TEXT("ProjectileSceneComponent"));
+ // }
+ // if (!CollisionComponent) {
+ // CollisionComponent = CreateDefaultSubobject(
+ // TEXT("SphereComponent"));
+ // RootComponent = CollisionComponent;
+ // }
+ if (!ProjectileMeshComponent) {
+ ProjectileMeshComponent = CreateDefaultSubobject(
+ TEXT("ProjectileMeshComponent"));
+ static ConstructorHelpers::FObjectFinder Mesh(
+ TEXT(
+ "StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
+ if (Mesh.Succeeded()) {
+ ProjectileMeshComponent->SetStaticMesh(Mesh.Object);
+ RootComponent = ProjectileMeshComponent;
+ }
+ }
+ if (!ProjectileMovementComponent) {
+ ProjectileMovementComponent = CreateDefaultSubobject<
+ UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
+ ProjectileMovementComponent->SetUpdatedComponent(
+ // CollisionComponent);
+ ProjectileMeshComponent);
+ ProjectileMovementComponent->bRotationFollowsVelocity = true;
+ ProjectileMovementComponent->InitialSpeed = 1000.0f;
+ ProjectileMovementComponent->MaxSpeed = 1000.0f;
+ ProjectileMovementComponent->ProjectileGravityScale = 0.0f;
+ }
+ InitialLifeSpan = 2.0f;
+}
+
+void AProjectile::Initialize(const UAbility *Ability,
+ uint8 playerIndex,
+ float PathLength) {
+ ProjectileMovementComponent->InitialSpeed =
+ ProjectileMovementComponent->MaxSpeed = Ability->Speed;
+ Damage = Ability->Damage;
+ SplashRadius = Ability->SplashRadius;
+ float Scale = Ability->LinearWidth / 100;
+ // CollisionComponent->SetSphereRadius(Ability->LinearWidth / 2);
+ ProjectileMeshComponent->SetWorldScale3D({Scale, Scale, Scale});
+ PlayerIndex = playerIndex;
+ SetLifeSpan(PathLength / Ability->Speed);
+}
+
+void AProjectile::Shoot(FVector From, FVector To) const {
+ ProjectileMovementComponent->Velocity =
+ (To - From).GetSafeNormal() * ProjectileMovementComponent->InitialSpeed;
+}
+
+void AProjectile::NotifyActorBeginOverlap(AActor *OtherActor) {
+ Super::NotifyActorBeginOverlap(OtherActor);
+ ATrooper *OtherTrooper = Cast(OtherActor);
+ if (OtherTrooper) {
+ UE_LOG(LogTemp, Warning,
+ TEXT("Begin overlap: id: %d, index: %d, damage: %f, my index: %d"
+ ),
+ OtherTrooper->GetId(), OtherTrooper->GetPlayerIndex(), Damage,
+ PlayerIndex);
+ if (PlayerIndex != -1 && PlayerIndex != OtherTrooper->
+ GetPlayerIndex()) {
+ OtherTrooper->TrooperTakeDamage(Damage);
+ }
+ } else {
+ UE_LOG(LogTemp, Warning, TEXT("Overlapped not a trooper"));
+ }
+}
+
+// void AMyProjectile::NotifyHit(UPrimitiveComponent *MyComp,
+// AActor *Other,
+// UPrimitiveComponent *OtherComp,
+// bool bSelfMoved,
+// FVector HitLocation,
+// FVector HitNormal,
+// FVector NormalImpulse,
+// const FHitResult &Hit) {
+// Super::NotifyHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation,
+// HitNormal,
+// NormalImpulse, Hit);
+// ATrooper *OtherTrooper = Cast(Other);
+// if (OtherTrooper) {
+// UE_LOG(LogTemp, Warning,
+// TEXT("On Hit: id: %d, index: %d, damage: %f, my index: %d"
+// ),
+// OtherTrooper->GetId(), OtherTrooper->GetPlayerIndex(), Damage,
+// PlayerIndex);
+// if (PlayerIndex != -1 && PlayerIndex != OtherTrooper->
+// GetPlayerIndex()) {
+// OtherTrooper->TakeDamage(Damage);
+// }
+// } else {
+// UE_LOG(LogTemp, Warning, TEXT("Overlapped not a trooper"));
+// }
+// }
+
+
+void AProjectile::BeginPlay() {
+ Super::BeginPlay();
+}
+
+void AProjectile::EndPlay(const EEndPlayReason::Type EndPlayReason) {
+ Super::EndPlay(EndPlayReason);
+ Explode();
+}
+
+void AProjectile::Explode_Implementation() const {
+ const FTransform SpawnTransform = GetTransform();
+ FActorSpawnParameters SpawnParameters;
+ SpawnParameters.Instigator = GetInstigator();
+ SpawnParameters.SpawnCollisionHandlingOverride =
+ ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
+ AExplosion *Explosion = GetWorld()->SpawnActor(
+ ExplosionSubclass, SpawnTransform, SpawnParameters);
+ Explosion->Initialize(Damage, SplashRadius, PlayerIndex);
+ Explosion->SetActorLocation(GetActorLocation());
+}
+
+void AProjectile::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(AProjectile, Damage);
+ DOREPLIFETIME(AProjectile, PlayerIndex);
+ // DOREPLIFETIME(AMyProjectile, CollisionComponent);
+ DOREPLIFETIME(AProjectile, ProjectileMeshComponent);
+ DOREPLIFETIME(AProjectile, ProjectileMovementComponent);
+ DOREPLIFETIME(AProjectile, SplashRadius);
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.h b/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.h
new file mode 100644
index 0000000..9c1a9d2
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Projectile.h
@@ -0,0 +1,65 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Ability.h"
+#include "Explosion.h"
+#include "Components/SphereComponent.h"
+#include "GameFramework/Actor.h"
+#include "GameFramework/ProjectileMovementComponent.h"
+#include "Projectile.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API AProjectile : public AActor {
+ GENERATED_BODY()
+
+public:
+ AProjectile();
+
+ void Initialize(const UAbility *Ability,
+ uint8 playerIndex,
+ float PathLength);
+
+ void Shoot(FVector From, FVector To) const;
+
+protected:
+ virtual void NotifyActorBeginOverlap(AActor *OtherActor) override;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ TSubclassOf ExplosionSubclass;
+
+ // virtual void NotifyHit(UPrimitiveComponent *MyComp,
+ // AActor *Other,
+ // UPrimitiveComponent *OtherComp,
+ // bool bSelfMoved,
+ // FVector HitLocation,
+ // FVector HitNormal,
+ // FVector NormalImpulse,
+ // const FHitResult &Hit) override;
+
+ UPROPERTY(Replicated)
+ float Damage;
+
+ UPROPERTY(Replicated)
+ int8 PlayerIndex = -1;
+
+ UPROPERTY(Replicated)
+ float SplashRadius;
+
+ // UPROPERTY(EditAnywhere, Replicated)
+ // USphereComponent *CollisionComponent;
+
+ UPROPERTY(EditAnywhere, Replicated)
+ UStaticMeshComponent *ProjectileMeshComponent;
+
+ UPROPERTY(VisibleAnywhere, Replicated)
+ UProjectileMovementComponent *ProjectileMovementComponent;
+
+ virtual void BeginPlay() override;
+
+ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+
+ UFUNCTION(Server, Reliable)
+ void Explode() const;
+};
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.cpp b/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.cpp
new file mode 100644
index 0000000..d0f1809
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.cpp
@@ -0,0 +1,411 @@
+#include "Trooper.h"
+#include
+
+#include "HealthBar.h"
+#include "../BattleGameState.h"
+#include "Projectile.h"
+#include "Components/WidgetComponent.h"
+#include "Net/UnrealNetwork.h"
+
+// Sets default values
+ATrooper::ATrooper()
+ : HitPoints(StartHitPoints), ActionPoints(StartActionPoints) {
+ bReplicates = true;
+
+ PrimaryActorTick.bCanEverTick = true;
+ Tags.Add(FName("Trooper"));
+ AttackAbility = CreateDefaultSubobject("AttackAbility");
+ SpecialAbility = CreateDefaultSubobject("SpecialAbility");
+
+ HealthWidgetComponent = CreateDefaultSubobject(
+ "HealthBar");
+ HealthWidgetComponent->AttachToComponent(RootComponent,
+ FAttachmentTransformRules::KeepRelativeTransform);
+
+ SelectionStaticMesh = CreateDefaultSubobject(
+ "SelectionMesh");
+ static ConstructorHelpers::FObjectFinder MeshToUse(TEXT(
+ "StaticMesh'/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder'"));
+ SelectionStaticMesh->AttachToComponent(RootComponent,
+ FAttachmentTransformRules::KeepRelativeTransform);
+ SelectionStaticMesh->SetWorldScale3D({2.0f, 2.0f, 0.01f});
+ SelectionStaticMesh->SetVisibility(false);
+
+ if (MeshToUse.Object) {
+ SelectionStaticMesh->SetStaticMesh(MeshToUse.Object);
+ }
+ // SelectionStaticMesh->SetRelativeTransform(FTransform({1000,1000,100}, {0, 0, 0}), false,
+ // nullptr, ETeleportType::TeleportPhysics);
+ // SelectionStaticMesh->
+
+ // MyStaticMesh = CreateDefaultSubobject("Mesh");
+ // RootComponent = MyStaticMesh;
+ // MeshPath = TEXT("StaticMesh'/Game/StarterContent/Props/SM_Chair.SM_Chair'");
+ // static ConstructorHelpers::FObjectFinder MeshToUse(MeshPath);
+ // if (MeshToUse.Object) {
+ // MyStaticMesh->SetStaticMesh(MeshToUse.Object);
+ // }
+}
+
+// void ATrooper::SetStaticMesh() const {
+// static ConstructorHelpers::FObjectFinder MeshToUse(
+// TEXT(
+// "StaticMesh'/Game/CityofBrass_Enemies/Static/Wizard_StaticMesh.Wizard_StaticMesh'"
+// )
+// );
+// if (MeshToUse.Object) {
+// MyStaticMesh->SetStaticMesh(MeshToUse.Object);
+// }
+// }
+
+// Called when the game starts or when spawned
+void ATrooper::BeginPlay() {
+ Super::BeginPlay();
+ Cast(HealthWidgetComponent->GetUserWidgetObject())->
+ SetOwnerTrooper(this);
+}
+
+void ATrooper::Initialize(uint8 const NewPlayerIndex,
+ FVector const SpawnLocation,
+ uint8 const NewId) {
+ PlayerIndex = NewPlayerIndex;
+ bIsMoving = false;
+ AttackPlayedTime = 0.0f;
+ TakingDamagePlayedTime = 0.0f;
+ CurrentLocation = SpawnLocation;
+ Id = NewId;
+}
+
+void ATrooper::Tick(float const DeltaTime) {
+ if (bIsAttacking) {
+ AttackPlayedTime += DeltaTime;
+ if (bIsWaitingForFire && AttackPlayedTime >= FireAfterTime) {
+ FireProjectile();
+ CurrentAbilityIndex = -1;
+ CurrentAbilityDestination = {};
+ bIsWaitingForFire = false;
+ }
+ if (AttackPlayedTime >= AttackDuration) {
+ AttackPlayedTime = 0.0f;
+ // bIsAttacking = false;
+ SetIsAttacking(false);
+ }
+ }
+ if (bIsTakingDamage) {
+ TakingDamagePlayedTime += DeltaTime;
+ if (TakingDamagePlayedTime >= TakingDamageDuration) {
+ TakingDamagePlayedTime = 0.0f;
+ // bIsTakingDamage = false;
+ SetIsTakingDamage(false);
+ }
+ }
+ if (bIsMoving) {
+ FVector PositionVector = (TargetLocation - CurrentLocation);
+ PositionVector.Normalize();
+ PositionVector *= (Speed * DeltaTime);
+ if (PositionVector.Size() >= (TargetLocation - CurrentLocation).
+ Size()) {
+ CurrentLocation = TargetLocation;
+ // bIsMoving = false;
+ SetIsMoving(false);
+ } else {
+ CurrentLocation += PositionVector;
+ }
+ SetActorLocation(CurrentLocation);
+ }
+}
+
+void ATrooper::SetIsAttacking(bool IsAttacking) {
+ bIsAttacking = IsAttacking;
+ if (IsAttacking) {
+ SetActorTickEnabled(true);
+ } else {
+ TryDisableTick();
+ }
+}
+
+void ATrooper::SetIsTakingDamage(bool IsTakingDamage) {
+ bIsTakingDamage = IsTakingDamage;
+ if (IsTakingDamage) {
+ SetActorTickEnabled(true);
+ } else {
+ TryDisableTick();
+ }
+}
+
+void ATrooper::SetIsMoving(bool IsMoving) {
+ bIsMoving = IsMoving;
+ if (IsMoving) {
+ SetActorTickEnabled(true);
+ } else {
+ TryDisableTick();
+ }
+}
+
+void ATrooper::TryDisableTick() {
+ if (!bIsAttacking && !bIsTakingDamage && !bIsMoving) {
+ SetActorTickEnabled(false);
+ if (AIController && AIController->IsValidLowLevel()) {
+ AIController->ActionDone();
+ }
+ }
+}
+
+// void ATrooper::OnRepNotify_PlayerIndex() const {
+// const AMyPlayerState *player = Cast(
+// GetPlayerState());
+// if (!player)
+// return;
+// const uint8 ClientIndex = player->GetPlayerIndex();
+// UE_LOG(LogTemp, Warning,
+// TEXT("On rep notify, index: %d, client index: %d, id: %d"),
+// PlayerIndex,
+// ClientIndex, Id);
+// if (ClientIndex == PlayerIndex) {
+// HighlightAsEnemy();
+// }
+// }
+
+void ATrooper::MoveTrooper_Implementation(FVector const NewPos) {
+ TargetLocation = NewPos;
+ // bIsMoving = true;
+ SetIsMoving(true);
+ ActionPoints -= (NewPos - CurrentLocation).Size() * MoveCost;
+}
+
+uint8 ATrooper::GetId() const {
+ return Id;
+}
+
+void ATrooper::GetLifetimeReplicatedProps(
+ TArray &OutLifetimeProps) const {
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+ DOREPLIFETIME(ATrooper, PlayerIndex);
+ DOREPLIFETIME(ATrooper, CurrentLocation);
+ DOREPLIFETIME(ATrooper, TargetLocation);
+ DOREPLIFETIME(ATrooper, bIsMoving);
+ DOREPLIFETIME(ATrooper, Id);
+ DOREPLIFETIME(ATrooper, bIsAttacking);
+ DOREPLIFETIME(ATrooper, HitPoints);
+ DOREPLIFETIME(ATrooper, ActionPoints);
+ DOREPLIFETIME(ATrooper, HealthWidgetComponent);
+ DOREPLIFETIME(ATrooper, AttackPlayedTime);
+ DOREPLIFETIME(ATrooper, TakingDamagePlayedTime);
+ DOREPLIFETIME(ATrooper, bIsTakingDamage);
+ DOREPLIFETIME(ATrooper, bIsDead);
+ DOREPLIFETIME(ATrooper, CurrentAbilityIndex);
+ DOREPLIFETIME(ATrooper, CurrentAbilityDestination);
+}
+
+int8 ATrooper::GetPlayerIndex() const {
+ return PlayerIndex;
+}
+
+//
+// ATrooperWizard::ATrooperWizard() {
+// MeshPath = TEXT(
+// // "StaticMesh'/Game/CityofBrass_Enemies/Static/Wizard_StaticMesh.Wizard_StaticMesh'");
+// "StaticMesh'/Game/CityofBrass_Enemies/Static/SkeletonMelee_StaticMesh.SkeletonMelee_StaticMesh'");
+// SetStaticMesh();
+// }
+//
+// ATrooperSkeletonMelee::ATrooperSkeletonMelee() {
+// MeshPath = TEXT(
+// "StaticMesh'/Game/CityofBrass_Enemies/Static/SkeletonMelee_StaticMesh.SkeletonMelee_StaticMesh'");
+// SetStaticMesh();
+// }
+
+FVector ATrooper::GetLocation() const {
+ return CurrentLocation;
+}
+
+float ATrooper::GetActionRadius(int action) const {
+ switch (action) {
+ case 1:
+ return AttackAbility->ActionCost <= ActionPoints
+ ? AttackAbility->ActionRadius
+ : 0;
+ case 2:
+ return SpecialAbility->ActionCost <= ActionPoints
+ ? SpecialAbility->ActionRadius
+ : 0;
+ default:
+ return ActionPoints / MoveCost;
+ }
+}
+
+float ATrooper::GetRealActionRadius(int action) const {
+ switch (action) {
+ case 1:
+ return AttackAbility->ActionRadius;
+ case 2:
+ return SpecialAbility->ActionRadius;
+ default:
+ return ActionPoints / MoveCost;
+ }
+}
+
+float ATrooper::GetHitPoints() const {
+ return HitPoints;
+}
+
+float ATrooper::GetMaxHitPoints() const {
+ return StartHitPoints;
+}
+
+void ATrooper::SetSelection(bool Selection, uint8 ActionType) const {
+ if (SelectionStaticMesh) {
+ if (SelectionStaticMesh->GetMaterial(0) != GreenMaterial) {
+ SelectionStaticMesh->SetMaterial(0, GreenMaterial);
+ SelectionStaticMesh->SetRelativeLocation({0, 0, 1});
+ }
+ if (Selection) {
+ UpdateSelectionRadius(ActionType);
+ } else {
+ SelectionStaticMesh->SetWorldScale3D({2.f, 2.f, 0.01f});
+ }
+ SelectionStaticMesh->SetVisibility(Selection);
+ }
+}
+
+void ATrooper::UpdateSelectionRadius(uint8 ActionType) const {
+ const float radiusScale =
+ GetActionRadius(ActionType) / PIXELS_IN_RADIUS;
+ SelectionStaticMesh->SetWorldScale3D(
+ {radiusScale, radiusScale, 0.01f});
+}
+
+void ATrooper::HighlightAsEnemy_Implementation(int8 Index) const {
+ if (PlayerIndex != Index) {
+ SelectionStaticMesh->SetVisibility(true);
+ }
+}
+
+void ATrooper::ResetActionPoints() {
+ ActionPoints = StartActionPoints;
+}
+
+UAbility *ATrooper::GetAbility(int AbilityIndex) const {
+ switch (AbilityIndex) {
+ case 1:
+ return AttackAbility;
+ case 2:
+ return SpecialAbility;
+ default:
+ return nullptr;
+ }
+}
+
+void ATrooper::SetAIPossession(AEnemyAIController *EnemyController) {
+ AIController = EnemyController;
+}
+
+bool ATrooper::IsDead() const {
+ return bIsDead;
+}
+
+void ATrooper::TrooperTakeDamage_Implementation(float Damage) {
+ if (bIsTakingDamage || bIsDead) {
+ return;
+ }
+ HitPoints = FMath::Max(0, HitPoints - Damage);
+ if (HitPoints == 0) {
+ bIsDead = true;
+ SetLifeSpan(DyingAnimationDuration);
+ GetWorld()->GetGameState()->DecreaseLivingTroopers(
+ PlayerIndex);
+ } else {
+ // bIsTakingDamage = true;
+ SetIsTakingDamage(true);
+ }
+}
+
+TSubclassOf ATrooper::GetProjectileClass(
+ uint8 AbilityIndex) const {
+ switch (AbilityIndex) {
+ case 1:
+ return AttackProjectileClass;
+ case 2:
+ return SpecialProjectileClass;
+ default:
+ return AProjectile::StaticClass();
+ }
+}
+
+void ATrooper::FireProjectile_Implementation() {
+ if (!this || !this->IsValidLowLevel())
+ return;
+ FTransform SpawnTransform(
+ (CurrentAbilityDestination - CurrentLocation).Rotation());
+ const FVector TransformedVector = SpawnTransform.TransformVector(
+ {GetAbility(CurrentAbilityIndex)->LinearWidth / 2 + 50.0f, 0.0f,
+ 88.0f});
+ const FVector SpawnLocation = CurrentLocation + TransformedVector;
+ SpawnTransform.SetLocation(SpawnLocation);
+ FActorSpawnParameters SpawnParameters;
+ SpawnParameters.Owner = this;
+ SpawnParameters.Instigator = GetInstigator();
+ SpawnParameters.SpawnCollisionHandlingOverride =
+ ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
+ AProjectile *Projectile = GetWorld()->SpawnActor(
+ GetProjectileClass(CurrentAbilityIndex), SpawnTransform,
+ SpawnParameters);
+ Projectile->SetActorLocation(SpawnLocation);
+ Projectile->Initialize(GetAbility(CurrentAbilityIndex), PlayerIndex,
+ (CurrentAbilityDestination - SpawnLocation).
+ Size());
+ Projectile->Shoot(SpawnLocation, CurrentAbilityDestination);
+}
+
+int ATrooper::GetAnimationValue() {
+ if (bIsDead) {
+ return 4;
+ }
+ if (bIsTakingDamage) {
+ return 3;
+ }
+ if (bIsAttacking) {
+ return 2;
+ }
+ if (bIsMoving) {
+ return 1;
+ }
+ return 0;
+}
+
+void ATrooper::Attack_Implementation(int AbilityIndex, FVector ToLocation) {
+ // bIsAttacking = true;
+ SetIsAttacking(true);
+ bIsWaitingForFire = true;
+ ActionPoints -= GetAbility(AbilityIndex)->ActionCost;
+ CurrentAbilityIndex = AbilityIndex;
+ ToLocation.Z = 88.0f;
+ CurrentAbilityDestination = ToLocation;
+ // FTransform SpawnTransform((ToLocation - CurrentLocation).Rotation());
+ // SpawnTransform.SetLocation(
+ // CurrentLocation + SpawnTransform.TransformVector(
+ // {GetAbility(AbilityIndex)->LinearWidth/2 + 50.0f, 0.0f, 0.0f})
+ // );
+ // FActorSpawnParameters SpawnParameters;
+ // SpawnParameters.Owner = this;
+ // SpawnParameters.Instigator = GetInstigator();
+ // SpawnParameters.SpawnCollisionHandlingOverride =
+ // ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
+ // AMyProjectile *Projectile = GetWorld()->SpawnActor(
+ // GetProjectileClass(AbilityIndex), SpawnTransform, SpawnParameters);
+ // Projectile->Initialize(GetAbility(AbilityIndex));
+ // Projectile->Shoot(CurrentLocation, ToLocation);
+}
+
+bool ATrooper::CheckMoveCorrectness(const FVector newPos) const {
+ return (newPos - CurrentLocation).Size() * MoveCost <= ActionPoints;
+ // return (newPos - CurrentLocation).Size() <= MoveRadius;
+}
+
+bool ATrooper::CheckAttackCorrectness(const FVector attackLocation,
+ int abilityIndex) const {
+ return GetAbility(abilityIndex) != nullptr && (
+ attackLocation - CurrentLocation).Size() < GetActionRadius(
+ abilityIndex);
+ // return (attackLocation - CurrentLocation).Size() <= AttackRadius;
+}
diff --git a/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.h b/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.h
new file mode 100644
index 0000000..f3de3bc
--- /dev/null
+++ b/Source/TurnBasedTutorial/BattleMode/Trooper/Trooper.h
@@ -0,0 +1,222 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Ability.h"
+#include "../Singleplayer/EnemyAIController.h"
+#include "GameFramework/Character.h"
+#include "Trooper.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API ATrooper : public ACharacter {
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this actor's properties
+ ATrooper();
+
+ void Initialize(uint8 const NewPlayerIndex,
+ FVector const SpawnLocation,
+ uint8 const NewId);
+
+ UFUNCTION()
+ int8 GetPlayerIndex() const;
+
+ UFUNCTION(Server, Reliable)
+ void MoveTrooper(FVector const NewPos);
+
+ UFUNCTION()
+ uint8 GetId() const;
+
+ UFUNCTION()
+ bool CheckMoveCorrectness(const FVector newPos) const;
+
+ UFUNCTION()
+ bool CheckAttackCorrectness(const FVector attackLocation,
+ int abilityIndex) const;
+
+ UFUNCTION()
+ FVector GetLocation() const;
+
+ UFUNCTION(BlueprintCallable)
+ int GetAnimationValue();
+
+ UFUNCTION(Server, Reliable)
+ void Attack(int AbilityIndex, FVector ToLocation);
+
+ UFUNCTION()
+ float GetActionRadius(int action) const;
+
+ UFUNCTION()
+ float GetRealActionRadius(int action) const;
+
+ UFUNCTION()
+ float GetHitPoints() const;
+
+ UFUNCTION()
+ float GetMaxHitPoints() const;
+
+ UFUNCTION()
+ void SetSelection(bool Selected, uint8 ActionType) const;
+
+ UFUNCTION()
+ void UpdateSelectionRadius(uint8 ActionType) const;
+
+ UFUNCTION(Client, Reliable)
+ void HighlightAsEnemy(int8 Index) const;
+
+ UFUNCTION()
+ void ResetActionPoints();
+
+ UFUNCTION()
+ UAbility *GetAbility(int AbilityIndex) const;
+
+ UFUNCTION(Server, Reliable)
+ void TrooperTakeDamage(float Damage);
+
+ UFUNCTION()
+ void SetAIPossession(AEnemyAIController *EnemyController);
+
+ UFUNCTION()
+ bool IsDead() const;
+
+protected:
+ constexpr static float PIXELS_IN_RADIUS = 50;
+
+ UPROPERTY()
+ AEnemyAIController *AIController = nullptr;
+
+ UPROPERTY(EditAnywhere)
+ UMaterialInterface *GreenMaterial = nullptr;
+
+ UPROPERTY(EditAnywhere)
+ UMaterialInterface *RedMaterial = nullptr;
+
+ UPROPERTY(EditAnywhere)
+ UAbility *AttackAbility;
+
+ UPROPERTY(EditAnywhere)
+ UAbility *SpecialAbility;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ TSubclassOf AttackProjectileClass;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ TSubclassOf SpecialProjectileClass;
+
+ UFUNCTION()
+ TSubclassOf GetProjectileClass(uint8 AbilityIndex) const;
+
+ UFUNCTION(Server, Reliable)
+ void FireProjectile();
+
+ UPROPERTY(Replicated)
+ uint8 CurrentAbilityIndex = -1;
+
+ UPROPERTY(Replicated)
+ FVector CurrentAbilityDestination = {};
+
+ UPROPERTY(EditAnywhere)
+ float Speed = 300.0f;
+
+ UPROPERTY(EditAnywhere)
+ float StartHitPoints = 100.0f;
+
+ UPROPERTY(EditAnywhere)
+ float MoveCost = 0.0667f;
+
+ UPROPERTY(EditAnywhere)
+ float StartActionPoints = 100.0f;
+
+ UPROPERTY(Replicated)
+ float HitPoints;
+
+ UPROPERTY(Replicated)
+ float ActionPoints;
+
+ UPROPERTY(Replicated)
+ bool bIsAttacking = false;
+
+ UPROPERTY(Replicated)
+ float AttackPlayedTime;
+
+ const float AttackDuration = 1.16667f;
+
+ const float FireAfterTime = 0.6f;
+
+ UPROPERTY(Replicated)
+ bool bIsWaitingForFire = false;
+
+ UPROPERTY(Replicated)
+ bool bIsTakingDamage = false;
+
+ UPROPERTY(Replicated)
+ float TakingDamagePlayedTime;
+
+ const float TakingDamageDuration = 1.46667f;
+
+ const float DyingAnimationDuration = 2.83333f;
+
+ UPROPERTY(Replicated)
+ bool bIsDead = false;
+
+ virtual void BeginPlay() override;
+
+ virtual void Tick(float const DeltaTime) override;
+
+ UPROPERTY(VisibleAnywhere, Replicated)
+ class UWidgetComponent *HealthWidgetComponent;
+
+ // void SetStaticMesh() const;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
+ UStaticMeshComponent *SelectionStaticMesh;
+
+ // UPROPERTY(EditAnywhere, BlueprintReadWrite)
+ // USkeletalMeshComponent *MySkeletalMesh;
+
+ // const TCHAR *MeshPath = nullptr;
+
+ // UFUNCTION()
+ // void OnRepNotify_PlayerIndex() const;
+
+ UPROPERTY(Replicated/*Using = OnRepNotify_PlayerIndex*/)
+ int8 PlayerIndex = -1;
+
+ UPROPERTY(Replicated)
+ uint8 Id;
+
+ UPROPERTY(Replicated)
+ FVector CurrentLocation;
+
+ UPROPERTY(Replicated)
+ FVector TargetLocation;
+
+ UPROPERTY(Replicated)
+ bool bIsMoving = false;
+
+ void SetIsAttacking(bool IsAttacking);
+
+ void SetIsTakingDamage(bool IsTakingDamage);
+
+ void SetIsMoving(bool IsMoving);
+
+ void TryDisableTick();
+};
+
+// UCLASS()
+// class ATrooperWizard : public ATrooper {
+// GENERATED_BODY()
+//
+// public:
+// ATrooperWizard();
+//
+// };
+//
+// UCLASS()
+// class ATrooperSkeletonMelee : public ATrooper {
+// GENERATED_BODY()
+//
+// public:
+// ATrooperSkeletonMelee();
+//
+// };
diff --git a/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.cpp b/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.cpp
new file mode 100644
index 0000000..02ca458
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.cpp
@@ -0,0 +1,13 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "MainMenuPlayerController.h"
+#include "Blueprint/UserWidget.h"
+
+void AMainMenuPlayerController::BeginPlay() {
+ Super::BeginPlay();
+ SetShowMouseCursor(true);
+ UUserWidget *CreatedWidget = CreateWidget(
+ GetWorld(), WidgetClass);
+ CreatedWidget->AddToViewport();
+}
diff --git a/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.h b/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.h
new file mode 100644
index 0000000..8ad88e6
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/MainMenuPlayerController.h
@@ -0,0 +1,22 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/PlayerController.h"
+#include "MainMenuPlayerController.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API
+ AMainMenuPlayerController : public APlayerController {
+ GENERATED_BODY()
+
+ virtual void BeginPlay() override;
+
+protected:
+ UPROPERTY(EditAnywhere)
+ TSubclassOf WidgetClass;
+};
diff --git a/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.cpp b/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.cpp
new file mode 100644
index 0000000..0baf22f
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.cpp
@@ -0,0 +1,40 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "MainMenuWidget.h"
+#include "Components/Button.h"
+#include "Kismet/GameplayStatics.h"
+
+void UMainMenuWidget::NativeConstruct() {
+ Super::NativeConstruct();
+
+ HostOnlineGameButton->OnClicked.AddDynamic(
+ this, &ThisClass::UMainMenuWidget::OnHostOnlineGameButtonClicked);
+
+ GetMyGameSubsystem()->OnCreateSessionCompleteEvent.AddDynamic(
+ this, &ThisClass::StartSessionWhenCreatingSessonComplete);
+}
+
+void UMainMenuWidget::OnHostOnlineGameButtonClicked() {
+ // Ensure we have left any session
+ GetMyGameSubsystem()->DestroySession();
+
+ GetMyGameSubsystem()->CreateSession(
+ "Lobby " + FString::FromInt(FMath::RandRange(1, 1e6)), 2, true);
+}
+
+void UMainMenuWidget::StartSessionWhenCreatingSessonComplete(bool bSuccess) {
+ if (!bSuccess) {
+ return;
+ }
+ GetMyGameSubsystem()->StartSession();
+}
+
+
+USessionsGameInstanceSubsystem *UMainMenuWidget::GetMyGameSubsystem() const {
+ const UGameInstance *GameInstance = UGameplayStatics::GetGameInstance(
+ GetWorld());
+ USessionsGameInstanceSubsystem *GameInstanceSubsystem = GameInstance->
+ GetSubsystem();
+ return GameInstanceSubsystem;
+}
diff --git a/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.h b/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.h
new file mode 100644
index 0000000..992d040
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/MainMenuWidget.h
@@ -0,0 +1,33 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Blueprint/UserWidget.h"
+#include "../SessionsGameInstanceSubsystem.h"
+
+#include "MainMenuWidget.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API UMainMenuWidget : public UUserWidget {
+ GENERATED_BODY()
+
+public:
+ UPROPERTY(meta = (BindWidget))
+ class UButton *HostOnlineGameButton;
+
+ virtual void NativeConstruct() override;
+
+protected:
+ UFUNCTION()
+ void OnHostOnlineGameButtonClicked();
+
+ UFUNCTION()
+ void StartSessionWhenCreatingSessonComplete(bool bSuccess);
+
+private:
+ USessionsGameInstanceSubsystem *GetMyGameSubsystem() const;
+};
diff --git a/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.cpp b/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.cpp
new file mode 100644
index 0000000..849328b
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.cpp
@@ -0,0 +1,24 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "SessionListEntryWidget.h"
+#include "OnlineSessionSettings.h"
+#include "Components/TextBlock.h"
+
+void USessionListEntryWidget::Update(int SessionIndex,
+ const FOnlineSessionSearchResult &
+ Session) {
+ SessionId = SessionIndex;
+ IndexText->SetText(FText::AsNumber(SessionIndex + 1));
+
+ Session.Session.SessionSettings.Get(SETTING_MAPNAME, SessionName);
+ SessionNameText->SetText(FText::FromString(SessionName));
+
+ const int MaxPlayerCount = Session.Session.SessionSettings.
+ NumPublicConnections;
+ const int CurPlayerCount = MaxPlayerCount - Session.Session.
+ NumOpenPublicConnections;
+
+ PlayersCountText->SetText(FText::AsNumber(CurPlayerCount));
+ PingText->SetText(FText::AsNumber(Session.PingInMs));
+}
diff --git a/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.h b/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.h
new file mode 100644
index 0000000..8c74028
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/SessionListEntryWidget.h
@@ -0,0 +1,33 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Blueprint/UserWidget.h"
+#include "SessionListEntryWidget.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API USessionListEntryWidget : public UUserWidget {
+ GENERATED_BODY()
+
+public:
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(BindWidget))
+ class UTextBlock *IndexText;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(BindWidget))
+ class UTextBlock *SessionNameText;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(BindWidget))
+ class UTextBlock *PlayersCountText;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(BindWidget))
+ class UTextBlock *PingText;
+
+ void Update(int SessionIndex, const FOnlineSessionSearchResult &Session);
+
+ int SessionId;
+ FString SessionName;
+};
diff --git a/Source/TurnBasedTutorial/MainMenu/SessionListWidget.cpp b/Source/TurnBasedTutorial/MainMenu/SessionListWidget.cpp
new file mode 100644
index 0000000..af32814
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/SessionListWidget.cpp
@@ -0,0 +1,83 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "SessionListWidget.h"
+#include "../SessionsGameInstanceSubsystem.h"
+#include "Components/VerticalBox.h"
+#include "SessionListEntryWidget.h"
+#include "Components/Button.h"
+#include "Kismet/GameplayStatics.h"
+
+void USessionListWidget::NativeConstruct() {
+ Super::NativeConstruct();
+
+ RefreshListButton->OnClicked.AddDynamic(
+ this, &ThisClass::OnRefreshListButtonClicked);
+ ConnectToSelectedSessionButton->OnClicked.AddDynamic(
+ this, &ThisClass::ConnectToFirstSession);
+
+ const auto MyGameInstanceSubsystem = GetMyGameSubsystem();
+ MyGameInstanceSubsystem->OnFindSessionsCompleteEvent.AddUObject(
+ this, &ThisClass::RefreshList);
+ MyGameInstanceSubsystem->OnJoinSessionCompleteEvent.AddUObject(
+ this, &ThisClass::OnJoinSessionSuccess);
+
+ // Ensure we have left any session
+ GetMyGameSubsystem()->DestroySession();
+
+ // Initiate search
+ MyGameInstanceSubsystem->FindSessions(10, true);
+}
+
+void USessionListWidget::RefreshList(
+ const TArray &SessionResults,
+ bool bSuccessful) {
+ if (!bSuccessful) {
+ UE_LOG(LogTemp, Error, TEXT("Find sessions FAILED!!!!"));
+ // TODO: Mark find sessions error
+ return;
+ }
+ SessionListBox->ClearChildren();
+ for (const auto &Session : SessionResults) {
+ auto *ItemWidget = CreateWidget(
+ this, EntryClass);
+ ItemWidget->Update(SessionListBox->GetChildrenCount(), Session);
+ SessionListBox->AddChild(ItemWidget);
+ }
+}
+
+
+void USessionListWidget::OnRefreshListButtonClicked() {
+ // TODO: Show that we started searching...
+ // Initiate search
+ SessionListBox->ClearChildren();
+ GetMyGameSubsystem()->FindSessions(10, true);
+}
+
+void USessionListWidget::ConnectToFirstSession() {
+ GetMyGameSubsystem()->JoinSession(0);
+}
+
+
+void USessionListWidget::OnJoinSessionSuccess(
+ EOnJoinSessionCompleteResult::Type Result) {
+ if (Result != EOnJoinSessionCompleteResult::Success) {
+ UE_LOG(LogTemp, Error, TEXT("Failed to connect to session!!"));
+ return;
+ }
+ if (!GetMyGameSubsystem()->TryConnectToCurrentSession()) {
+ UE_LOG(LogTemp, Error, TEXT("Failed to travel client to session!!"));
+ return;
+ }
+ UE_LOG(LogTemp, Display, TEXT("Connected and travelled to session!!!"));
+}
+
+
+USessionsGameInstanceSubsystem *
+USessionListWidget::GetMyGameSubsystem() const {
+ const UGameInstance *GameInstance = UGameplayStatics::GetGameInstance(
+ GetWorld());
+ USessionsGameInstanceSubsystem *GameInstanceSubsystem = GameInstance->
+ GetSubsystem();
+ return GameInstanceSubsystem;
+}
diff --git a/Source/TurnBasedTutorial/MainMenu/SessionListWidget.h b/Source/TurnBasedTutorial/MainMenu/SessionListWidget.h
new file mode 100644
index 0000000..e0b43ef
--- /dev/null
+++ b/Source/TurnBasedTutorial/MainMenu/SessionListWidget.h
@@ -0,0 +1,48 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "../SessionsGameInstanceSubsystem.h"
+#include "Blueprint/UserWidget.h"
+#include "SessionListWidget.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API USessionListWidget : public UUserWidget {
+ GENERATED_BODY()
+
+protected:
+ virtual void NativeConstruct() override;
+
+ UPROPERTY(meta = (BindWidget))
+ class UButton *ConnectToSelectedSessionButton;
+
+ UPROPERTY(meta = (BindWidget))
+ class UButton *GoBackToMainMenuButton;
+
+ UPROPERTY(meta = (BindWidget))
+ class UButton *RefreshListButton;
+
+ UPROPERTY(meta = (BindWidget))
+ class UVerticalBox *SessionListBox;
+
+ UPROPERTY(EditDefaultsOnly, Category="Session Info Class")
+ TSubclassOf EntryClass;
+
+ void RefreshList(const TArray &SessionResults,
+ bool bSuccessful);
+
+ void OnJoinSessionSuccess(EOnJoinSessionCompleteResult::Type Result);
+
+private:
+ USessionsGameInstanceSubsystem *GetMyGameSubsystem() const;
+
+ UFUNCTION()
+ void OnRefreshListButtonClicked();
+
+ UFUNCTION()
+ void ConnectToFirstSession();
+};
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.cpp b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.cpp
new file mode 100644
index 0000000..529481b
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.cpp
@@ -0,0 +1,11 @@
+#include "ManageSquadGameMode.h"
+
+#include "ManageSquadGameState.h"
+#include "ManageSquadPawn.h"
+#include "ManageSquadPlayerController.h"
+
+AManageSquadGameMode::AManageSquadGameMode() {
+ PlayerControllerClass = AManageSquadPlayerController::StaticClass();
+ DefaultPawnClass = AManageSquadPawn::StaticClass();
+ GameStateClass = AManageSquadGameState::StaticClass();
+}
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.h b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.h
new file mode 100644
index 0000000..7abdf6f
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameMode.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/GameMode.h"
+#include "ManageSquadGameMode.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API AManageSquadGameMode : public AGameMode {
+ GENERATED_BODY()
+
+public:
+ AManageSquadGameMode();
+};
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.cpp b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.cpp
new file mode 100644
index 0000000..da48053
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.cpp
@@ -0,0 +1,11 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "ManageSquadGameState.h"
+
+AManageSquadGameState::AManageSquadGameState() {
+ TroopersKinds = {0, 0, 0, 0, 0};
+}
+
+void AManageSquadGameState::ChangeSquad(int TrooperIndex, int TrooperKind) {
+ TroopersKinds[TrooperIndex] = TrooperKind;
+}
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.h b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.h
new file mode 100644
index 0000000..1198780
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadGameState.h
@@ -0,0 +1,25 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/GameState.h"
+#include "ManageSquadGameState.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API AManageSquadGameState : public AGameState {
+ GENERATED_BODY()
+
+public:
+ AManageSquadGameState();
+
+ UFUNCTION()
+ void ChangeSquad(int TrooperIndex, int TrooperKind);
+
+private:
+ UPROPERTY()
+ TArray TroopersKinds;
+};
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.cpp b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.cpp
new file mode 100644
index 0000000..1fafe54
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.cpp
@@ -0,0 +1,19 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "ManageSquadPawn.h"
+
+// Called when the game starts or when spawned
+void AManageSquadPawn::BeginPlay() {
+ Super::BeginPlay();
+}
+
+// Called every frame
+void AManageSquadPawn::Tick(float DeltaTime) {
+ Super::Tick(DeltaTime);
+}
+
+// Called to bind functionality to input
+void AManageSquadPawn::SetupPlayerInputComponent(
+ UInputComponent *PlayerInputComponent) {
+ // Super::SetupPlayerInputComponent(PlayerInputComponent);
+}
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.h b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.h
new file mode 100644
index 0000000..dace062
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPawn.h
@@ -0,0 +1,24 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/DefaultPawn.h"
+#include "ManageSquadPawn.generated.h"
+
+UCLASS()
+class TURNBASEDTUTORIAL_API AManageSquadPawn : public ADefaultPawn {
+ GENERATED_BODY()
+
+protected:
+ // Called when the game starts or when spawned
+ virtual void BeginPlay() override;
+
+public:
+ // Called every frame
+ virtual void Tick(float DeltaTime) override;
+
+ // Called to bind functionality to input
+ virtual void SetupPlayerInputComponent(
+ class UInputComponent *PlayerInputComponent) override;
+};
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.cpp b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.cpp
new file mode 100644
index 0000000..72d4c77
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.cpp
@@ -0,0 +1,63 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "ManageSquadPlayerController.h"
+
+#include "ManageSquadGameState.h"
+
+AManageSquadPlayerController::AManageSquadPlayerController() {
+ SetShowMouseCursor(true);
+}
+
+void AManageSquadPlayerController::SetupInputComponent() {
+ Super::SetupInputComponent();
+ InputComponent->BindAction("MyAction", IE_Pressed, this,
+ &AManageSquadPlayerController::OnLeftMouseClick);
+}
+
+void AManageSquadPlayerController::OnLeftMouseClick() {
+ UE_LOG(LogTemp, Warning, TEXT("Mouse clicked"));
+ FHitResult HitResult;
+ bool const IsHitResult = GetHitResultUnderCursorForObjects(
+ TArray>{ObjectTypeQuery1}, false,
+ HitResult);
+ if (!IsHitResult)
+ return;
+
+ UE_LOG(LogTemp, Warning, TEXT("Got hit result"));
+ // auto const NewlySelectedLocation = HitResult.Location;
+ AManageSquadTrooper *NewlySelectedTrooper = Cast
+ (
+ HitResult.GetActor());
+
+ if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper->
+ IsValidLowLevel()) {
+ // we selected something that is not a trooper (or trooper in shitty state...)
+ UE_LOG(LogTemp, Warning, TEXT("Not a trooper"));
+ return;
+ }
+ // skip re-selection
+ if (SelectedTrooper == NewlySelectedTrooper) {
+ UE_LOG(LogTemp, Warning, TEXT("Skip reselection"));
+ return;
+ }
+ UE_LOG(LogTemp, Warning, TEXT("Trooper"));
+ switch (NewlySelectedTrooper->GetType()) {
+ case ETrooperType::TROOPER_SAMPLE:
+ if (SelectedTrooper) {
+ UE_LOG(LogTemp, Warning, TEXT("Trooper replacement"));
+ SelectedTrooper->ChangeSkeletalMesh(NewlySelectedTrooper);
+ Cast(
+ GetWorld()->GetGameState())->ChangeSquad(
+ SelectedTrooper->GetIndex(),
+ NewlySelectedTrooper->GetIndex()
+ );
+ }
+ break;
+ case ETrooperType::TROOPER_IN_SQUAD:
+ SelectedTrooper = NewlySelectedTrooper;
+ UE_LOG(LogTemp, Warning, TEXT("Changed selection"));
+ break;
+ default:
+ break;
+ }
+}
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.h b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.h
new file mode 100644
index 0000000..12749f0
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadPlayerController.h
@@ -0,0 +1,29 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/PlayerController.h"
+#include "ManageSquadTrooper.h"
+#include "ManageSquadPlayerController.generated.h"
+
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API AManageSquadPlayerController
+ : public APlayerController {
+ GENERATED_BODY()
+
+public:
+ AManageSquadPlayerController();
+
+ virtual void SetupInputComponent() override;
+
+private:
+ UPROPERTY()
+ AManageSquadTrooper *SelectedTrooper;
+
+ UFUNCTION()
+ void OnLeftMouseClick();
+};
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.cpp b/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.cpp
new file mode 100644
index 0000000..3e140f4
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.cpp
@@ -0,0 +1,31 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "ManageSquadTrooper.h"
+
+AManageSquadTrooper::AManageSquadTrooper() {
+ GetMesh()->SetCollisionResponseToAllChannels(
+ ECollisionResponse::ECR_Overlap);
+}
+
+// Called when the game starts or when spawned
+void AManageSquadTrooper::BeginPlay() {
+ Super::BeginPlay();
+}
+
+// Called every frame
+void AManageSquadTrooper::Tick(float DeltaTime) {
+ Super::Tick(DeltaTime);
+}
+
+void AManageSquadTrooper::
+ChangeSkeletalMesh(const AManageSquadTrooper *OtherTrooper) const {
+ GetMesh()->SetSkeletalMesh(OtherTrooper->GetMesh()->SkeletalMesh);
+}
+
+ETrooperType AManageSquadTrooper::GetType() const {
+ return Type;
+}
+
+int AManageSquadTrooper::GetIndex() const {
+ return Index;
+}
diff --git a/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.h b/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.h
new file mode 100644
index 0000000..2da243b
--- /dev/null
+++ b/Source/TurnBasedTutorial/ManageSquad/ManageSquadTrooper.h
@@ -0,0 +1,43 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/Character.h"
+#include "ManageSquadTrooper.generated.h"
+
+UENUM()
+enum ETrooperType {
+ TROOPER_SAMPLE,
+ TROOPER_IN_SQUAD
+};
+
+UCLASS()
+class TURNBASEDTUTORIAL_API AManageSquadTrooper : public ACharacter {
+ GENERATED_BODY()
+
+ AManageSquadTrooper();
+
+protected:
+ // Called when the game starts or when spawned
+ virtual void BeginPlay() override;
+
+ UPROPERTY(EditAnywhere)
+ int Index = -1;
+
+ UPROPERTY(EditAnywhere)
+ TEnumAsByte Type;
+
+public:
+ // Called every frame
+ virtual void Tick(float DeltaTime) override;
+
+ UFUNCTION()
+ void ChangeSkeletalMesh(const AManageSquadTrooper *OtherTrooper) const;
+
+ UFUNCTION()
+ ETrooperType GetType() const;
+
+ UFUNCTION()
+ int GetIndex() const;
+};
diff --git a/Source/TurnBasedTutorial/MyGameMode.cpp b/Source/TurnBasedTutorial/MyGameMode.cpp
deleted file mode 100644
index d09b49d..0000000
--- a/Source/TurnBasedTutorial/MyGameMode.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#include "MyGameMode.h"
-#include "Kismet/GameplayStatics.h"
-#include "Trooper.h"
-
-AMyGameMode::AMyGameMode() : Super() {
- UE_LOG(LogTemp, Warning, TEXT("GameMode Constructor"));
- PlayerControllerClass = AMyPlayerController::StaticClass();
-}
-
-void AMyGameMode::BeginPlay() {
- Super::BeginPlay();
- ATrooper::InitNumberOfTroopersForId();
- UE_LOG(LogTemp, Warning, TEXT("GameMode BeginPlay"));
- if (GetWorld()->GetMapName().Contains("BattleFieldMap")) {
- UE_LOG(LogTemp, Warning, TEXT("Player Logined"));
- StartGame();
- }
-}
-
-void AMyGameMode::StartGame() {
- FVector Location(2000.0f, -1000.0f, 0.0f);
- FRotator Rotation(0.0f, 180.0f, 0.0f);
- FActorSpawnParameters SpawnInfo;
- for (int i = 0; i < 5; ++i) {
- AActor *spawned = GetWorld()->SpawnActor(Location, Rotation, SpawnInfo);
- dynamic_cast(spawned)->InitTrooper(Location, true);
- Location += { 0.f, 500.f, 0.0f };
- }
- Location = { -2000.0f, -1000.0f, 0.0f };
- Rotation = { 0.0f, 0.0f, 0.0f };
- for (int i = 0; i < 5; ++i) {
- AActor* spawned = GetWorld()->SpawnActor(Location, Rotation, SpawnInfo);
- dynamic_cast(spawned)->InitTrooper(Location, false);
- Location += { 0.f, 500.f, 0.0f };
- }
- GetPlayerController()->StartTurn();
-}
-
-AMyPlayerController *AMyGameMode::GetPlayerController() {
- return dynamic_cast(
- UGameplayStatics::GetPlayerController(GetWorld(), 0)
- );
-}
diff --git a/Source/TurnBasedTutorial/MyGameMode.h b/Source/TurnBasedTutorial/MyGameMode.h
deleted file mode 100644
index f7cccbe..0000000
--- a/Source/TurnBasedTutorial/MyGameMode.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "MyPlayerController.h"
-#include "GameFramework/GameMode.h"
-#include "MyGameMode.generated.h"
-
-
-
-UCLASS()
-class TURNBASEDTUTORIAL_API AMyGameMode : public AGameMode
-{
- GENERATED_BODY()
-
-public:
- AMyGameMode();
-
- void BeginPlay() override;
-
-private:
- void StartGame();
-
- AMyPlayerController *GetPlayerController();
-};
diff --git a/Source/TurnBasedTutorial/MyPlayerController.cpp b/Source/TurnBasedTutorial/MyPlayerController.cpp
deleted file mode 100644
index f069d84..0000000
--- a/Source/TurnBasedTutorial/MyPlayerController.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#include "MyPlayerController.h"
-
-AMyPlayerController::AMyPlayerController() : Super(), IsMyTurn(false), SelectedTrooper(nullptr) {
- UE_LOG(LogTemp, Warning, TEXT("Player controller created"));
-}
-
-void AMyPlayerController::SetupInputComponent() {
- Super::SetupInputComponent();
- InputComponent->BindAction("MyAction", IE_Pressed, this,
- &AMyPlayerController::OnLeftMouseClick);
-}
-
-void AMyPlayerController::StartTurn() {
- IsMyTurn = true;
- UE_LOG(LogTemp, Warning, TEXT("Your turn"));
-}
-
-void AMyPlayerController::EndTurn() {
- IsMyTurn = false;
- UE_LOG(LogTemp, Warning, TEXT("Not your turn"));
-}
-
-void AMyPlayerController::SetTrooperIsMoving(bool isMoving) {
- IsThereTrooperMoving = isMoving;
-}
-
-void AMyPlayerController::OnLeftMouseClick() {
- if (IsThereTrooperMoving) {
- return;
- }
- UE_LOG(LogTemp, Warning, TEXT("Mouse clicked"));
- FHitResult HitResult;
- bool IsHitResult = GetHitResultUnderCursorByChannel(TraceTypeQuery1, false, HitResult);
- if (IsHitResult) {
- AActor *actor = HitResult.Actor.Get();
- if (actor->ActorHasTag(FName("Trooper"))) {
- ATrooper* trooper = dynamic_cast(actor);
- if (trooper != nullptr && trooper != SelectedTrooper) {
- if (trooper->IsOnPlayersSide()) {
- UE_LOG(LogTemp, Warning, TEXT("Hitted trooper id: %d, on our side"),
- trooper->GetId());
- SelectedTrooper = trooper;
- }
- else {
- UE_LOG(LogTemp, Warning, TEXT("Hitted trooper id: %d, enemy"),
- trooper->GetId());
- UE_LOG(LogTemp, Warning, TEXT("Trooper #%d hit enemy trooper #%d"),
- SelectedTrooper->GetId(), trooper->GetId());
- }
- }
- }
- else if (actor->ActorHasTag(FName("Floor"))) {
- UE_LOG(LogTemp, Warning, TEXT("Hitted floor: %f, %f, %f"), HitResult.Location.X,
- HitResult.Location.Y, HitResult.Location.Z);
- if (SelectedTrooper != nullptr) {
- SelectedTrooper->MoveTrooper(HitResult.Location);
- IsThereTrooperMoving = true;
- }
- }
- }
-}
diff --git a/Source/TurnBasedTutorial/MyPlayerController.h b/Source/TurnBasedTutorial/MyPlayerController.h
deleted file mode 100644
index 90fb98a..0000000
--- a/Source/TurnBasedTutorial/MyPlayerController.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-#pragma once
-
-#include "CoreMinimal.h"
-#include "Trooper.h"
-#include "GameFramework/PlayerController.h"
-#include "MyPlayerController.generated.h"
-
-/**
- *
- */
-UCLASS()
-class TURNBASEDTUTORIAL_API AMyPlayerController : public APlayerController
-{
- GENERATED_BODY()
-
-public:
- AMyPlayerController();
-
- void StartTurn();
-
- void EndTurn();
-
- virtual void SetupInputComponent() override;
-
- void SetTrooperIsMoving(bool isMoving);
-
-private:
- bool IsMyTurn;
-
- bool IsThereTrooperMoving = false;
-
- ATrooper* SelectedTrooper;
-
- void OnLeftMouseClick();
-
-};
diff --git a/Source/TurnBasedTutorial/MyPlayerState.cpp b/Source/TurnBasedTutorial/MyPlayerState.cpp
deleted file mode 100644
index 00bda13..0000000
--- a/Source/TurnBasedTutorial/MyPlayerState.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-
-#include "MyPlayerState.h"
-
diff --git a/Source/TurnBasedTutorial/MyPlayerState.h b/Source/TurnBasedTutorial/MyPlayerState.h
deleted file mode 100644
index 991a5cb..0000000
--- a/Source/TurnBasedTutorial/MyPlayerState.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "GameFramework/PlayerState.h"
-#include "MyPlayerState.generated.h"
-
-/**
- *
- */
-UCLASS()
-class TURNBASEDTUTORIAL_API AMyPlayerState : public APlayerState
-{
- GENERATED_BODY()
-
-};
diff --git a/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.cpp b/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.cpp
new file mode 100644
index 0000000..8f1de33
--- /dev/null
+++ b/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.cpp
@@ -0,0 +1,399 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "SessionsGameInstanceSubsystem.h"
+
+#include "BattleMode/BattlePlayerController.h"
+#include "OnlineSubsystemUtils.h"
+#include "GameFramework/GameModeBase.h"
+#include "Kismet/GameplayStatics.h"
+
+USessionsGameInstanceSubsystem::USessionsGameInstanceSubsystem()
+ : bIsHost(false),
+ CreateSessionCompleteDelegate(
+ FOnCreateSessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnCreateSessionCompleted)),
+ UpdateSessionCompleteDelegate(
+ FOnUpdateSessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnUpdateSessionCompleted)),
+ StartSessionCompleteDelegate(
+ FOnStartSessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnStartSessionCompleted)),
+ EndSessionCompleteDelegate(
+ FOnEndSessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnEndSessionCompleted)),
+ DestroySessionCompleteDelegate(
+ FOnDestroySessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnDestroySessionCompleted)),
+ FindSessionsCompleteDelegate(
+ FOnFindSessionsCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnFindSessionsCompleted)),
+ JoinSessionCompleteDelegate(
+ FOnJoinSessionCompleteDelegate::CreateUObject(
+ this, &ThisClass::OnJoinSessionCompleted)) {
+}
+
+
+void USessionsGameInstanceSubsystem::CreateSession(FString SessionName,
+ int32 NumPublicConnections,
+ bool bIsLANMatch) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnCreateSessionCompleteEvent.Broadcast(false);
+ return;
+ }
+
+ LastSessionSettings = MakeShareable(new FOnlineSessionSettings());
+ LastSessionSettings->NumPrivateConnections = 0;
+ LastSessionSettings->NumPublicConnections = NumPublicConnections;
+ LastSessionSettings->bAllowInvites = true;
+ LastSessionSettings->bAllowJoinInProgress = true;
+ LastSessionSettings->bAllowJoinViaPresence = true;
+ LastSessionSettings->bAllowJoinViaPresenceFriendsOnly = true;
+ LastSessionSettings->bUsesPresence = true;
+ LastSessionSettings->bIsLANMatch = bIsLANMatch;
+ LastSessionSettings->bShouldAdvertise = true;
+
+ LastSessionSettings->Set(SETTING_MAPNAME, SessionName,
+ EOnlineDataAdvertisementType::ViaOnlineService);
+
+ CreateSessionCompleteDelegateHandle = SessionInterface->
+ AddOnCreateSessionCompleteDelegate_Handle(
+ CreateSessionCompleteDelegate);
+
+ const ULocalPlayer *localPlayer = GetWorld()->
+ GetFirstLocalPlayerFromController();
+ if (!SessionInterface->CreateSession(
+ *localPlayer->GetPreferredUniqueNetId(), NAME_GameSession,
+ *LastSessionSettings)) {
+ SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(
+ CreateSessionCompleteDelegateHandle);
+
+ OnCreateSessionCompleteEvent.Broadcast(false);
+ }
+}
+
+void USessionsGameInstanceSubsystem::OnCreateSessionCompleted(
+ FName SessionName,
+ bool bSuccessful) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(
+ CreateSessionCompleteDelegateHandle);
+ }
+
+ bIsHost = true;
+
+ OnCreateSessionCompleteEvent.Broadcast(bSuccessful);
+}
+
+void USessionsGameInstanceSubsystem::UpdateSession() {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnCreateSessionCompleteEvent.Broadcast(false);
+ return;
+ }
+
+ const TSharedPtr UpdatedSessionSettings =
+ MakeShareable(
+ new FOnlineSessionSettings(*LastSessionSettings));
+
+ // Here we can insert any changes we want
+ UpdatedSessionSettings->Set(SETTING_MAPNAME, FString("Updated Level Name"),
+ EOnlineDataAdvertisementType::ViaOnlineService);
+
+ UpdateSessionCompleteDelegateHandle =
+ SessionInterface->AddOnUpdateSessionCompleteDelegate_Handle(
+ UpdateSessionCompleteDelegate);
+
+ if (!SessionInterface->UpdateSession(NAME_GameSession,
+ *UpdatedSessionSettings)) {
+ SessionInterface->ClearOnUpdateSessionCompleteDelegate_Handle(
+ UpdateSessionCompleteDelegateHandle);
+
+ OnUpdateSessionCompleteEvent.Broadcast(false);
+ } else {
+ LastSessionSettings = UpdatedSessionSettings;
+ }
+}
+
+void USessionsGameInstanceSubsystem::OnUpdateSessionCompleted(
+ FName SessionName,
+ bool bSuccessful) {
+ const IOnlineSessionPtr sessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (sessionInterface.IsValid()) {
+ sessionInterface->ClearOnUpdateSessionCompleteDelegate_Handle(
+ UpdateSessionCompleteDelegateHandle);
+ }
+
+ OnUpdateSessionCompleteEvent.Broadcast(bSuccessful);
+}
+
+
+void USessionsGameInstanceSubsystem::StartSession() {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnCreateSessionCompleteEvent.Broadcast(false);
+ return;
+ }
+ StartSessionCompleteDelegateHandle = SessionInterface->
+ AddOnStartSessionCompleteDelegate_Handle(
+ StartSessionCompleteDelegate);
+
+ if (!SessionInterface->StartSession(NAME_GameSession)) {
+ SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(
+ StartSessionCompleteDelegateHandle);
+
+ OnStartSessionCompleteEvent.Broadcast(false);
+ }
+}
+
+
+void USessionsGameInstanceSubsystem::OnStartSessionCompleted(
+ FName SessionName,
+ bool bSuccessful) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(
+ StartSessionCompleteDelegateHandle);
+ }
+ OnStartSessionCompleteEvent.Broadcast(bSuccessful);
+
+ // TODO: Move this from gameinstance subsystem. This should not be here.
+ UGameplayStatics::OpenLevel(GetWorld(), "BattleFieldMap", true, "listen");
+}
+
+
+void USessionsGameInstanceSubsystem::EndSession() {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnCreateSessionCompleteEvent.Broadcast(false);
+ return;
+ }
+
+ EndSessionCompleteDelegateHandle = SessionInterface->
+ AddOnEndSessionCompleteDelegate_Handle(
+ EndSessionCompleteDelegate);
+
+ if (!SessionInterface->EndSession(NAME_GameSession)) {
+ SessionInterface->ClearOnEndSessionCompleteDelegate_Handle(
+ EndSessionCompleteDelegateHandle);
+
+ OnEndSessionCompleteEvent.Broadcast(false);
+ }
+}
+
+
+void USessionsGameInstanceSubsystem::OnEndSessionCompleted(
+ FName SessionName,
+ bool bSuccessful) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnEndSessionCompleteDelegate_Handle(
+ EndSessionCompleteDelegateHandle);
+ }
+
+ OnEndSessionCompleteEvent.Broadcast(bSuccessful);
+}
+
+
+void USessionsGameInstanceSubsystem::DestroySession() {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnDestroySessionCompleteEvent.Broadcast(false);
+ return;
+ }
+
+ DestroySessionCompleteDelegateHandle = SessionInterface->
+ AddOnDestroySessionCompleteDelegate_Handle(
+ DestroySessionCompleteDelegate);
+
+ if (!SessionInterface->DestroySession(NAME_GameSession)) {
+ SessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(
+ EndSessionCompleteDelegateHandle);
+
+ OnDestroySessionCompleteEvent.Broadcast(false);
+ }
+}
+
+
+void USessionsGameInstanceSubsystem::OnDestroySessionCompleted(
+ FName SessionName,
+ bool bSuccessful) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(
+ DestroySessionCompleteDelegateHandle);
+ }
+
+ OnDestroySessionCompleteEvent.Broadcast(bSuccessful);
+}
+
+
+void USessionsGameInstanceSubsystem::FindSessions(int32 MaxSearchResults,
+ bool bIsLANQuery) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnFindSessionsCompleteEvent.Broadcast(
+ TArray(), false);
+ return;
+ }
+
+ FindSessionsCompleteDelegateHandle = SessionInterface->
+ AddOnFindSessionsCompleteDelegate_Handle(
+ FindSessionsCompleteDelegate);
+
+ LastSessionSearch = MakeShareable(new FOnlineSessionSearch());
+ LastSessionSearch->MaxSearchResults = MaxSearchResults;
+ LastSessionSearch->bIsLanQuery = bIsLANQuery;
+
+ // Disable dedicated server search (maybe enable later, when dedicated server is implemented)
+ // LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
+
+ const ULocalPlayer *LocalPlayer = GetWorld()->
+ GetFirstLocalPlayerFromController();
+ if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(),
+ LastSessionSearch.ToSharedRef())) {
+ SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(
+ FindSessionsCompleteDelegateHandle);
+
+ OnFindSessionsCompleteEvent.Broadcast(
+ TArray(), false);
+ }
+}
+
+
+void USessionsGameInstanceSubsystem::OnFindSessionsCompleted(bool bSuccessful) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(
+ FindSessionsCompleteDelegateHandle);
+ }
+
+ if (LastSessionSearch->SearchResults.Num() <= 0) {
+ OnFindSessionsCompleteEvent.Broadcast(
+ TArray(), bSuccessful);
+ return;
+ }
+
+ OnFindSessionsCompleteEvent.Broadcast(LastSessionSearch->SearchResults,
+ bSuccessful);
+}
+
+
+void USessionsGameInstanceSubsystem::JoinSession(
+ const FOnlineSessionSearchResult &SessionSearchResult) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnJoinSessionCompleteEvent.Broadcast(
+ EOnJoinSessionCompleteResult::UnknownError);
+ return;
+ }
+
+ JoinSessionCompleteDelegateHandle = SessionInterface->
+ AddOnJoinSessionCompleteDelegate_Handle(
+ JoinSessionCompleteDelegate);
+
+ const ULocalPlayer *LocalPlayer = GetWorld()->
+ GetFirstLocalPlayerFromController();
+ if (!SessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(),
+ NAME_GameSession, SessionSearchResult)) {
+ SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(
+ JoinSessionCompleteDelegateHandle);
+
+ OnJoinSessionCompleteEvent.Broadcast(
+ EOnJoinSessionCompleteResult::UnknownError);
+ }
+}
+
+void USessionsGameInstanceSubsystem::JoinSession(const int32 Index) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ OnJoinSessionCompleteEvent.Broadcast(
+ EOnJoinSessionCompleteResult::UnknownError);
+ return;
+ }
+ if (!LastSessionSearch.IsValid() || Index >= LastSessionSearch->
+ SearchResults.Num()) {
+ OnJoinSessionCompleteEvent.Broadcast(
+ EOnJoinSessionCompleteResult::UnknownError);
+ return;
+ }
+
+ JoinSessionCompleteDelegateHandle = SessionInterface->
+ AddOnJoinSessionCompleteDelegate_Handle(
+ JoinSessionCompleteDelegate);
+
+ const ULocalPlayer *LocalPlayer = GetWorld()->
+ GetFirstLocalPlayerFromController();
+ if (!SessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(),
+ NAME_GameSession,
+ LastSessionSearch->SearchResults[
+ Index])) {
+ SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(
+ JoinSessionCompleteDelegateHandle);
+
+ OnJoinSessionCompleteEvent.Broadcast(
+ EOnJoinSessionCompleteResult::UnknownError);
+ }
+}
+
+
+void USessionsGameInstanceSubsystem::OnJoinSessionCompleted(
+ FName SessionName,
+ EOnJoinSessionCompleteResult::Type Result) {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (SessionInterface.IsValid()) {
+ SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(
+ JoinSessionCompleteDelegateHandle);
+ }
+
+ OnJoinSessionCompleteEvent.Broadcast(Result);
+}
+
+
+bool USessionsGameInstanceSubsystem::TryConnectToCurrentSession() const {
+ const IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(
+ GetWorld());
+ if (!SessionInterface.IsValid()) {
+ return false;
+ }
+
+ FString ConnectString;
+ if (!SessionInterface->GetResolvedConnectString(
+ NAME_GameSession, ConnectString)) {
+ return false;
+ }
+
+ APlayerController *PlayerController = GetWorld()->
+ GetFirstPlayerController();
+ PlayerController->ClientTravel(ConnectString, TRAVEL_Absolute);
+ return true;
+}
+
+void USessionsGameInstanceSubsystem::QuitCurrentSession() {
+ if (bIsHost) {
+ UGameplayStatics::GetGameMode(GetWorld())->ReturnToMainMenuHost();
+ } else {
+ APlayerController *LocalController = GEngine->
+ GetFirstLocalPlayerController(GetWorld());
+ LocalController->ClientReturnToMainMenuWithTextReason(
+ FText::FromString("Session ended"));
+ }
+ bIsHost = false;
+ DestroySession();
+}
diff --git a/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.h b/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.h
new file mode 100644
index 0000000..fd7d84d
--- /dev/null
+++ b/Source/TurnBasedTutorial/SessionsGameInstanceSubsystem.h
@@ -0,0 +1,116 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "OnlineSessionSettings.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "Subsystems/GameInstanceSubsystem.h"
+#include "SessionsGameInstanceSubsystem.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyOnCreateSessionComplete, bool,
+ bSuccessful);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyOnUpdateSessionComplete, bool,
+ bSuccessful);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyOnStartSessionCompete, bool,
+ bSuccessful);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyOnEndSessionComplete, bool,
+ bSuccessful);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyOnDestroySessionComplete, bool,
+ bSuccessful);
+
+DECLARE_MULTICAST_DELEGATE_TwoParams(FMyOnFindSessionsComplete,
+ const TArray&
+ SessionResults, bool bSuccessful);
+
+DECLARE_MULTICAST_DELEGATE_OneParam(FMyOnJoinSessionCompele,
+ EOnJoinSessionCompleteResult::Type Result);
+/**
+ *
+ */
+UCLASS()
+class TURNBASEDTUTORIAL_API USessionsGameInstanceSubsystem
+ : public UGameInstanceSubsystem {
+ GENERATED_BODY()
+
+public:
+ USessionsGameInstanceSubsystem();
+
+ void CreateSession(FString SessionName,
+ int32 NumPublicConnections,
+ bool bIsLANMatch);
+
+ void UpdateSession();
+
+ void StartSession();
+
+ void EndSession();
+
+ void DestroySession();
+
+ void FindSessions(int32 MaxSearchResults, bool bIsLANQuery);
+
+ void JoinSession(const FOnlineSessionSearchResult &SessionSearchResult);
+
+ void JoinSession(const int32 Index);
+
+ bool TryConnectToCurrentSession() const;
+
+ void QuitCurrentSession();
+
+ void UpdateSessionName(FString NewSessionName);
+
+ FMyOnCreateSessionComplete OnCreateSessionCompleteEvent;
+ FMyOnUpdateSessionComplete OnUpdateSessionCompleteEvent;
+ FMyOnStartSessionCompete OnStartSessionCompleteEvent;
+ FMyOnEndSessionComplete OnEndSessionCompleteEvent;
+ FMyOnDestroySessionComplete OnDestroySessionCompleteEvent;
+ FMyOnFindSessionsComplete OnFindSessionsCompleteEvent;
+ FMyOnJoinSessionCompele OnJoinSessionCompleteEvent;
+
+protected:
+ void OnCreateSessionCompleted(FName SessionName, bool bSuccessful);
+
+ void OnUpdateSessionCompleted(FName SessionName, bool bSuccessful);
+
+ void OnStartSessionCompleted(FName SessionName, bool bSuccessful);
+
+ void OnEndSessionCompleted(FName SessionName, bool bSuccessful);
+
+ void OnDestroySessionCompleted(FName SessionName, bool bSuccessful);
+
+ void OnFindSessionsCompleted(bool bSuccessful);
+
+ void OnJoinSessionCompleted(FName SessionName,
+ EOnJoinSessionCompleteResult::Type Result);
+
+private:
+ bool bIsHost;
+
+ FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate;
+ FDelegateHandle CreateSessionCompleteDelegateHandle;
+ TSharedPtr LastSessionSettings;
+
+ FOnUpdateSessionCompleteDelegate UpdateSessionCompleteDelegate;
+ FDelegateHandle UpdateSessionCompleteDelegateHandle;
+
+ FOnStartSessionCompleteDelegate StartSessionCompleteDelegate;
+ FDelegateHandle StartSessionCompleteDelegateHandle;
+
+ FOnEndSessionCompleteDelegate EndSessionCompleteDelegate;
+ FDelegateHandle EndSessionCompleteDelegateHandle;
+
+ FOnDestroySessionCompleteDelegate DestroySessionCompleteDelegate;
+ FDelegateHandle DestroySessionCompleteDelegateHandle;
+
+ FOnFindSessionsCompleteDelegate FindSessionsCompleteDelegate;
+ FDelegateHandle FindSessionsCompleteDelegateHandle;
+ TSharedPtr LastSessionSearch;
+
+ FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate;
+ FDelegateHandle JoinSessionCompleteDelegateHandle;
+};
diff --git a/Source/TurnBasedTutorial/Trooper.cpp b/Source/TurnBasedTutorial/Trooper.cpp
deleted file mode 100644
index 2315b23..0000000
--- a/Source/TurnBasedTutorial/Trooper.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "Trooper.h"
-#include
-#include "MyPlayerController.h"
-
-// Sets default values
-ATrooper::ATrooper() {
- PrimaryActorTick.bCanEverTick = true;
- Tags.Add(FName("Trooper"));
- Id = NumberOfTroopersForId++;
- Position.Set(0, 0, 0);
- Mesh = CreateDefaultSubobject("Mesh");
- RootComponent = Mesh;
- static ConstructorHelpers::FObjectFinder MeshToUse(TEXT(
- "StaticMesh'/Game/StarterContent/Props/SM_Chair.SM_Chair'"
- ));
- if (MeshToUse.Object)
- {
- Mesh->SetStaticMesh(MeshToUse.Object);
- }
-}
-
-// Called when the game starts or when spawned
-void ATrooper::BeginPlay()
-{
- Super::BeginPlay();
-}
-
-void ATrooper::Tick(float deltaTime) {
- if (IsMoving) {
- FVector vector = (MoveToVector - Position);
- vector.Normalize();
- vector *= (Speed * deltaTime);
- if (vector.Size() >= (MoveToVector - Position).Size()) {
- Position = MoveToVector;
- IsMoving = false;
- dynamic_cast(
- UGameplayStatics::GetPlayerController(GetWorld(), 0)
- )->SetTrooperIsMoving(false);
- }
- else {
- Position += vector;
- }
- SetActorLocation(Position);
- }
-}
-
-void ATrooper::MoveTrooper(FVector newPos) {
- MoveToVector = newPos;
- IsMoving = true;
-}
-
-int ATrooper::NumberOfTroopersForId = 0;
-
-void ATrooper::InitNumberOfTroopersForId() {
- NumberOfTroopersForId = 0;
-}
-
-FVector ATrooper::GetPosition() {
- return Position;
-}
-
-bool ATrooper::IsOnPlayersSide() {
- return OnPlayersSide;
-}
-
-int ATrooper::GetId() {
- return Id;
-}
-
-void ATrooper::InitTrooper(FVector position, bool onPlayersSide) {
- Position = position;
- OnPlayersSide = onPlayersSide;
-}
diff --git a/Source/TurnBasedTutorial/Trooper.h b/Source/TurnBasedTutorial/Trooper.h
deleted file mode 100644
index c6d2b75..0000000
--- a/Source/TurnBasedTutorial/Trooper.h
+++ /dev/null
@@ -1,56 +0,0 @@
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "GameFramework/Actor.h"
-#include "Components/StaticMeshComponent.h"
-#include "Trooper.generated.h"
-
-UCLASS()
-class TURNBASEDTUTORIAL_API ATrooper : public AActor
-{
- GENERATED_BODY()
-
-public:
- // Sets default values for this actor's properties
- ATrooper();
-
-protected:
- static int NumberOfTroopersForId;
-
- virtual void BeginPlay() override;
-
- virtual void Tick(float deltaTime) override;
-
- UPROPERTY()
- FVector Position;
-
- UPROPERTY()
- bool OnPlayersSide;
-
- UPROPERTY()
- int Id;
-
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
- UStaticMeshComponent* Mesh;
-
- UPROPERTY()
- float Speed = 300.0f;
-
- bool IsMoving = false;
-
- FVector MoveToVector;
-
-public:
- void MoveTrooper(FVector newPos);
-
- static void InitNumberOfTroopersForId();
-
- FVector GetPosition();
-
- bool IsOnPlayersSide();
-
- int GetId();
-
- void InitTrooper(FVector position, bool onPlayersSide);
-};
diff --git a/Source/TurnBasedTutorial/TurnBasedTutorial.Build.cs b/Source/TurnBasedTutorial/TurnBasedTutorial.Build.cs
index 7f99bb8..de07b92 100644
--- a/Source/TurnBasedTutorial/TurnBasedTutorial.Build.cs
+++ b/Source/TurnBasedTutorial/TurnBasedTutorial.Build.cs
@@ -16,7 +16,7 @@ public class TurnBasedTutorial : ModuleRules
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
- // PrivateDependencyModuleNames.Add("OnlineSubsystem");
+ PrivateDependencyModuleNames.AddRange(new string[] {"OnlineSubsystem", "OnlineSubsystemUtils"});
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
diff --git a/Source/TurnBasedTutorial/TurnBasedTutorial.cpp b/Source/TurnBasedTutorial/TurnBasedTutorial.cpp
index 967bcf2..dad6184 100644
--- a/Source/TurnBasedTutorial/TurnBasedTutorial.cpp
+++ b/Source/TurnBasedTutorial/TurnBasedTutorial.cpp
@@ -3,4 +3,5 @@
#include "TurnBasedTutorial.h"
#include "Modules/ModuleManager.h"
-IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, TurnBasedTutorial, "TurnBasedTutorial" );
+IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, TurnBasedTutorial,
+ "TurnBasedTutorial");
diff --git a/Source/TurnBasedTutorial/TurnBasedTutorial.h b/Source/TurnBasedTutorial/TurnBasedTutorial.h
index 677c8e2..018672a 100644
--- a/Source/TurnBasedTutorial/TurnBasedTutorial.h
+++ b/Source/TurnBasedTutorial/TurnBasedTutorial.h
@@ -2,5 +2,4 @@
#pragma once
-#include "CoreMinimal.h"
-
+#include "Core.h"
diff --git a/TurnBased.sln.DotSettings.user b/TurnBased.sln.DotSettings.user
new file mode 100644
index 0000000..f591ba1
--- /dev/null
+++ b/TurnBased.sln.DotSettings.user
@@ -0,0 +1,2 @@
+
+ INFO
\ No newline at end of file
diff --git a/TurnBased.uproject b/TurnBased.uproject
index 7454dbb..8485d6b 100644
--- a/TurnBased.uproject
+++ b/TurnBased.uproject
@@ -1,6 +1,6 @@
{
"FileVersion": 3,
- "EngineAssociation": "{B4FE6467-4579-3D84-B22A-558A3D607596}",
+ "EngineAssociation": "4.27",
"Category": "",
"Description": "",
"Modules": [
@@ -9,7 +9,8 @@
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
- "Engine"
+ "Engine",
+ "UMG"
]
}
]
diff --git a/WindowsNoEditor/Manifest_NonUFSFiles_Win64.txt b/WindowsNoEditor/Manifest_NonUFSFiles_Win64.txt
new file mode 100644
index 0000000..f1f2447
--- /dev/null
+++ b/WindowsNoEditor/Manifest_NonUFSFiles_Win64.txt
@@ -0,0 +1,25 @@
+TurnBased/Binaries/Win64/TurnBasedTutorial.exe 2023-03-16T03:05:02.259Z
+TurnBased/Binaries/Win64/OpenImageDenoise.dll 2023-03-16T01:52:15.842Z
+TurnBased/Binaries/Win64/tbb12.dll 2023-03-16T01:52:15.844Z
+Engine/Extras/Redist/en-us/UE4PrereqSetup_x64.exe 2023-03-16T01:46:38.646Z
+Engine/Binaries/ThirdParty/Oculus/OVRPlugin/OVRPlugin/Win64/OVRPlugin.dll 2023-03-16T01:41:33.496Z
+Engine/Binaries/ThirdParty/DbgHelp/dbghelp.dll 2023-03-16T01:41:25.090Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/PxFoundationPROFILE_x64.dll 2023-03-16T01:41:46.155Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/PxPvdSDKPROFILE_x64.dll 2023-03-16T01:41:46.339Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/PhysX3PROFILE_x64.dll 2023-03-16T01:41:45.025Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/PhysX3CookingPROFILE_x64.dll 2023-03-16T01:41:45.005Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/PhysX3CommonPROFILE_x64.dll 2023-03-16T01:41:44.869Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/APEX_ClothingPROFILE_x64.dll 2023-03-16T01:41:41.229Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/APEX_LegacyPROFILE_x64.dll 2023-03-16T01:41:42.482Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/ApexFrameworkPROFILE_x64.dll 2023-03-16T01:41:43.065Z
+Engine/Binaries/ThirdParty/PhysX3/Win64/VS2015/NvClothPROFILE_x64.dll 2023-03-16T01:41:43.666Z
+Engine/Binaries/ThirdParty/Ogg/Win64/VS2015/libogg_64.dll 2023-03-16T01:41:33.497Z
+Engine/Binaries/ThirdParty/Vorbis/Win64/VS2015/libvorbis_64.dll 2023-03-16T01:41:53.464Z
+Engine/Binaries/ThirdParty/Vorbis/Win64/VS2015/libvorbisfile_64.dll 2023-03-16T01:41:53.464Z
+Engine/Binaries/ThirdParty/NVIDIA/NVaftermath/Win64/GFSDK_Aftermath_Lib.x64.dll 2023-03-16T01:41:32.651Z
+Engine/Binaries/ThirdParty/OpenXR/win64/openxr_loader.dll 2023-03-16T01:41:33.921Z
+Engine/Binaries/ThirdParty/OpenVR/OpenVRv1_5_17/Win64/openvr_api.dll 2023-03-16T01:41:33.917Z
+Engine/Binaries/ThirdParty/NVIDIA/GeForceNOW/Win64/GfnRuntimeSdk.dll 2023-03-16T01:41:32.648Z
+Engine/Binaries/ThirdParty/Windows/WinPixEventRuntime/x64/WinPixEventRuntime.dll 2023-03-16T01:41:53.487Z
+Engine/Binaries/ThirdParty/Windows/XAudio2_9/x64/xaudio2_9redist.dll 2023-03-16T01:41:54.149Z
+TurnBasedTutorial.exe 2023-03-16T03:07:14.267Z
diff --git a/WindowsNoEditor/TurnBased/Content/Paks/TurnBased-WindowsNoEditor.pak b/WindowsNoEditor/TurnBased/Content/Paks/TurnBased-WindowsNoEditor.pak
new file mode 100644
index 0000000..549883f
Binary files /dev/null and b/WindowsNoEditor/TurnBased/Content/Paks/TurnBased-WindowsNoEditor.pak differ