diff --git a/.gitignore b/.gitignore index 87ea7c8e..e953e0ed 100644 --- a/.gitignore +++ b/.gitignore @@ -567,6 +567,9 @@ FodyWeavers.xsd # Our Assets Managing content/Assets/* +content/VFX_Asset/* +content/SFX_Asset/* +content/Enemy_Asset/* # Rider .idea all ignore .idea/* \ No newline at end of file diff --git a/Build/Windows/Application.ico b/Build/Windows/Application.ico new file mode 100644 index 00000000..2612cd3a Binary files /dev/null and b/Build/Windows/Application.ico differ diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index e53adf8c..6aed512e 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -1,10 +1,19 @@ [/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Game/Maps/Base.Base -GameInstanceClass=/Game/BP_Instance_0701.BP_Instance_0701_C +EditorStartupMap=/Game/Maps/TG_MainMenu.TG_MainMenu +LocalMapOptions= TransitionMap=/Game/Maps/Base.Base -EditorStartupMap=/Game/Maps/Base.Base +bUseSplitscreen=True +TwoPlayerSplitscreenLayout=Horizontal +ThreePlayerSplitscreenLayout=FavorTop +FourPlayerSplitscreenLayout=Grid +bOffsetPlayerGamepadIds=False +GameInstanceClass=/Game/BP_Instance_0701.BP_Instance_0701_C +GameDefaultMap=/Game/Maps/TG_MainMenu.TG_MainMenu +ServerDefaultMap=/Engine/Maps/Entry.Entry +GlobalDefaultGameMode=/Game/Core/GameMode/BP_Gamemode_MainMenu.BP_Gamemode_MainMenu_C +GlobalDefaultServerGameMode=None [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 @@ -44,13 +53,17 @@ r.DynamicGlobalIlluminationMethod=1 r.ReflectionMethod=1 -r.Shadow.Virtual.Enable=1 +r.Shadow.Virtual.Enable=0 r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8 r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8 +r.VirtualTextures=True +r.Substrate=False +r.DefaultFeature.AutoExposure=False +r.CustomDepth=3 [/Script/LinuxTargetPlatform.LinuxTargetSettings] -TargetedRHIs=SF_VULKAN_SM5 @@ -66,13 +79,27 @@ AppliedDefaultGraphicsPerformance=Maximum CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' [/Script/Engine.UserInterfaceSettings] +RenderFocusRule=NavigationOnly +HardwareCursors=() +SoftwareCursors=((Default, "/Game/TG/TestWBP/WBP_TestCursor.WBP_TestCursor_C")) +ApplicationScale=1.000000 +UIScaleRule=ShortestSide +CustomScalingRuleClass=None +UIScaleCurve=(EditorCurveData=(Keys=((Time=480.000000,Value=0.444000),(Time=720.000000,Value=0.666000),(Time=1080.000000,Value=1.000000),(Time=8640.000000,Value=8.000000)),DefaultValue=340282346638528859811704183484516925440.000000,PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant),ExternalCurve=None) +bAllowHighDPIInGameMode=False +DesignScreenSize=(X=1920,Y=1080) +bLoadWidgetsOnDedicatedServer=True bAuthorizeAutomaticWidgetVariableCreation=False +CustomFontDPI=96 FontDPIPreset=Standard -FontDPI=72 +bUseCustomFontDPI=False [/Script/Engine.Engine] +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/ProjectMJ") +ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/ProjectMJ") +bUseFixedFrameRate=True +SmoothedFrameRateRange=(LowerBound=(Type=Inclusive,Value=22.000000),UpperBound=(Type=Open,Value=62.000000)) +FixedFrameRate=120.000000 [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True @@ -93,6 +120,14 @@ net.AllowPIESeamlessTravel=1 [CoreRedirects] +ClassRedirects=(OldName="/Script/ProjectMJ.ProjectyMJAbilitySystemComponent",NewName="/Script/ProjectMJ.ProjectMJAbilitySystemComponent") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJItemBase2",NewName="/Script/ProjectMJ.MJItemBase") ++StructRedirects=(OldName="/Script/ProjectMJ.InventoryItemData",NewName="/Script/ProjectMJ.ItemDataRow") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJEnemyHPBarComponent",NewName="/Script/ProjectMJ.MJWidgetComponent") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJDialogueInteractionWidget",NewName="/Script/ProjectMJ.MJInteractionWidget") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJGA_BasicMeleeAttack",NewName="/Script/ProjectMJ.MJGA_AIActionInstantAbility") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJGA_BaseProjectile",NewName="/Script/ProjectMJ.MJGA_SpawnProjectile") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJGA_ActionAbility",NewName="/Script/ProjectMJ.MJGA_AIActionInstantAbility") ++ClassRedirects=(OldName="/Script/ProjectMJ.MJGA_ActionInstantAbility",NewName="/Script/ProjectMJ.MJGA_AIActionInstantAbility") [/Script/Engine.CollisionProfile] -Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False) @@ -131,9 +166,16 @@ net.AllowPIESeamlessTravel=1 +Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") +Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") +Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") -+Profiles=(Name="MJCapsule",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Pawn",CustomResponses=((Channel="Destructible",Response=ECR_Ignore),(Channel="MJAbilityTargetTrace")),HelpMessage="ProjectMJ Capsule") ++Profiles=(Name="MJCapsule",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Pawn",CustomResponses=((Channel="Destructible",Response=ECR_Ignore),(Channel="MJAbilityTargetTrace"),(Channel="MJProjectile",Response=ECR_Overlap)),HelpMessage="ProjectMJ Capsule") +Profiles=(Name="MJTrigger",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="ProjectMJ Trigger ") ++Profiles=(Name="MJProjectile",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="MJProjectile",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore),(Channel="MJGround",Response=ECR_Overlap)),HelpMessage="ProjectMJ Projectile") ++Profiles=(Name="MJObstacle",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap),(Channel="MJProjectile")),HelpMessage="ProjectMJ Obstacle") ++Profiles=(Name="MJBlock",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Camera",Response=ECR_Ignore),(Channel="MJProjectile",Response=ECR_Overlap)),HelpMessage="ProjectMJ BlockActor") +DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="MJAbilityTargetTrace") ++DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Ignore,bTraceType=False,bStaticObject=False,Name="MJProjectile") ++DefaultChannelResponses=(Channel=ECC_GameTraceChannel3,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="MJGround") ++EditProfiles=(Name="Pawn",CustomResponses=((Channel="MJProjectile",Response=ECR_Ignore))) ++EditProfiles=(Name="InvisibleWall",CustomResponses=((Channel="MJGround",Response=ECR_Ignore))) -ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") -ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") -ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") @@ -153,4 +195,13 @@ net.AllowPIESeamlessTravel=1 +CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") +CollisionChannelRedirects=(OldName="MJTargetActor",NewName="MJAbilityTargetTrace") ++CollisionChannelRedirects=(OldName="Ground",NewName="MJGround") + +[Core.Log] +Log[LogAbilitySystem]=VerBose + +[/Script/Engine.AudioSettings] +DefaultBaseSoundMix=/Game/TG/TestAudio/Global_SoundMix.Global_SoundMix +DefaultSoundClassName=/Game/TG/TestAudio/SoundClass/MJMasterSound.MJMasterSound +DefaultMediaSoundClassName=/Game/TG/TestAudio/SoundClass/MJMasterSound.MJMasterSound diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index 174bdea9..87bfdafa 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -1,7 +1,129 @@ + [/Script/EngineSettings.GeneralProjectSettings] ProjectID=DCCACB5E482475AFEF92389D4E855B5A - +ProjectName=Then one day : The Memorist +CompanyName=ThenOneDayStudio +CompanyDistinguishedName=ThenOneDayStudio +SupportContact=thenonedaystudio@gmail.com +CopyrightNotice=ThenOneDayStudio +ProjectDisplayedTitle=NSLOCTEXT("[/Script/EngineSettings]", "417D5D4C4A7FD701E640489654FC2713", "Then one day : The Memorist") +ProjectDebugTitleInfo=NSLOCTEXT("[/Script/EngineSettings]", "10237A1145E72D6A500A279EFFF60276", "Then one day : The Memorist") [/Script/GameplayAbilities.AbilitySystemGlobals] -bUseDebugTargetFromHud=True \ No newline at end of file +bUseDebugTargetFromHud=True + +[/Script/UnrealEd.ProjectPackagingSettings] +Build=IfProjectHasCode +BuildConfiguration=PPBC_Development +BuildTarget= +FullRebuild=False +ForDistribution=False +IncludeDebugFiles=False +BlueprintNativizationMethod=Disabled +bIncludeNativizedAssetsInProjectGeneration=False +bExcludeMonolithicEngineHeadersInNativizedCode=False +UsePakFile=True +bUseIoStore=True +bUseZenStore=False +bMakeBinaryConfig=False +bGenerateChunks=False +bGenerateNoChunks=False +bChunkHardReferencesOnly=False +bForceOneChunkPerFile=False +MaxChunkSize=0 +bBuildHttpChunkInstallData=False +HttpChunkInstallDataDirectory=(Path="") +WriteBackMetadataToAssetRegistry=Disabled +bWritePluginSizeSummaryJsons=False +bCompressed=True +PackageCompressionFormat=Oodle +bForceUseProjectCompressionFormatIgnoreHardwareOverride=False +PackageAdditionalCompressionOptions= +PackageCompressionMethod=Kraken +PackageCompressionLevel_DebugDevelopment=4 +PackageCompressionLevel_TestShipping=4 +PackageCompressionLevel_Distribution=7 +PackageCompressionMinBytesSaved=1024 +PackageCompressionMinPercentSaved=5 +bPackageCompressionEnableDDC=False +PackageCompressionMinSizeToConsiderDDC=0 +HttpChunkInstallDataVersion= +IncludePrerequisites=True +IncludeAppLocalPrerequisites=False +bShareMaterialShaderCode=True +bDeterministicShaderCodeOrder=False +bSharedMaterialNativeLibraries=True +ApplocalPrerequisitesDirectory=(Path="") +IncludeCrashReporter=False +InternationalizationPreset=English +-CulturesToStage=en ++CulturesToStage=en +LocalizationTargetCatchAllChunkId=0 +bCookAll=False +bCookMapsOnly=False +bSkipEditorContent=False +bSkipMovies=False +-IniKeyDenylist=KeyStorePassword +-IniKeyDenylist=KeyPassword +-IniKeyDenylist=rsa.privateexp +-IniKeyDenylist=rsa.modulus +-IniKeyDenylist=rsa.publicexp +-IniKeyDenylist=aes.key +-IniKeyDenylist=SigningPublicExponent +-IniKeyDenylist=SigningModulus +-IniKeyDenylist=SigningPrivateExponent +-IniKeyDenylist=EncryptionKey +-IniKeyDenylist=DevCenterUsername +-IniKeyDenylist=DevCenterPassword +-IniKeyDenylist=IOSTeamID +-IniKeyDenylist=SigningCertificate +-IniKeyDenylist=MobileProvision +-IniKeyDenylist=IniKeyDenylist +-IniKeyDenylist=IniSectionDenylist ++IniKeyDenylist=KeyStorePassword ++IniKeyDenylist=KeyPassword ++IniKeyDenylist=rsa.privateexp ++IniKeyDenylist=rsa.modulus ++IniKeyDenylist=rsa.publicexp ++IniKeyDenylist=aes.key ++IniKeyDenylist=SigningPublicExponent ++IniKeyDenylist=SigningModulus ++IniKeyDenylist=SigningPrivateExponent ++IniKeyDenylist=EncryptionKey ++IniKeyDenylist=DevCenterUsername ++IniKeyDenylist=DevCenterPassword ++IniKeyDenylist=IOSTeamID ++IniKeyDenylist=SigningCertificate ++IniKeyDenylist=MobileProvision ++IniKeyDenylist=IniKeyDenylist ++IniKeyDenylist=IniSectionDenylist +-IniSectionDenylist=HordeStorageServers +-IniSectionDenylist=StorageServers ++IniSectionDenylist=HordeStorageServers ++IniSectionDenylist=StorageServers ++MapsToCook=(FilePath="/Game/Maps/TG_MainMenu") ++MapsToCook=(FilePath="/Game/Maps/TG_Town") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle01") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle02") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle03") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle04") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle05") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Boss") ++MapsToCook=(FilePath="/Game/Maps/Dungeon_Chunks/Dungeon_Chunk_Reward") +bRetainStagedDirectory=False +CustomStageCopyHandler= + +[/Script/Engine.AssetManagerSettings] +-PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) +-PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass=/Script/Engine.PrimaryAssetLabel,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) ++PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass="/Script/Engine.World",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) ++PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass="/Script/Engine.PrimaryAssetLabel",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) ++PrimaryAssetTypesToScan=(PrimaryAssetType="DungeonGenerationSubSystem",AssetBaseClass="/Script/ProjectMJ.MJDungeonGenerationSubSystem",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Script/ProjectMJ.MJDungeonGenerationSubSystem")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) +bOnlyCookProductionAssets=False +bShouldManagerDetermineTypeAndName=False +bShouldGuessTypeAndNameInEditor=True +bShouldAcquireMissingChunksOnLoad=False +bShouldWarnAboutInvalidAssets=True +MetaDataTagsForAssetRegistry=() + diff --git a/Config/DefaultGameplayTags.ini b/Config/DefaultGameplayTags.ini new file mode 100644 index 00000000..e13cb5b3 --- /dev/null +++ b/Config/DefaultGameplayTags.ini @@ -0,0 +1,13 @@ +[/Script/GameplayTags.GameplayTagsSettings] +ImportTagsFromConfig=True +WarnOnInvalidTags=True +ClearInvalidTags=False +AllowEditorTagUnloading=True +AllowGameTagUnloading=False +FastReplication=False +InvalidTagCharacters="\"\'," ++GameplayTagRedirects=(OldTagName="Item.Wearable.Weapon.Stadd",NewTagName="Item.Wearable.Weapon.Staff") ++GameplayTagRedirects=(OldTagName="Item.Etc.WoodElemental.Wodd",NewTagName="Item.Etc.WoodElemental.Wood") +NumBitsForContainerSize=6 +NetIndexFirstBitSegment=16 + diff --git a/Config/Tags/Character.ini b/Config/Tags/Character.ini index ba3ffb85..c2ce612c 100644 --- a/Config/Tags/Character.ini +++ b/Config/Tags/Character.ini @@ -1,3 +1,7 @@ [/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Character.State.IsAttacking",DevComment="") +GameplayTagList=(Tag="Character.State.IsCharging",DevComment="") GameplayTagList=(Tag="Character.State.IsDead",DevComment="") +GameplayTagList=(Tag="Character.State.IsHurt",DevComment="") +GameplayTagList=(Tag="Character.State.IsInactivated",DevComment="") diff --git a/Config/Tags/DMTestTags.ini b/Config/Tags/DMTestTags.ini deleted file mode 100644 index 287f4eab..00000000 --- a/Config/Tags/DMTestTags.ini +++ /dev/null @@ -1,7 +0,0 @@ -[/Script/GameplayTags.GameplayTagsList] -GameplayTagList=(Tag="Event.Character.Action.MeleeHitCheck",DevComment="") -GameplayTagList=(Tag="Input.Test.E",DevComment="") -GameplayTagList=(Tag="Input.Test.Q",DevComment="") -GameplayTagList=(Tag="Input.Test.R",DevComment="") -GameplayTagList=(Tag="Input.Test.W",DevComment="") - diff --git a/Config/Tags/Data.ini b/Config/Tags/Data.ini index 035fa8d2..425d97df 100644 --- a/Config/Tags/Data.ini +++ b/Config/Tags/Data.ini @@ -1,4 +1,46 @@ [/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Data.Character.AbilityPower",DevComment="") +GameplayTagList=(Tag="Data.Character.Armor",DevComment="") +GameplayTagList=(Tag="Data.Character.AttackDamage",DevComment="") +GameplayTagList=(Tag="Data.Character.AttackSpeed",DevComment="") +GameplayTagList=(Tag="Data.Character.CriticalChance",DevComment="") +GameplayTagList=(Tag="Data.Character.CriticalDamage",DevComment="") +GameplayTagList=(Tag="Data.Character.Damage",DevComment="") +GameplayTagList=(Tag="Data.Character.DropExperience",DevComment="") +GameplayTagList=(Tag="Data.Character.Experience",DevComment="") +GameplayTagList=(Tag="Data.Character.Focus",DevComment="") +GameplayTagList=(Tag="Data.Character.FocusRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.Health",DevComment="") +GameplayTagList=(Tag="Data.Character.HealthRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.IsCritical",DevComment="") +GameplayTagList=(Tag="Data.Character.Level",DevComment="") +GameplayTagList=(Tag="Data.Character.Mana",DevComment="") +GameplayTagList=(Tag="Data.Character.ManaRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxAbilityPower",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxArmor",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxAttackDamage",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxAttackSpeed",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxCriticalChance",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxCriticalDamage",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxDropExperience",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxExperience",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxFocus",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxFocusRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxHealth",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxHealthRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxLevel",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxMana",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxManaRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxMovementSpeed",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxResistance",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxSkillCooldown",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxStamina",DevComment="") +GameplayTagList=(Tag="Data.Character.MaxStaminaRegeneration",DevComment="") +GameplayTagList=(Tag="Data.Character.MovementSpeed",DevComment="") +GameplayTagList=(Tag="Data.Character.Resistance",DevComment="") +GameplayTagList=(Tag="Data.Character.SkillCooldown",DevComment="") +GameplayTagList=(Tag="Data.Character.Stamina",DevComment="") +GameplayTagList=(Tag="Data.Character.StaminaRegeneration",DevComment="") GameplayTagList=(Tag="Data.Skill",DevComment="") GameplayTagList=(Tag="Data.Skill.AbilityPowerScaling",DevComment="") GameplayTagList=(Tag="Data.Skill.AttackDamageScaling",DevComment="") @@ -9,6 +51,10 @@ GameplayTagList=(Tag="Data.Skill.CostFocus",DevComment="") GameplayTagList=(Tag="Data.Skill.CostMana",DevComment="") GameplayTagList=(Tag="Data.Skill.CostStamina",DevComment="") GameplayTagList=(Tag="Data.Skill.EffectDuration",DevComment="") +GameplayTagList=(Tag="Data.Skill.ExplosionADScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.ExplosionAPScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.ExplosionBaseDamage",DevComment="") +GameplayTagList=(Tag="Data.Skill.ExplosionRadius",DevComment="") GameplayTagList=(Tag="Data.Skill.Healing",DevComment="") GameplayTagList=(Tag="Data.Skill.LifeSteal",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxAbilityPowerScaling",DevComment="") @@ -20,26 +66,47 @@ GameplayTagList=(Tag="Data.Skill.MaxCostFocus",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxCostMana",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxCostStamina",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxEffectDuration",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxExplosionADScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxExplosionAPScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxExplosionBaseDamage",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxExplosionRadius",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxHealing",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxLifeSteal",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxPostDelay",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxPreDelay",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxProjectileCount",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxProjectileLifeSpan",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxProjectilePierceCount",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxProjectileSpeed",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxSkillAttackRate",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxSkillLevel",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxSkillRadius",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxSkillRange",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusBaseDamage",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusEffectADScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusEffectAPScaling",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxStatusEffectChance",DevComment="") GameplayTagList=(Tag="Data.Skill.MaxStatusEffectDuration",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusEffectMaxStack",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusEffectPeriod",DevComment="") +GameplayTagList=(Tag="Data.Skill.MaxStatusEffectSlowPercent",DevComment="") GameplayTagList=(Tag="Data.Skill.PostDelay",DevComment="") GameplayTagList=(Tag="Data.Skill.PreDelay",DevComment="") GameplayTagList=(Tag="Data.Skill.ProjectileCount",DevComment="") +GameplayTagList=(Tag="Data.Skill.ProjectileLifeSpan",DevComment="") +GameplayTagList=(Tag="Data.Skill.ProjectilePierceCount",DevComment="") GameplayTagList=(Tag="Data.Skill.ProjectileSpeed",DevComment="") +GameplayTagList=(Tag="Data.Skill.SkillAttackLocationOffset",DevComment="") GameplayTagList=(Tag="Data.Skill.SkillAttackRate",DevComment="") GameplayTagList=(Tag="Data.Skill.SkillLevel",DevComment="") GameplayTagList=(Tag="Data.Skill.SkillRadius",DevComment="") GameplayTagList=(Tag="Data.Skill.SkillRange",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusBaseDamage",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusEffectADScaling",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusEffectAPScaling",DevComment="") GameplayTagList=(Tag="Data.Skill.StatusEffectChance",DevComment="") GameplayTagList=(Tag="Data.Skill.StatusEffectDuration",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusEffectMaxStack",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusEffectPeriod",DevComment="") +GameplayTagList=(Tag="Data.Skill.StatusEffectSlowPercent",DevComment="") diff --git a/Config/Tags/Enemy.ini b/Config/Tags/Enemy.ini new file mode 100644 index 00000000..cefe9a4c --- /dev/null +++ b/Config/Tags/Enemy.ini @@ -0,0 +1,33 @@ +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Enemy.Name",DevComment="") +GameplayTagList=(Tag="Enemy.Name.BluePlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.BlueSpider",DevComment="") +GameplayTagList=(Tag="Enemy.Name.BrownWolf",DevComment="") +GameplayTagList=(Tag="Enemy.Name.DarkWolf",DevComment="") +GameplayTagList=(Tag="Enemy.Name.EarthGolem",DevComment="") +GameplayTagList=(Tag="Enemy.Name.FireElemental",DevComment="") +GameplayTagList=(Tag="Enemy.Name.ForestCreature",DevComment="") +GameplayTagList=(Tag="Enemy.Name.FireGolem",DevComment="") +GameplayTagList=(Tag="Enemy.Name.GreenPlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.GreenSpider",DevComment="") +GameplayTagList=(Tag="Enemy.Name.GreyWolf",DevComment="") +GameplayTagList=(Tag="Enemy.Name.IceGolem",DevComment="") +GameplayTagList=(Tag="Enemy.Name.IceWolf",DevComment="") +GameplayTagList=(Tag="Enemy.Name.MiniBluePlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.MiniGreenPlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.MiniPinkPlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.PinkPlant",DevComment="") +GameplayTagList=(Tag="Enemy.Name.PoisonElemental",DevComment="") +GameplayTagList=(Tag="Enemy.Name.RedSpider",DevComment="") +GameplayTagList=(Tag="Enemy.Name.StoneElement",DevComment="") +GameplayTagList=(Tag="Enemy.Name.WaterElemental",DevComment="") +GameplayTagList=(Tag="Enemy.Species",DevComment="") +GameplayTagList=(Tag="Enemy.Species.CarnivorousPlant",DevComment="") +GameplayTagList=(Tag="Enemy.Species.Golem",DevComment="") +GameplayTagList=(Tag="Enemy.Species.Spider",DevComment="") +GameplayTagList=(Tag="Enemy.Species.Spirit",DevComment="") +GameplayTagList=(Tag="Enemy.Type.Boss",DevComment="") +GameplayTagList=(Tag="Enemy.Species.Wolf",DevComment="") +GameplayTagList=(Tag="Enemy.Type.Nomal",DevComment="") +GameplayTagList=(Tag="ForestCreature",DevComment="") + diff --git a/Config/Tags/GameplayCue.ini b/Config/Tags/GameplayCue.ini new file mode 100644 index 00000000..2ab84360 --- /dev/null +++ b/Config/Tags/GameplayCue.ini @@ -0,0 +1,13 @@ +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="GameplayCue.Skill.AirArrow.Hit",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.AirArrow.Muzzle",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.BasicMeleeAttack.Attack",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.BasicMeleeAttack.Hit",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.Catastrophe.Hit",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.Frozen",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.Frozen.Status",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.NormalMeleeAttack.Attack",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.NormalMeleeAttack.Hit",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.PoisonSlash.Hit",DevComment="") +GameplayTagList=(Tag="GameplayCue.Skill.PoisonSlash.Status",DevComment="") + diff --git a/Config/Tags/Input.ini b/Config/Tags/Input.ini new file mode 100644 index 00000000..6099fbaf --- /dev/null +++ b/Config/Tags/Input.ini @@ -0,0 +1,8 @@ +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Input.Mouse.Left.Hold",DevComment="") +GameplayTagList=(Tag="Input.Mouse.Left.Pressed",DevComment="") +GameplayTagList=(Tag="Input.Mouse.Left.Released",DevComment="") +GameplayTagList=(Tag="Input.Mouse.Right.Hold",DevComment="") +GameplayTagList=(Tag="Input.Mouse.Right.Pressed",DevComment="") +GameplayTagList=(Tag="Input.Mouse.Right.Released",DevComment="") + diff --git a/Config/Tags/Item.ini b/Config/Tags/Item.ini new file mode 100644 index 00000000..6ef9e67a --- /dev/null +++ b/Config/Tags/Item.ini @@ -0,0 +1,34 @@ +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Item.Consumable.Arrow",DevComment="") +GameplayTagList=(Tag="Item.Consumable.Buff.Attack",DevComment="") +GameplayTagList=(Tag="Item.Consumable.Buff.Speed",DevComment="") +GameplayTagList=(Tag="Item.Consumable.Potion.HP",DevComment="") +GameplayTagList=(Tag="Item.Consumable.Potion.MP",DevComment="") +GameplayTagList=(Tag="Item.Etc",DevComment="") +GameplayTagList=(Tag="Item.Etc.Core",DevComment="") +GameplayTagList=(Tag="Item.Etc.Fabric",DevComment="") +GameplayTagList=(Tag="Item.Etc.Poison",DevComment="") +GameplayTagList=(Tag="Item.Etc.SpiderEggs",DevComment="") +GameplayTagList=(Tag="Item.Etc.Stone",DevComment="") +GameplayTagList=(Tag="Item.Etc.Web",DevComment="") +GameplayTagList=(Tag="Item.Etc.Wood",DevComment="") +GameplayTagList=(Tag="Item.Wearable",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Accessory.Bracelet",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Accessory.Necklace",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Accessory.Ring",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Armor.Bottom",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Armor.Helmet",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Armor.Shoes",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Armor.Top",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Charge",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Charge.Bow",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Charge.Gun",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Melee",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Melee.DoubleSword",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Melee.Spear",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Melee.Sword",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Ranged",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Ranged.Orb",DevComment="") +GameplayTagList=(Tag="Item.Wearable.Weapon.Ranged.Staff",DevComment="") + diff --git a/Config/Tags/Skill.ini b/Config/Tags/Skill.ini index e4227f95..bead7469 100644 --- a/Config/Tags/Skill.ini +++ b/Config/Tags/Skill.ini @@ -1,8 +1,55 @@ [/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Event.Character.Action.AirArrow",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.AirSwordAura",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.AlphaStrike",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.BasicMeleeAttack",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Catastrophe",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.EarthGolemIdentity",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.FireElementalMeleeAttack",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.FireGolemIdentity",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.ForestCreatureMeleeAttack",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.IceGolemIdentity",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.EarthGolem",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.Fire",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.FireGolem",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.ForestCreature",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.IceGolem",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.Spider",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.Normal.Stone",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.NormalMeleeAttack",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.PoisonSlash",DevComment="") +GameplayTagList=(Tag="Event.Character.Action.StoneElementMeleeAttack",DevComment="") +GameplayTagList=(Tag="Event.Character.Appear",DevComment="") +GameplayTagList=(Tag="Event.Character.Dead",DevComment="") +GameplayTagList=(Tag="Event.Character.Hurt",DevComment="") +GameplayTagList=(Tag="Event.Marker.AirSwordAura",DevComment="") GameplayTagList=(Tag="Skill.Charge",DevComment="") -GameplayTagList=(Tag="Skill.Charge.Catastrophe",DevComment="") +GameplayTagList=(Tag="Skill.Charge.AlphaStrike",DevComment="") +GameplayTagList=(Tag="Skill.Charge.Cooldown",DevComment="") GameplayTagList=(Tag="Skill.Instant",DevComment="") +GameplayTagList=(Tag="Skill.Instant.AirArrow",DevComment="") +GameplayTagList=(Tag="Skill.Instant.AirSwordAura",DevComment="") GameplayTagList=(Tag="Skill.Instant.BasicMeleeAttack",DevComment="") +GameplayTagList=(Tag="Skill.Instant.Cooldown",DevComment="") +GameplayTagList=(Tag="Skill.Instant.EarthGolemIdentity",DevComment="") +GameplayTagList=(Tag="Skill.Instant.FireElementalMeleeAttack",DevComment="") +GameplayTagList=(Tag="Skill.Instant.FireGolemIdentity",DevComment="") +GameplayTagList=(Tag="Skill.Instant.ForestCreatureMeleeAttack",DevComment="") +GameplayTagList=(Tag="Skill.Instant.IceGolemIdentity",DevComment="") +GameplayTagList=(Tag="Skill.Instant.MysticShot",DevComment="") +GameplayTagList=(Tag="Skill.Instant.PoisonSlash",DevComment="") +GameplayTagList=(Tag="Skill.Instant.StoneElementalMeleeAttack",DevComment="") +GameplayTagList=(Tag="Skill.Normal.Cooldown",DevComment="") +GameplayTagList=(Tag="Skill.Normal.EarthGolem",DevComment="") +GameplayTagList=(Tag="Skill.Normal.Fire",DevComment="") +GameplayTagList=(Tag="Skill.Normal.FireGolem",DevComment="") +GameplayTagList=(Tag="Skill.Normal.ForestCreature",DevComment="") +GameplayTagList=(Tag="Skill.Normal.IceGolem",DevComment="") +GameplayTagList=(Tag="Skill.Normal.MeleeAttack",DevComment="") +GameplayTagList=(Tag="Skill.Normal.Spider",DevComment="") +GameplayTagList=(Tag="Skill.Normal.Stone",DevComment="") GameplayTagList=(Tag="Skill.Passive",DevComment="") -GameplayTagList=(Tag="Skill.State.CoolDown",DevComment="") +GameplayTagList=(Tag="Skill.Passive.DamageUp",DevComment="") +GameplayTagList=(Tag="Skill.Passive.Test",DevComment="") diff --git a/Config/Tags/Status.ini b/Config/Tags/Status.ini new file mode 100644 index 00000000..ad14fae4 --- /dev/null +++ b/Config/Tags/Status.ini @@ -0,0 +1,3 @@ +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="Status.Debuff.Poison",DevComment="") + diff --git a/Content/AI/Elemental/ABP_MJElemental.uasset b/Content/AI/Elemental/ABP_MJElemental.uasset new file mode 100644 index 00000000..7d1b5c77 Binary files /dev/null and b/Content/AI/Elemental/ABP_MJElemental.uasset differ diff --git a/Content/AI/Elemental/AM_ElementalAppear.uasset b/Content/AI/Elemental/AM_ElementalAppear.uasset new file mode 100644 index 00000000..1cb08d6f Binary files /dev/null and b/Content/AI/Elemental/AM_ElementalAppear.uasset differ diff --git a/Content/AI/Elemental/AM_ElementalDead.uasset b/Content/AI/Elemental/AM_ElementalDead.uasset new file mode 100644 index 00000000..72bf7fcb Binary files /dev/null and b/Content/AI/Elemental/AM_ElementalDead.uasset differ diff --git a/Content/AI/Elemental/AM_ElementalHurt.uasset b/Content/AI/Elemental/AM_ElementalHurt.uasset new file mode 100644 index 00000000..14f1d5ba Binary files /dev/null and b/Content/AI/Elemental/AM_ElementalHurt.uasset differ diff --git a/Content/AI/Elemental/AM_ElementalInActivated.uasset b/Content/AI/Elemental/AM_ElementalInActivated.uasset new file mode 100644 index 00000000..4932c995 Binary files /dev/null and b/Content/AI/Elemental/AM_ElementalInActivated.uasset differ diff --git a/Content/AI/Elemental/Anim_ElementalInActivated.uasset b/Content/AI/Elemental/Anim_ElementalInActivated.uasset new file mode 100644 index 00000000..4698c392 Binary files /dev/null and b/Content/AI/Elemental/Anim_ElementalInActivated.uasset differ diff --git a/Content/AI/Elemental/BS_ElementalLocomotion.uasset b/Content/AI/Elemental/BS_ElementalLocomotion.uasset new file mode 100644 index 00000000..742961d7 Binary files /dev/null and b/Content/AI/Elemental/BS_ElementalLocomotion.uasset differ diff --git a/Content/AI/Elemental/GA_ElementalStateAbility.uasset b/Content/AI/Elemental/GA_ElementalStateAbility.uasset new file mode 100644 index 00000000..c252d49c Binary files /dev/null and b/Content/AI/Elemental/GA_ElementalStateAbility.uasset differ diff --git a/Content/AI/Elemental/MJGA_ActionAppearElemental.uasset b/Content/AI/Elemental/MJGA_ActionAppearElemental.uasset new file mode 100644 index 00000000..1d692dab Binary files /dev/null and b/Content/AI/Elemental/MJGA_ActionAppearElemental.uasset differ diff --git a/Content/AI/Elemental/MJGA_ActionDamageElemental.uasset b/Content/AI/Elemental/MJGA_ActionDamageElemental.uasset new file mode 100644 index 00000000..88b02086 Binary files /dev/null and b/Content/AI/Elemental/MJGA_ActionDamageElemental.uasset differ diff --git a/Content/AI/Elemental/MJGA_ActionDeadElemental.uasset b/Content/AI/Elemental/MJGA_ActionDeadElemental.uasset new file mode 100644 index 00000000..32709acd Binary files /dev/null and b/Content/AI/Elemental/MJGA_ActionDeadElemental.uasset differ diff --git a/Content/AI/Elemental/NS_ElementalInActivated.uasset b/Content/AI/Elemental/NS_ElementalInActivated.uasset new file mode 100644 index 00000000..50a06501 Binary files /dev/null and b/Content/AI/Elemental/NS_ElementalInActivated.uasset differ diff --git a/Content/AI/Elemental/NS_Water_Magic_Waterstep.uasset b/Content/AI/Elemental/NS_Water_Magic_Waterstep.uasset new file mode 100644 index 00000000..e44f9bbc Binary files /dev/null and b/Content/AI/Elemental/NS_Water_Magic_Waterstep.uasset differ diff --git a/Content/AI/Elemental/PoisonElemental/BP_PoisonElemental.uasset b/Content/AI/Elemental/PoisonElemental/BP_PoisonElemental.uasset new file mode 100644 index 00000000..6aa78b7d Binary files /dev/null and b/Content/AI/Elemental/PoisonElemental/BP_PoisonElemental.uasset differ diff --git a/Content/AI/Elemental/PoisonElemental/Curve_PoisonElemental_Table.uasset b/Content/AI/Elemental/PoisonElemental/Curve_PoisonElemental_Table.uasset new file mode 100644 index 00000000..d2f1bd6f Binary files /dev/null and b/Content/AI/Elemental/PoisonElemental/Curve_PoisonElemental_Table.uasset differ diff --git a/Content/AI/Elemental/PoisonElemental/DA_PoisonElementalDropItems.uasset b/Content/AI/Elemental/PoisonElemental/DA_PoisonElementalDropItems.uasset new file mode 100644 index 00000000..e1c63107 Binary files /dev/null and b/Content/AI/Elemental/PoisonElemental/DA_PoisonElementalDropItems.uasset differ diff --git a/Content/AI/Elemental/PoisonElemental/Skill/Identity/AM_PoisonElementalIdentity.uasset b/Content/AI/Elemental/PoisonElemental/Skill/Identity/AM_PoisonElementalIdentity.uasset new file mode 100644 index 00000000..965de78c Binary files /dev/null and b/Content/AI/Elemental/PoisonElemental/Skill/Identity/AM_PoisonElementalIdentity.uasset differ diff --git a/Content/AI/Elemental/WaterElemental/NS_Water_Magic_Wave1.uasset b/Content/AI/Elemental/WaterElemental/NS_Water_Magic_Wave1.uasset new file mode 100644 index 00000000..689d7b0b Binary files /dev/null and b/Content/AI/Elemental/WaterElemental/NS_Water_Magic_Wave1.uasset differ diff --git a/Content/AI/FireElemental/ABP_MJFire.uasset b/Content/AI/FireElemental/ABP_MJFire.uasset new file mode 100644 index 00000000..6b9e0e15 Binary files /dev/null and b/Content/AI/FireElemental/ABP_MJFire.uasset differ diff --git a/Content/AI/FireElemental/AM_FireMeleeAttack.uasset b/Content/AI/FireElemental/AM_FireMeleeAttack.uasset new file mode 100644 index 00000000..47a35ed4 Binary files /dev/null and b/Content/AI/FireElemental/AM_FireMeleeAttack.uasset differ diff --git a/Content/AI/FireElemental/AS_FireDeath.uasset b/Content/AI/FireElemental/AS_FireDeath.uasset new file mode 100644 index 00000000..421953c9 Binary files /dev/null and b/Content/AI/FireElemental/AS_FireDeath.uasset differ diff --git a/Content/AI/FireElemental/BPGA_MJActionFireMeleeAttack.uasset b/Content/AI/FireElemental/BPGA_MJActionFireMeleeAttack.uasset new file mode 100644 index 00000000..e37cf20f Binary files /dev/null and b/Content/AI/FireElemental/BPGA_MJActionFireMeleeAttack.uasset differ diff --git a/Content/AI/FireElemental/BPGA_MJFireMeleeAttack.uasset b/Content/AI/FireElemental/BPGA_MJFireMeleeAttack.uasset new file mode 100644 index 00000000..b3cd60e4 Binary files /dev/null and b/Content/AI/FireElemental/BPGA_MJFireMeleeAttack.uasset differ diff --git a/Content/AI/FireElemental/BP_FireElemental.uasset b/Content/AI/FireElemental/BP_FireElemental.uasset new file mode 100644 index 00000000..1a44cd9b Binary files /dev/null and b/Content/AI/FireElemental/BP_FireElemental.uasset differ diff --git a/Content/AI/FireElemental/BS_FireLocomotion.uasset b/Content/AI/FireElemental/BS_FireLocomotion.uasset new file mode 100644 index 00000000..01108baf Binary files /dev/null and b/Content/AI/FireElemental/BS_FireLocomotion.uasset differ diff --git a/Content/AI/FireElemental/Curve_FireElemental_Table.uasset b/Content/AI/FireElemental/Curve_FireElemental_Table.uasset new file mode 100644 index 00000000..2b166c7b Binary files /dev/null and b/Content/AI/FireElemental/Curve_FireElemental_Table.uasset differ diff --git a/Content/AI/ForestCreture/ABP_MJForestCreature.uasset b/Content/AI/ForestCreture/ABP_MJForestCreature.uasset new file mode 100644 index 00000000..9e711243 Binary files /dev/null and b/Content/AI/ForestCreture/ABP_MJForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Barrage.uasset b/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Barrage.uasset new file mode 100644 index 00000000..c040ecd4 Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Barrage.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Power.uasset b/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Power.uasset new file mode 100644 index 00000000..77168c5b Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Anim_SP_Blast_Power.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Anim_SP_Charge_On.uasset b/Content/AI/ForestCreture/Animation/Anim_SP_Charge_On.uasset new file mode 100644 index 00000000..30b0fd37 Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Anim_SP_Charge_On.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Idle.uasset b/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Idle.uasset new file mode 100644 index 00000000..5aaa97dd Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Idle.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Over.uasset b/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Over.uasset new file mode 100644 index 00000000..e37a90ef Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Anim_SP_Charging_Over.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Enemy_Jump_Attack.uasset b/Content/AI/ForestCreture/Animation/Enemy_Jump_Attack.uasset new file mode 100644 index 00000000..41cf26a8 Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Enemy_Jump_Attack.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Enemy_Scream.uasset b/Content/AI/ForestCreture/Animation/Enemy_Scream.uasset new file mode 100644 index 00000000..04afcbd7 Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Enemy_Scream.uasset differ diff --git a/Content/AI/ForestCreture/Animation/LeftPunch.uasset b/Content/AI/ForestCreture/Animation/LeftPunch.uasset new file mode 100644 index 00000000..d004b4fe Binary files /dev/null and b/Content/AI/ForestCreture/Animation/LeftPunch.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Montage/AM_CreatureDeath.uasset b/Content/AI/ForestCreture/Animation/Montage/AM_CreatureDeath.uasset new file mode 100644 index 00000000..b86db8c9 Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Montage/AM_CreatureDeath.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Montage/AM_CreatureScream.uasset b/Content/AI/ForestCreture/Animation/Montage/AM_CreatureScream.uasset new file mode 100644 index 00000000..f5a5b06d Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Montage/AM_CreatureScream.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Montage/AM_Damaged.uasset b/Content/AI/ForestCreture/Animation/Montage/AM_Damaged.uasset new file mode 100644 index 00000000..fb17a62e Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Montage/AM_Damaged.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Montage/AM_Forest_Creature_Attack.uasset b/Content/AI/ForestCreture/Animation/Montage/AM_Forest_Creature_Attack.uasset new file mode 100644 index 00000000..87235e8f Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Montage/AM_Forest_Creature_Attack.uasset differ diff --git a/Content/AI/ForestCreture/Animation/Montage/AM_SP_Charge.uasset b/Content/AI/ForestCreture/Animation/Montage/AM_SP_Charge.uasset new file mode 100644 index 00000000..1e050e6f Binary files /dev/null and b/Content/AI/ForestCreture/Animation/Montage/AM_SP_Charge.uasset differ diff --git a/Content/AI/ForestCreture/BB_MJForestCreature.uasset b/Content/AI/ForestCreture/BB_MJForestCreature.uasset new file mode 100644 index 00000000..b59c3074 Binary files /dev/null and b/Content/AI/ForestCreture/BB_MJForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/BPGA_ActionAppear_ForestCreature.uasset b/Content/AI/ForestCreture/BPGA_ActionAppear_ForestCreature.uasset new file mode 100644 index 00000000..c66adea5 Binary files /dev/null and b/Content/AI/ForestCreture/BPGA_ActionAppear_ForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/BPGA_ActionDamage_Forest.uasset b/Content/AI/ForestCreture/BPGA_ActionDamage_Forest.uasset new file mode 100644 index 00000000..92c02b8e Binary files /dev/null and b/Content/AI/ForestCreture/BPGA_ActionDamage_Forest.uasset differ diff --git a/Content/AI/ForestCreture/BPGA_ActionDeath_Forest.uasset b/Content/AI/ForestCreture/BPGA_ActionDeath_Forest.uasset new file mode 100644 index 00000000..85815677 Binary files /dev/null and b/Content/AI/ForestCreture/BPGA_ActionDeath_Forest.uasset differ diff --git a/Content/AI/ForestCreture/BP_MJForestCreature.uasset b/Content/AI/ForestCreture/BP_MJForestCreature.uasset new file mode 100644 index 00000000..a2ab35f5 Binary files /dev/null and b/Content/AI/ForestCreture/BP_MJForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/BP_MJForestCreatureAIContoller.uasset b/Content/AI/ForestCreture/BP_MJForestCreatureAIContoller.uasset new file mode 100644 index 00000000..64b82492 Binary files /dev/null and b/Content/AI/ForestCreture/BP_MJForestCreatureAIContoller.uasset differ diff --git a/Content/AI/ForestCreture/BS_MJCreatureLocomotion.uasset b/Content/AI/ForestCreture/BS_MJCreatureLocomotion.uasset new file mode 100644 index 00000000..abd35569 Binary files /dev/null and b/Content/AI/ForestCreture/BS_MJCreatureLocomotion.uasset differ diff --git a/Content/AI/ForestCreture/BT/BT_ForestCreatureBase.uasset b/Content/AI/ForestCreture/BT/BT_ForestCreatureBase.uasset new file mode 100644 index 00000000..c5dfe0c3 Binary files /dev/null and b/Content/AI/ForestCreture/BT/BT_ForestCreatureBase.uasset differ diff --git a/Content/AI/ForestCreture/BT/BT_Phase_01.uasset b/Content/AI/ForestCreture/BT/BT_Phase_01.uasset new file mode 100644 index 00000000..53c5d3d6 Binary files /dev/null and b/Content/AI/ForestCreture/BT/BT_Phase_01.uasset differ diff --git a/Content/AI/ForestCreture/Curve_ForestCreature_Table.uasset b/Content/AI/ForestCreture/Curve_ForestCreature_Table.uasset new file mode 100644 index 00000000..bcedc04a Binary files /dev/null and b/Content/AI/ForestCreture/Curve_ForestCreature_Table.uasset differ diff --git a/Content/AI/ForestCreture/DT_ForestCreatureStateAbility.uasset b/Content/AI/ForestCreture/DT_ForestCreatureStateAbility.uasset new file mode 100644 index 00000000..0fde598d Binary files /dev/null and b/Content/AI/ForestCreture/DT_ForestCreatureStateAbility.uasset differ diff --git a/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_ActionIdentityForestCreature.uasset b/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_ActionIdentityForestCreature.uasset new file mode 100644 index 00000000..15322748 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_ActionIdentityForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_IdentityForestCreature.uasset b/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_IdentityForestCreature.uasset new file mode 100644 index 00000000..2d539d75 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/IdentitySkill/BPGA_IdentityForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/Skill/IdentitySkill/Curve_Identity_ForestCreature_Table.uasset b/Content/AI/ForestCreture/Skill/IdentitySkill/Curve_Identity_ForestCreature_Table.uasset new file mode 100644 index 00000000..d0315286 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/IdentitySkill/Curve_Identity_ForestCreature_Table.uasset differ diff --git a/Content/AI/ForestCreture/Skill/IdentitySkill/DT_IdentityForestCreatureAbility.uasset b/Content/AI/ForestCreture/Skill/IdentitySkill/DT_IdentityForestCreatureAbility.uasset new file mode 100644 index 00000000..b9b9a37f Binary files /dev/null and b/Content/AI/ForestCreture/Skill/IdentitySkill/DT_IdentityForestCreatureAbility.uasset differ diff --git a/Content/AI/ForestCreture/Skill/Normal/BPGA_ActionNormalForestCreature.uasset b/Content/AI/ForestCreture/Skill/Normal/BPGA_ActionNormalForestCreature.uasset new file mode 100644 index 00000000..b555b061 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/Normal/BPGA_ActionNormalForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/Skill/Normal/BPGA_NormalForestCreature.uasset b/Content/AI/ForestCreture/Skill/Normal/BPGA_NormalForestCreature.uasset new file mode 100644 index 00000000..e163ecc7 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/Normal/BPGA_NormalForestCreature.uasset differ diff --git a/Content/AI/ForestCreture/Skill/Normal/Curve_ForestCreature_SkillTable.uasset b/Content/AI/ForestCreture/Skill/Normal/Curve_ForestCreature_SkillTable.uasset new file mode 100644 index 00000000..22b6a752 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/Normal/Curve_ForestCreature_SkillTable.uasset differ diff --git a/Content/AI/ForestCreture/Skill/Normal/DT_ForestCreatureSkillAbility.uasset b/Content/AI/ForestCreture/Skill/Normal/DT_ForestCreatureSkillAbility.uasset new file mode 100644 index 00000000..3f4744fc Binary files /dev/null and b/Content/AI/ForestCreture/Skill/Normal/DT_ForestCreatureSkillAbility.uasset differ diff --git a/Content/AI/ForestCreture/Skill/Normal/Enemy_Jump_Attack_Montage.uasset b/Content/AI/ForestCreture/Skill/Normal/Enemy_Jump_Attack_Montage.uasset new file mode 100644 index 00000000..0e336013 Binary files /dev/null and b/Content/AI/ForestCreture/Skill/Normal/Enemy_Jump_Attack_Montage.uasset differ diff --git a/Content/AI/Golem/ABP_MJGolem.uasset b/Content/AI/Golem/ABP_MJGolem.uasset new file mode 100644 index 00000000..df9795f2 Binary files /dev/null and b/Content/AI/Golem/ABP_MJGolem.uasset differ diff --git a/Content/AI/Golem/AM_GolemAppear.uasset b/Content/AI/Golem/AM_GolemAppear.uasset new file mode 100644 index 00000000..bbee7cc7 Binary files /dev/null and b/Content/AI/Golem/AM_GolemAppear.uasset differ diff --git a/Content/AI/Golem/AM_GolemDead.uasset b/Content/AI/Golem/AM_GolemDead.uasset new file mode 100644 index 00000000..8e22cfba Binary files /dev/null and b/Content/AI/Golem/AM_GolemDead.uasset differ diff --git a/Content/AI/Golem/AM_GolemHurt.uasset b/Content/AI/Golem/AM_GolemHurt.uasset new file mode 100644 index 00000000..b3e80dec Binary files /dev/null and b/Content/AI/Golem/AM_GolemHurt.uasset differ diff --git a/Content/AI/Golem/BS_GolemLocomotion.uasset b/Content/AI/Golem/BS_GolemLocomotion.uasset new file mode 100644 index 00000000..b0cdffc0 Binary files /dev/null and b/Content/AI/Golem/BS_GolemLocomotion.uasset differ diff --git a/Content/AI/Golem/EarthGolem/BP_EarthGolem.uasset b/Content/AI/Golem/EarthGolem/BP_EarthGolem.uasset new file mode 100644 index 00000000..b17ed5d4 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/BP_EarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Curve_EarthGolem_Table.uasset b/Content/AI/Golem/EarthGolem/Curve_EarthGolem_Table.uasset new file mode 100644 index 00000000..b3d673e4 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Curve_EarthGolem_Table.uasset differ diff --git a/Content/AI/Golem/EarthGolem/DA_EarthGolemDropItems.uasset b/Content/AI/Golem/EarthGolem/DA_EarthGolemDropItems.uasset new file mode 100644 index 00000000..d4c8e793 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/DA_EarthGolemDropItems.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/AM_EarthGolemIdentity.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/AM_EarthGolemIdentity.uasset new file mode 100644 index 00000000..1b7f73d3 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/AM_EarthGolemIdentity.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJActionIdentityEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJActionIdentityEarthGolem.uasset new file mode 100644 index 00000000..934657c7 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJActionIdentityEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJIdentityEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJIdentityEarthGolem.uasset new file mode 100644 index 00000000..123cd23e Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGA_MJIdentityEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/BPGE_DamageIdentityEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGE_DamageIdentityEarthGolem.uasset new file mode 100644 index 00000000..9f96893e Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/BPGE_DamageIdentityEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/Curve_IdentityEarthGolem_Table.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/Curve_IdentityEarthGolem_Table.uasset new file mode 100644 index 00000000..770d7720 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/Curve_IdentityEarthGolem_Table.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/DT_IdentityEarthGolemAbility.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/DT_IdentityEarthGolemAbility.uasset new file mode 100644 index 00000000..449fb28c Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/DT_IdentityEarthGolemAbility.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Identity/NS_EarthGolemIdentity.uasset b/Content/AI/Golem/EarthGolem/Skill/Identity/NS_EarthGolemIdentity.uasset new file mode 100644 index 00000000..352b457d Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Identity/NS_EarthGolemIdentity.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/AM_EarthGolemNormal.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/AM_EarthGolemNormal.uasset new file mode 100644 index 00000000..fc35eed0 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/AM_EarthGolemNormal.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJActionNormalEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJActionNormalEarthGolem.uasset new file mode 100644 index 00000000..1546004a Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJActionNormalEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJNormalEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJNormalEarthGolem.uasset new file mode 100644 index 00000000..fd41fce1 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGA_MJNormalEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/BPGE_DamageNormalEarthGolem.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGE_DamageNormalEarthGolem.uasset new file mode 100644 index 00000000..e928a856 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/BPGE_DamageNormalEarthGolem.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/Curve_NormalEarthGolem_Table.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/Curve_NormalEarthGolem_Table.uasset new file mode 100644 index 00000000..b435c465 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/Curve_NormalEarthGolem_Table.uasset differ diff --git a/Content/AI/Golem/EarthGolem/Skill/Normal/DT_NormalEarthGolemAbility.uasset b/Content/AI/Golem/EarthGolem/Skill/Normal/DT_NormalEarthGolemAbility.uasset new file mode 100644 index 00000000..e359f753 Binary files /dev/null and b/Content/AI/Golem/EarthGolem/Skill/Normal/DT_NormalEarthGolemAbility.uasset differ diff --git a/Content/AI/Golem/FireGolem/BP_FireGolem.uasset b/Content/AI/Golem/FireGolem/BP_FireGolem.uasset new file mode 100644 index 00000000..22c6f7bb Binary files /dev/null and b/Content/AI/Golem/FireGolem/BP_FireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Curve_FireGolem_Table.uasset b/Content/AI/Golem/FireGolem/Curve_FireGolem_Table.uasset new file mode 100644 index 00000000..bec9f924 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Curve_FireGolem_Table.uasset differ diff --git a/Content/AI/Golem/FireGolem/DA_FireGolemDropItems.uasset b/Content/AI/Golem/FireGolem/DA_FireGolemDropItems.uasset new file mode 100644 index 00000000..6852dad6 Binary files /dev/null and b/Content/AI/Golem/FireGolem/DA_FireGolemDropItems.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/AM_FireGolemIdentity.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/AM_FireGolemIdentity.uasset new file mode 100644 index 00000000..d7e2788b Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/AM_FireGolemIdentity.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_ExplosionFireGolemIdentity.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_ExplosionFireGolemIdentity.uasset new file mode 100644 index 00000000..72603568 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_ExplosionFireGolemIdentity.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJActionIdentityFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJActionIdentityFireGolem.uasset new file mode 100644 index 00000000..7f52e092 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJActionIdentityFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJIdentityFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJIdentityFireGolem.uasset new file mode 100644 index 00000000..945172f1 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MJIdentityFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MarkerFireGolemIdentityProjectile.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MarkerFireGolemIdentityProjectile.uasset new file mode 100644 index 00000000..f5c479ae Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGA_MarkerFireGolemIdentityProjectile.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_DamageIdentityFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_DamageIdentityFireGolem.uasset new file mode 100644 index 00000000..670ff327 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_DamageIdentityFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_ExplosionDamageFireGolemIdentity.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_ExplosionDamageFireGolemIdentity.uasset new file mode 100644 index 00000000..ee1e6707 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BPGE_ExplosionDamageFireGolemIdentity.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BP_FireGolemIdentityProjectile.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BP_FireGolemIdentityProjectile.uasset new file mode 100644 index 00000000..960066bb Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BP_FireGolemIdentityProjectile.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/BP_MovementFireGolemIdentity.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/BP_MovementFireGolemIdentity.uasset new file mode 100644 index 00000000..ac71cfab Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/BP_MovementFireGolemIdentity.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/Curve_IdentityFireGolem_Table.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/Curve_IdentityFireGolem_Table.uasset new file mode 100644 index 00000000..7795467a Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/Curve_IdentityFireGolem_Table.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/DT_IdentityFireGolemAbility.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/DT_IdentityFireGolemAbility.uasset new file mode 100644 index 00000000..c2306c40 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/DT_IdentityFireGolemAbility.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentity.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentity.uasset new file mode 100644 index 00000000..7d5f6d19 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentity.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentityMark.uasset b/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentityMark.uasset new file mode 100644 index 00000000..424a57b9 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Identity/NS_FireGolemIdentityMark.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/AM_FireGolemNormal.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/AM_FireGolemNormal.uasset new file mode 100644 index 00000000..6933606c Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/AM_FireGolemNormal.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJActionNormalFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJActionNormalFireGolem.uasset new file mode 100644 index 00000000..32019860 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJActionNormalFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJNormalFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJNormalFireGolem.uasset new file mode 100644 index 00000000..5b5ff270 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/BPGA_MJNormalFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/BPGE_DamageNormalFireGolem.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/BPGE_DamageNormalFireGolem.uasset new file mode 100644 index 00000000..02e35b03 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/BPGE_DamageNormalFireGolem.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/Curve_NormalFireGolem_Table.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/Curve_NormalFireGolem_Table.uasset new file mode 100644 index 00000000..85153d17 Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/Curve_NormalFireGolem_Table.uasset differ diff --git a/Content/AI/Golem/FireGolem/Skill/Normal/DT_NormalFireGolemAbility.uasset b/Content/AI/Golem/FireGolem/Skill/Normal/DT_NormalFireGolemAbility.uasset new file mode 100644 index 00000000..7af425dd Binary files /dev/null and b/Content/AI/Golem/FireGolem/Skill/Normal/DT_NormalFireGolemAbility.uasset differ diff --git a/Content/AI/Golem/GA_GolemStateAbility.uasset b/Content/AI/Golem/GA_GolemStateAbility.uasset new file mode 100644 index 00000000..fb7c0fa7 Binary files /dev/null and b/Content/AI/Golem/GA_GolemStateAbility.uasset differ diff --git a/Content/AI/Golem/IceGolem/BP_IceGolem.uasset b/Content/AI/Golem/IceGolem/BP_IceGolem.uasset new file mode 100644 index 00000000..34baaf40 Binary files /dev/null and b/Content/AI/Golem/IceGolem/BP_IceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Curve_IceGolem_Table.uasset b/Content/AI/Golem/IceGolem/Curve_IceGolem_Table.uasset new file mode 100644 index 00000000..fc7524b4 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Curve_IceGolem_Table.uasset differ diff --git a/Content/AI/Golem/IceGolem/DA_IceGolemDropItems.uasset b/Content/AI/Golem/IceGolem/DA_IceGolemDropItems.uasset new file mode 100644 index 00000000..1f583037 Binary files /dev/null and b/Content/AI/Golem/IceGolem/DA_IceGolemDropItems.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/AM_IceGolemIdentity.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/AM_IceGolemIdentity.uasset new file mode 100644 index 00000000..2e2694c5 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/AM_IceGolemIdentity.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJActionIdentityIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJActionIdentityIceGolem.uasset new file mode 100644 index 00000000..514dd789 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJActionIdentityIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJIdentityIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJIdentityIceGolem.uasset new file mode 100644 index 00000000..c82526b4 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/BPGA_MJIdentityIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/BPGC_StatusFrozen.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/BPGC_StatusFrozen.uasset new file mode 100644 index 00000000..fda7ad13 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/BPGC_StatusFrozen.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_DamageIdentityIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_DamageIdentityIceGolem.uasset new file mode 100644 index 00000000..14ee59be Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_DamageIdentityIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_StatusFrozen.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_StatusFrozen.uasset new file mode 100644 index 00000000..e1e848df Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/BPGE_StatusFrozen.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/Curve_IdentityIceGolem_Table.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/Curve_IdentityIceGolem_Table.uasset new file mode 100644 index 00000000..18101aa3 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/Curve_IdentityIceGolem_Table.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/DT_IdentityIceGolemAbility.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/DT_IdentityIceGolemAbility.uasset new file mode 100644 index 00000000..7d351492 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/DT_IdentityIceGolemAbility.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceFrozenTest.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceFrozenTest.uasset new file mode 100644 index 00000000..38f9a6c3 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceFrozenTest.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemFrozen.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemFrozen.uasset new file mode 100644 index 00000000..9c3ffda4 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemFrozen.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemIdentity.uasset b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemIdentity.uasset new file mode 100644 index 00000000..0c4e665d Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Identity/NS_IceGolemIdentity.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/AM_IceGolemNormal.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/AM_IceGolemNormal.uasset new file mode 100644 index 00000000..5d661244 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/AM_IceGolemNormal.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJActionNormalIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJActionNormalIceGolem.uasset new file mode 100644 index 00000000..89c88df0 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJActionNormalIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJNormalIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJNormalIceGolem.uasset new file mode 100644 index 00000000..af54a725 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/BPGA_MJNormalIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/BPGE_DamageNormalIceGolem.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/BPGE_DamageNormalIceGolem.uasset new file mode 100644 index 00000000..be1fbc38 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/BPGE_DamageNormalIceGolem.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/Curve_NormalIceGolem_Table.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/Curve_NormalIceGolem_Table.uasset new file mode 100644 index 00000000..14b8a1b5 Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/Curve_NormalIceGolem_Table.uasset differ diff --git a/Content/AI/Golem/IceGolem/Skill/Normal/DT_NormalIceGolemAbility.uasset b/Content/AI/Golem/IceGolem/Skill/Normal/DT_NormalIceGolemAbility.uasset new file mode 100644 index 00000000..f1e79e8f Binary files /dev/null and b/Content/AI/Golem/IceGolem/Skill/Normal/DT_NormalIceGolemAbility.uasset differ diff --git a/Content/AI/Golem/MJGA_ActionAppearGolem.uasset b/Content/AI/Golem/MJGA_ActionAppearGolem.uasset new file mode 100644 index 00000000..f72f5b83 Binary files /dev/null and b/Content/AI/Golem/MJGA_ActionAppearGolem.uasset differ diff --git a/Content/AI/Golem/MJGA_ActionDamageGolem.uasset b/Content/AI/Golem/MJGA_ActionDamageGolem.uasset new file mode 100644 index 00000000..fdff6a10 Binary files /dev/null and b/Content/AI/Golem/MJGA_ActionDamageGolem.uasset differ diff --git a/Content/AI/Golem/MJGA_ActionDeadGolem.uasset b/Content/AI/Golem/MJGA_ActionDeadGolem.uasset new file mode 100644 index 00000000..29ad480d Binary files /dev/null and b/Content/AI/Golem/MJGA_ActionDeadGolem.uasset differ diff --git a/Content/AI/SpiderMinion/ABP_SpiderEgg.uasset b/Content/AI/SpiderMinion/ABP_SpiderEgg.uasset new file mode 100644 index 00000000..7394ae21 Binary files /dev/null and b/Content/AI/SpiderMinion/ABP_SpiderEgg.uasset differ diff --git a/Content/AI/SpiderMinion/ABP_SpiderMinion.uasset b/Content/AI/SpiderMinion/ABP_SpiderMinion.uasset new file mode 100644 index 00000000..7f75c051 Binary files /dev/null and b/Content/AI/SpiderMinion/ABP_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/BPGA_ActionAppear_SpiderMinioin.uasset b/Content/AI/SpiderMinion/BPGA_ActionAppear_SpiderMinioin.uasset new file mode 100644 index 00000000..3e2c964a Binary files /dev/null and b/Content/AI/SpiderMinion/BPGA_ActionAppear_SpiderMinioin.uasset differ diff --git a/Content/AI/SpiderMinion/BPGA_ActionDamage_SpiderMinion.uasset b/Content/AI/SpiderMinion/BPGA_ActionDamage_SpiderMinion.uasset new file mode 100644 index 00000000..1ab8dfbf Binary files /dev/null and b/Content/AI/SpiderMinion/BPGA_ActionDamage_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/BPGA_ActionDeath_SpiderMinion.uasset b/Content/AI/SpiderMinion/BPGA_ActionDeath_SpiderMinion.uasset new file mode 100644 index 00000000..08352d98 Binary files /dev/null and b/Content/AI/SpiderMinion/BPGA_ActionDeath_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/BP_MJSpiderMinionAIController.uasset b/Content/AI/SpiderMinion/BP_MJSpiderMinionAIController.uasset new file mode 100644 index 00000000..32a931bf Binary files /dev/null and b/Content/AI/SpiderMinion/BP_MJSpiderMinionAIController.uasset differ diff --git a/Content/AI/SpiderMinion/BP_SpiderCharacter.uasset b/Content/AI/SpiderMinion/BP_SpiderCharacter.uasset new file mode 100644 index 00000000..5316408b Binary files /dev/null and b/Content/AI/SpiderMinion/BP_SpiderCharacter.uasset differ diff --git a/Content/AI/SpiderMinion/BP_SpiderEgg.uasset b/Content/AI/SpiderMinion/BP_SpiderEgg.uasset new file mode 100644 index 00000000..d60b6839 Binary files /dev/null and b/Content/AI/SpiderMinion/BP_SpiderEgg.uasset differ diff --git a/Content/AI/SpiderMinion/BS_SpiderMinionLocomotion.uasset b/Content/AI/SpiderMinion/BS_SpiderMinionLocomotion.uasset new file mode 100644 index 00000000..15574d41 Binary files /dev/null and b/Content/AI/SpiderMinion/BS_SpiderMinionLocomotion.uasset differ diff --git a/Content/AI/SpiderMinion/BT/BT_SpiderMinion.uasset b/Content/AI/SpiderMinion/BT/BT_SpiderMinion.uasset new file mode 100644 index 00000000..02e1c572 Binary files /dev/null and b/Content/AI/SpiderMinion/BT/BT_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/Curve_Spider_Table.uasset b/Content/AI/SpiderMinion/Curve_Spider_Table.uasset new file mode 100644 index 00000000..296c1443 Binary files /dev/null and b/Content/AI/SpiderMinion/Curve_Spider_Table.uasset differ diff --git a/Content/AI/SpiderMinion/DT_SpiderMinionStateAbility.uasset b/Content/AI/SpiderMinion/DT_SpiderMinionStateAbility.uasset new file mode 100644 index 00000000..1f56f36b Binary files /dev/null and b/Content/AI/SpiderMinion/DT_SpiderMinionStateAbility.uasset differ diff --git a/Content/AI/SpiderMinion/Montage/AM_Appear_SpiderMinion.uasset b/Content/AI/SpiderMinion/Montage/AM_Appear_SpiderMinion.uasset new file mode 100644 index 00000000..8c7fb7b7 Binary files /dev/null and b/Content/AI/SpiderMinion/Montage/AM_Appear_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/Montage/AM_Attack_SpiderMinion.uasset b/Content/AI/SpiderMinion/Montage/AM_Attack_SpiderMinion.uasset new file mode 100644 index 00000000..8a71b590 Binary files /dev/null and b/Content/AI/SpiderMinion/Montage/AM_Attack_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/Montage/AM_Dead_SpiderMinion.uasset b/Content/AI/SpiderMinion/Montage/AM_Dead_SpiderMinion.uasset new file mode 100644 index 00000000..a4906596 Binary files /dev/null and b/Content/AI/SpiderMinion/Montage/AM_Dead_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/Montage/AM_Hit_SpiderMinion.uasset b/Content/AI/SpiderMinion/Montage/AM_Hit_SpiderMinion.uasset new file mode 100644 index 00000000..873a0be7 Binary files /dev/null and b/Content/AI/SpiderMinion/Montage/AM_Hit_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/SKill/Normal/BPGA_ActionNormalSpiderMinion.uasset b/Content/AI/SpiderMinion/SKill/Normal/BPGA_ActionNormalSpiderMinion.uasset new file mode 100644 index 00000000..500599d7 Binary files /dev/null and b/Content/AI/SpiderMinion/SKill/Normal/BPGA_ActionNormalSpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/SKill/Normal/BPGA_Normal_SpiderMinion.uasset b/Content/AI/SpiderMinion/SKill/Normal/BPGA_Normal_SpiderMinion.uasset new file mode 100644 index 00000000..06e56d70 Binary files /dev/null and b/Content/AI/SpiderMinion/SKill/Normal/BPGA_Normal_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/SKill/Normal/Curve_Normal_SpiderMinion.uasset b/Content/AI/SpiderMinion/SKill/Normal/Curve_Normal_SpiderMinion.uasset new file mode 100644 index 00000000..a25c9516 Binary files /dev/null and b/Content/AI/SpiderMinion/SKill/Normal/Curve_Normal_SpiderMinion.uasset differ diff --git a/Content/AI/SpiderMinion/SKill/Normal/DT_NormalSpiderMinionAbility.uasset b/Content/AI/SpiderMinion/SKill/Normal/DT_NormalSpiderMinionAbility.uasset new file mode 100644 index 00000000..f412a8af Binary files /dev/null and b/Content/AI/SpiderMinion/SKill/Normal/DT_NormalSpiderMinionAbility.uasset differ diff --git a/Content/AI/StoneElemental/ABP_MJStone.uasset b/Content/AI/StoneElemental/ABP_MJStone.uasset new file mode 100644 index 00000000..d4be719e Binary files /dev/null and b/Content/AI/StoneElemental/ABP_MJStone.uasset differ diff --git a/Content/AI/StoneElemental/AM_StoneAppear.uasset b/Content/AI/StoneElemental/AM_StoneAppear.uasset new file mode 100644 index 00000000..0f08914b Binary files /dev/null and b/Content/AI/StoneElemental/AM_StoneAppear.uasset differ diff --git a/Content/AI/StoneElemental/AM_StoneDamage.uasset b/Content/AI/StoneElemental/AM_StoneDamage.uasset new file mode 100644 index 00000000..2b2abc05 Binary files /dev/null and b/Content/AI/StoneElemental/AM_StoneDamage.uasset differ diff --git a/Content/AI/StoneElemental/AM_StoneDeath.uasset b/Content/AI/StoneElemental/AM_StoneDeath.uasset new file mode 100644 index 00000000..dcf86709 Binary files /dev/null and b/Content/AI/StoneElemental/AM_StoneDeath.uasset differ diff --git a/Content/AI/StoneElemental/Anim_StoneAppear.uasset b/Content/AI/StoneElemental/Anim_StoneAppear.uasset new file mode 100644 index 00000000..0606355c Binary files /dev/null and b/Content/AI/StoneElemental/Anim_StoneAppear.uasset differ diff --git a/Content/AI/StoneElemental/Anim_StoneDeath.uasset b/Content/AI/StoneElemental/Anim_StoneDeath.uasset new file mode 100644 index 00000000..4a826f62 Binary files /dev/null and b/Content/AI/StoneElemental/Anim_StoneDeath.uasset differ diff --git a/Content/AI/StoneElemental/BP_StoneElemental.uasset b/Content/AI/StoneElemental/BP_StoneElemental.uasset new file mode 100644 index 00000000..0abbe099 Binary files /dev/null and b/Content/AI/StoneElemental/BP_StoneElemental.uasset differ diff --git a/Content/AI/StoneElemental/Curve_StoneElement_Table.uasset b/Content/AI/StoneElemental/Curve_StoneElement_Table.uasset new file mode 100644 index 00000000..2b872cf1 Binary files /dev/null and b/Content/AI/StoneElemental/Curve_StoneElement_Table.uasset differ diff --git a/Content/AI/StoneElemental/GA_StoneStateAbility.uasset b/Content/AI/StoneElemental/GA_StoneStateAbility.uasset new file mode 100644 index 00000000..9bb00b35 Binary files /dev/null and b/Content/AI/StoneElemental/GA_StoneStateAbility.uasset differ diff --git a/Content/AI/StoneElemental/MJGA_ActionAppearStone.uasset b/Content/AI/StoneElemental/MJGA_ActionAppearStone.uasset new file mode 100644 index 00000000..491d454f Binary files /dev/null and b/Content/AI/StoneElemental/MJGA_ActionAppearStone.uasset differ diff --git a/Content/AI/StoneElemental/MJGA_ActionDamageStone.uasset b/Content/AI/StoneElemental/MJGA_ActionDamageStone.uasset new file mode 100644 index 00000000..b4842508 Binary files /dev/null and b/Content/AI/StoneElemental/MJGA_ActionDamageStone.uasset differ diff --git a/Content/AI/StoneElemental/MJGA_ActionDeathStone.uasset b/Content/AI/StoneElemental/MJGA_ActionDeathStone.uasset new file mode 100644 index 00000000..920d07b7 Binary files /dev/null and b/Content/AI/StoneElemental/MJGA_ActionDeathStone.uasset differ diff --git a/Content/AI/StoneElemental/NS_Death.uasset b/Content/AI/StoneElemental/NS_Death.uasset new file mode 100644 index 00000000..8527fc0a Binary files /dev/null and b/Content/AI/StoneElemental/NS_Death.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/AM_StoneIdentity.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/AM_StoneIdentity.uasset new file mode 100644 index 00000000..4c829de3 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/AM_StoneIdentity.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJActionIdentityStone.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJActionIdentityStone.uasset new file mode 100644 index 00000000..d0828860 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJActionIdentityStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJIdentityStone.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJIdentityStone.uasset new file mode 100644 index 00000000..8144a97c Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGA_MJIdentityStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/BPGE_DamageIdentityStone.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGE_DamageIdentityStone.uasset new file mode 100644 index 00000000..363911e4 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/BPGE_DamageIdentityStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/Curve_IdentityStone_Table.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/Curve_IdentityStone_Table.uasset new file mode 100644 index 00000000..8c55b96d Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/Curve_IdentityStone_Table.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/DT_IdentityStoneAbility.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/DT_IdentityStoneAbility.uasset new file mode 100644 index 00000000..97b45024 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/DT_IdentityStoneAbility.uasset differ diff --git a/Content/AI/StoneElemental/Skill/IdentitySkill/NS_StoneIdentity.uasset b/Content/AI/StoneElemental/Skill/IdentitySkill/NS_StoneIdentity.uasset new file mode 100644 index 00000000..7a03a928 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/IdentitySkill/NS_StoneIdentity.uasset differ diff --git a/Content/AI/StoneElemental/Skill/Normal/AM_NormalStone.uasset b/Content/AI/StoneElemental/Skill/Normal/AM_NormalStone.uasset new file mode 100644 index 00000000..b8edd465 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/Normal/AM_NormalStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/Normal/BPGA_MJActionNormalStone.uasset b/Content/AI/StoneElemental/Skill/Normal/BPGA_MJActionNormalStone.uasset new file mode 100644 index 00000000..5485d266 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/Normal/BPGA_MJActionNormalStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/Normal/BPGA_MJNormalStone.uasset b/Content/AI/StoneElemental/Skill/Normal/BPGA_MJNormalStone.uasset new file mode 100644 index 00000000..c1b01af4 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/Normal/BPGA_MJNormalStone.uasset differ diff --git a/Content/AI/StoneElemental/Skill/Normal/Curve_NormalStone_Table.uasset b/Content/AI/StoneElemental/Skill/Normal/Curve_NormalStone_Table.uasset new file mode 100644 index 00000000..ca6bb387 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/Normal/Curve_NormalStone_Table.uasset differ diff --git a/Content/AI/StoneElemental/Skill/Normal/DT_NormalStoneAbility.uasset b/Content/AI/StoneElemental/Skill/Normal/DT_NormalStoneAbility.uasset new file mode 100644 index 00000000..95bc40e7 Binary files /dev/null and b/Content/AI/StoneElemental/Skill/Normal/DT_NormalStoneAbility.uasset differ diff --git a/Content/BP_Instance_0701.uasset b/Content/BP_Instance_0701.uasset index d0ba7f86..c477ac04 100644 Binary files a/Content/BP_Instance_0701.uasset and b/Content/BP_Instance_0701.uasset differ diff --git a/Content/Characters/Animation/ABP_MJPlayerAnimBlueprint.uasset b/Content/Characters/Animation/ABP_MJPlayerAnimBlueprint.uasset new file mode 100644 index 00000000..bcd05c8b Binary files /dev/null and b/Content/Characters/Animation/ABP_MJPlayerAnimBlueprint.uasset differ diff --git a/Content/Characters/Animation/ABP_MJPlayerAnimation.uasset b/Content/Characters/Animation/ABP_MJPlayerAnimation.uasset index faa6fac3..a060825a 100644 Binary files a/Content/Characters/Animation/ABP_MJPlayerAnimation.uasset and b/Content/Characters/Animation/ABP_MJPlayerAnimation.uasset differ diff --git a/Content/Characters/Animation/ABP_MJTestAnimation.uasset b/Content/Characters/Animation/ABP_MJTestAnimation.uasset index f17faf2e..2e267bc4 100644 Binary files a/Content/Characters/Animation/ABP_MJTestAnimation.uasset and b/Content/Characters/Animation/ABP_MJTestAnimation.uasset differ diff --git a/Content/Characters/Animation/AnimLayer/ABP_Test_Master.uasset b/Content/Characters/Animation/AnimLayer/ABP_Test_Master.uasset index 059b3f60..6319585c 100644 Binary files a/Content/Characters/Animation/AnimLayer/ABP_Test_Master.uasset and b/Content/Characters/Animation/AnimLayer/ABP_Test_Master.uasset differ diff --git a/Content/Characters/Animation/AnimLayer/ABP_Test_Sword.uasset b/Content/Characters/Animation/AnimLayer/ABP_Test_Sword.uasset index 19b9a7b4..188118ff 100644 Binary files a/Content/Characters/Animation/AnimLayer/ABP_Test_Sword.uasset and b/Content/Characters/Animation/AnimLayer/ABP_Test_Sword.uasset differ diff --git a/Content/Characters/Animation/BlendSpace/BS_RunLocomitoin.uasset b/Content/Characters/Animation/BlendSpace/BS_RunLocomitoin.uasset index b6e1ca63..80ccd0ad 100644 Binary files a/Content/Characters/Animation/BlendSpace/BS_RunLocomitoin.uasset and b/Content/Characters/Animation/BlendSpace/BS_RunLocomitoin.uasset differ diff --git a/Content/Characters/Animation/BlendSpace/BS_RunLocomotion.uasset b/Content/Characters/Animation/BlendSpace/BS_RunLocomotion.uasset new file mode 100644 index 00000000..d3f16cd2 Binary files /dev/null and b/Content/Characters/Animation/BlendSpace/BS_RunLocomotion.uasset differ diff --git a/Content/Characters/Animation/BlendSpace/BS_Run_Locomotion.uasset b/Content/Characters/Animation/BlendSpace/BS_Run_Locomotion.uasset index 5ffd011e..94ea081b 100644 Binary files a/Content/Characters/Animation/BlendSpace/BS_Run_Locomotion.uasset and b/Content/Characters/Animation/BlendSpace/BS_Run_Locomotion.uasset differ diff --git a/Content/Characters/Animation/BlendSpace/BS_Sword_Run_Locomotion.uasset b/Content/Characters/Animation/BlendSpace/BS_Sword_Run_Locomotion.uasset index e25413c5..5dd4aa51 100644 Binary files a/Content/Characters/Animation/BlendSpace/BS_Sword_Run_Locomotion.uasset and b/Content/Characters/Animation/BlendSpace/BS_Sword_Run_Locomotion.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Attack_A.uasset b/Content/Characters/Animation/Montage/AM_Attack_A.uasset index bc6e9221..9ad13c0c 100644 Binary files a/Content/Characters/Animation/Montage/AM_Attack_A.uasset and b/Content/Characters/Animation/Montage/AM_Attack_A.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Attack_B.uasset b/Content/Characters/Animation/Montage/AM_Attack_B.uasset index f795db35..9396cb40 100644 Binary files a/Content/Characters/Animation/Montage/AM_Attack_B.uasset and b/Content/Characters/Animation/Montage/AM_Attack_B.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Attack_C.uasset b/Content/Characters/Animation/Montage/AM_Attack_C.uasset index 3636bf10..123bab64 100644 Binary files a/Content/Characters/Animation/Montage/AM_Attack_C.uasset and b/Content/Characters/Animation/Montage/AM_Attack_C.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Attack_Q.uasset b/Content/Characters/Animation/Montage/AM_Attack_Q.uasset index 0376d3dc..019108ae 100644 Binary files a/Content/Characters/Animation/Montage/AM_Attack_Q.uasset and b/Content/Characters/Animation/Montage/AM_Attack_Q.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Attack_W.uasset b/Content/Characters/Animation/Montage/AM_Attack_W.uasset index 23d6bcd7..0fc9b8ee 100644 Binary files a/Content/Characters/Animation/Montage/AM_Attack_W.uasset and b/Content/Characters/Animation/Montage/AM_Attack_W.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Laugh.uasset b/Content/Characters/Animation/Montage/AM_Laugh.uasset index 3cf7f545..368072d4 100644 Binary files a/Content/Characters/Animation/Montage/AM_Laugh.uasset and b/Content/Characters/Animation/Montage/AM_Laugh.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Player_Attack_01.uasset b/Content/Characters/Animation/Montage/AM_Player_Attack_01.uasset new file mode 100644 index 00000000..e6ef8d59 Binary files /dev/null and b/Content/Characters/Animation/Montage/AM_Player_Attack_01.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Player_Attack_02.uasset b/Content/Characters/Animation/Montage/AM_Player_Attack_02.uasset new file mode 100644 index 00000000..cbd9a8e7 Binary files /dev/null and b/Content/Characters/Animation/Montage/AM_Player_Attack_02.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Player_Attack_03.uasset b/Content/Characters/Animation/Montage/AM_Player_Attack_03.uasset new file mode 100644 index 00000000..3cce00af Binary files /dev/null and b/Content/Characters/Animation/Montage/AM_Player_Attack_03.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Player_Attack_04.uasset b/Content/Characters/Animation/Montage/AM_Player_Attack_04.uasset new file mode 100644 index 00000000..ee9471d4 Binary files /dev/null and b/Content/Characters/Animation/Montage/AM_Player_Attack_04.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_Point.uasset b/Content/Characters/Animation/Montage/AM_Point.uasset index 7f3e221f..33832975 100644 Binary files a/Content/Characters/Animation/Montage/AM_Point.uasset and b/Content/Characters/Animation/Montage/AM_Point.uasset differ diff --git a/Content/Characters/Animation/Montage/AM_TEST.uasset b/Content/Characters/Animation/Montage/AM_TEST.uasset index ee1ae1a3..1f381ec1 100644 Binary files a/Content/Characters/Animation/Montage/AM_TEST.uasset and b/Content/Characters/Animation/Montage/AM_TEST.uasset differ diff --git a/Content/Characters/Animation/Montage/Primary_Attack_Fast_D_Montage.uasset b/Content/Characters/Animation/Montage/Primary_Attack_Fast_D_Montage.uasset index 17a757d7..c32d4e14 100644 Binary files a/Content/Characters/Animation/Montage/Primary_Attack_Fast_D_Montage.uasset and b/Content/Characters/Animation/Montage/Primary_Attack_Fast_D_Montage.uasset differ diff --git a/Content/Characters/GA/Attack/BPGA_Catastrophe.uasset b/Content/Characters/GA/Attack/BPGA_Catastrophe.uasset deleted file mode 100644 index 181d8835..00000000 Binary files a/Content/Characters/GA/Attack/BPGA_Catastrophe.uasset and /dev/null differ diff --git a/Content/Characters/GA/Attack/BPGA_MJBasicMeleeAttack.uasset b/Content/Characters/GA/Attack/BPGA_MJBasicMeleeAttack.uasset deleted file mode 100644 index 404c2070..00000000 Binary files a/Content/Characters/GA/Attack/BPGA_MJBasicMeleeAttack.uasset and /dev/null differ diff --git a/Content/Characters/GA/Attack/BPGA_MJMeleeHitCheck.uasset b/Content/Characters/GA/Attack/BPGA_MJMeleeHitCheck.uasset deleted file mode 100644 index 45132daf..00000000 Binary files a/Content/Characters/GA/Attack/BPGA_MJMeleeHitCheck.uasset and /dev/null differ diff --git a/Content/Characters/GA/BPGA_Equip_Test.uasset b/Content/Characters/GA/BPGA_Equip_Test.uasset deleted file mode 100644 index 610df4a3..00000000 Binary files a/Content/Characters/GA/BPGA_Equip_Test.uasset and /dev/null differ diff --git a/Content/Characters/GA/BPGA_Test_Equip_Base.uasset b/Content/Characters/GA/BPGA_Test_Equip_Base.uasset deleted file mode 100644 index 0e565654..00000000 Binary files a/Content/Characters/GA/BPGA_Test_Equip_Base.uasset and /dev/null differ diff --git a/Content/Characters/GA/BPGA_Unequip_Weapon.uasset b/Content/Characters/GA/BPGA_Unequip_Weapon.uasset deleted file mode 100644 index 194db0ee..00000000 Binary files a/Content/Characters/GA/BPGA_Unequip_Weapon.uasset and /dev/null differ diff --git a/Content/Characters/GA/BPGA_Weapon_Sword.uasset b/Content/Characters/GA/BPGA_Weapon_Sword.uasset deleted file mode 100644 index deb1d72a..00000000 Binary files a/Content/Characters/GA/BPGA_Weapon_Sword.uasset and /dev/null differ diff --git a/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Master.uasset b/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Master.uasset deleted file mode 100644 index b3f4ff7c..00000000 Binary files a/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Master.uasset and /dev/null differ diff --git a/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Sword.uasset b/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Sword.uasset deleted file mode 100644 index bf8e5ab7..00000000 Binary files a/Content/Characters/GA/NormalAttack/BPGA_Attack_Normal_Sword.uasset and /dev/null differ diff --git a/Content/Characters/GA/PressAttack/BPGA_Attack_Press_Sword.uasset b/Content/Characters/GA/PressAttack/BPGA_Attack_Press_Sword.uasset deleted file mode 100644 index ef2339ad..00000000 Binary files a/Content/Characters/GA/PressAttack/BPGA_Attack_Press_Sword.uasset and /dev/null differ diff --git a/Content/Characters/GA/PressAttack/BPGA_Attack_Press_master.uasset b/Content/Characters/GA/PressAttack/BPGA_Attack_Press_master.uasset deleted file mode 100644 index 740de77d..00000000 Binary files a/Content/Characters/GA/PressAttack/BPGA_Attack_Press_master.uasset and /dev/null differ diff --git a/Content/Characters/Item/BP_Sword.uasset b/Content/Characters/Item/BP_Sword.uasset new file mode 100644 index 00000000..7b30cac3 Binary files /dev/null and b/Content/Characters/Item/BP_Sword.uasset differ diff --git a/Content/Characters/Item/BP_Test_Hat.uasset b/Content/Characters/Item/BP_Test_Hat.uasset deleted file mode 100644 index d75fceeb..00000000 Binary files a/Content/Characters/Item/BP_Test_Hat.uasset and /dev/null differ diff --git a/Content/Characters/NonPlayer/testNPC2_JS.uasset b/Content/Characters/NonPlayer/testNPC2_JS.uasset index 32bb6514..16f60ab8 100644 Binary files a/Content/Characters/NonPlayer/testNPC2_JS.uasset and b/Content/Characters/NonPlayer/testNPC2_JS.uasset differ diff --git a/Content/Characters/NonPlayer/testNPC_JS.uasset b/Content/Characters/NonPlayer/testNPC_JS.uasset index bafd0157..6bb0ce56 100644 Binary files a/Content/Characters/NonPlayer/testNPC_JS.uasset and b/Content/Characters/NonPlayer/testNPC_JS.uasset differ diff --git a/Content/Characters/Player/BPGE_StaminaRegen.uasset b/Content/Characters/Player/BPGE_StaminaRegen.uasset new file mode 100644 index 00000000..6889774b Binary files /dev/null and b/Content/Characters/Player/BPGE_StaminaRegen.uasset differ diff --git a/Content/Characters/Player/BP_MJNewPlayerCharacter.uasset b/Content/Characters/Player/BP_MJNewPlayerCharacter.uasset new file mode 100644 index 00000000..66b38823 Binary files /dev/null and b/Content/Characters/Player/BP_MJNewPlayerCharacter.uasset differ diff --git a/Content/Characters/Player/BP_MJPlayerCharacter.uasset b/Content/Characters/Player/BP_MJPlayerCharacter.uasset index 949314a2..03da5062 100644 Binary files a/Content/Characters/Player/BP_MJPlayerCharacter.uasset and b/Content/Characters/Player/BP_MJPlayerCharacter.uasset differ diff --git a/Content/Characters/Player/testPlayer_JS.uasset b/Content/Characters/Player/testPlayer_JS.uasset index aedf9eb7..8ad917c4 100644 Binary files a/Content/Characters/Player/testPlayer_JS.uasset and b/Content/Characters/Player/testPlayer_JS.uasset differ diff --git a/Content/Core/Controller/BP_MJPlayerController.uasset b/Content/Core/Controller/BP_MJPlayerController.uasset index 153b26a2..8a35f2a7 100644 Binary files a/Content/Core/Controller/BP_MJPlayerController.uasset and b/Content/Core/Controller/BP_MJPlayerController.uasset differ diff --git a/Content/Core/GameMode/BP_Gamemode_Boss_TG.uasset b/Content/Core/GameMode/BP_Gamemode_Boss_TG.uasset deleted file mode 100644 index fe9112cb..00000000 Binary files a/Content/Core/GameMode/BP_Gamemode_Boss_TG.uasset and /dev/null differ diff --git a/Content/Core/GameMode/BP_Gamemode_Dungeon.uasset b/Content/Core/GameMode/BP_Gamemode_Dungeon.uasset new file mode 100644 index 00000000..1f61c11c Binary files /dev/null and b/Content/Core/GameMode/BP_Gamemode_Dungeon.uasset differ diff --git a/Content/Core/GameMode/BP_Gamemode_Dungeon_TG.uasset b/Content/Core/GameMode/BP_Gamemode_Dungeon_TG.uasset deleted file mode 100644 index 0af16427..00000000 Binary files a/Content/Core/GameMode/BP_Gamemode_Dungeon_TG.uasset and /dev/null differ diff --git a/Content/Core/GameMode/BP_Gamemode_MainMenu.uasset b/Content/Core/GameMode/BP_Gamemode_MainMenu.uasset new file mode 100644 index 00000000..8193268d Binary files /dev/null and b/Content/Core/GameMode/BP_Gamemode_MainMenu.uasset differ diff --git a/Content/Core/GameMode/BP_Gamemode_Town.uasset b/Content/Core/GameMode/BP_Gamemode_Town.uasset new file mode 100644 index 00000000..753f0139 Binary files /dev/null and b/Content/Core/GameMode/BP_Gamemode_Town.uasset differ diff --git a/Content/Core/GameMode/BP_Gamemode_Town_TG.uasset b/Content/Core/GameMode/BP_Gamemode_Town_TG.uasset deleted file mode 100644 index ae4622e7..00000000 Binary files a/Content/Core/GameMode/BP_Gamemode_Town_TG.uasset and /dev/null differ diff --git a/Content/Core/GameMode/BP_MJGameMode.uasset b/Content/Core/GameMode/BP_MJGameMode.uasset index f9f6a4a2..9ec717d3 100644 Binary files a/Content/Core/GameMode/BP_MJGameMode.uasset and b/Content/Core/GameMode/BP_MJGameMode.uasset differ diff --git a/Content/Core/GameState/BP_GameState_Dungeon.uasset b/Content/Core/GameState/BP_GameState_Dungeon.uasset new file mode 100644 index 00000000..dc434682 Binary files /dev/null and b/Content/Core/GameState/BP_GameState_Dungeon.uasset differ diff --git a/Content/Core/GameState/BP_GameState_Town.uasset b/Content/Core/GameState/BP_GameState_Town.uasset new file mode 100644 index 00000000..006b9c84 Binary files /dev/null and b/Content/Core/GameState/BP_GameState_Town.uasset differ diff --git a/Content/DM/ABP_DM.uasset b/Content/DM/ABP_DM.uasset new file mode 100644 index 00000000..424a9674 Binary files /dev/null and b/Content/DM/ABP_DM.uasset differ diff --git a/Content/DM/BP_GameInstance_DM.uasset b/Content/DM/BP_GameInstance_DM.uasset deleted file mode 100644 index c5951b0a..00000000 Binary files a/Content/DM/BP_GameInstance_DM.uasset and /dev/null differ diff --git a/Content/DM/BS_DM.uasset b/Content/DM/BS_DM.uasset new file mode 100644 index 00000000..c1159f71 Binary files /dev/null and b/Content/DM/BS_DM.uasset differ diff --git a/Content/DM/Idle_DM.uasset b/Content/DM/Idle_DM.uasset new file mode 100644 index 00000000..86389ab4 Binary files /dev/null and b/Content/DM/Idle_DM.uasset differ diff --git a/Content/DM/Run_Loop.uasset b/Content/DM/Run_Loop.uasset new file mode 100644 index 00000000..25d81bf9 Binary files /dev/null and b/Content/DM/Run_Loop.uasset differ diff --git a/Content/DM/Run_Start.uasset b/Content/DM/Run_Start.uasset new file mode 100644 index 00000000..479a78f8 Binary files /dev/null and b/Content/DM/Run_Start.uasset differ diff --git a/Content/DM/Run_Stop.uasset b/Content/DM/Run_Stop.uasset new file mode 100644 index 00000000..51b0d5c5 Binary files /dev/null and b/Content/DM/Run_Stop.uasset differ diff --git a/Content/DM/Walk_Loop.uasset b/Content/DM/Walk_Loop.uasset new file mode 100644 index 00000000..f9e1e246 Binary files /dev/null and b/Content/DM/Walk_Loop.uasset differ diff --git a/Content/DM/Walk_Start.uasset b/Content/DM/Walk_Start.uasset new file mode 100644 index 00000000..b5630ef0 Binary files /dev/null and b/Content/DM/Walk_Start.uasset differ diff --git a/Content/DM/Walk_Stop.uasset b/Content/DM/Walk_Stop.uasset new file mode 100644 index 00000000..236d555d Binary files /dev/null and b/Content/DM/Walk_Stop.uasset differ diff --git a/Content/DataAsset/Character/DA_InnateEffect.uasset b/Content/DataAsset/Character/DA_InnateEffect.uasset new file mode 100644 index 00000000..b6e0eeb2 Binary files /dev/null and b/Content/DataAsset/Character/DA_InnateEffect.uasset differ diff --git a/Content/DataAsset/Character/DA_PlayerStart.uasset b/Content/DataAsset/Character/DA_PlayerStart.uasset index ceb0e4c6..17898006 100644 Binary files a/Content/DataAsset/Character/DA_PlayerStart.uasset and b/Content/DataAsset/Character/DA_PlayerStart.uasset differ diff --git a/Content/DataAsset/Character/DA_Start.uasset b/Content/DataAsset/Character/DA_Start.uasset deleted file mode 100644 index 1c6d8740..00000000 Binary files a/Content/DataAsset/Character/DA_Start.uasset and /dev/null differ diff --git a/Content/DataAsset/Input/DA_InputConfig.uasset b/Content/DataAsset/Input/DA_InputConfig.uasset index 731c3f70..8eb383bf 100644 Binary files a/Content/DataAsset/Input/DA_InputConfig.uasset and b/Content/DataAsset/Input/DA_InputConfig.uasset differ diff --git a/Content/DataAsset/Item/DA_ForestCreatureDropItems.uasset b/Content/DataAsset/Item/DA_ForestCreatureDropItems.uasset new file mode 100644 index 00000000..44a91466 Binary files /dev/null and b/Content/DataAsset/Item/DA_ForestCreatureDropItems.uasset differ diff --git a/Content/DataAsset/Item/DA_ItemMaps.uasset b/Content/DataAsset/Item/DA_ItemMaps.uasset new file mode 100644 index 00000000..6a3434b6 Binary files /dev/null and b/Content/DataAsset/Item/DA_ItemMaps.uasset differ diff --git a/Content/DataAsset/Item/DA_StoneDropItems.uasset b/Content/DataAsset/Item/DA_StoneDropItems.uasset new file mode 100644 index 00000000..051640b3 Binary files /dev/null and b/Content/DataAsset/Item/DA_StoneDropItems.uasset differ diff --git a/Content/DataTable/Dialogue/MJDataTableTest.uasset b/Content/DataTable/Dialogue/MJDataTableTest.uasset index f90edcf8..6c3ea2b3 100644 Binary files a/Content/DataTable/Dialogue/MJDataTableTest.uasset and b/Content/DataTable/Dialogue/MJDataTableTest.uasset differ diff --git a/Content/DataTable/Dialogue/MJDataTableTest2.uasset b/Content/DataTable/Dialogue/MJDataTableTest2.uasset index d4b211f9..65b401d2 100644 Binary files a/Content/DataTable/Dialogue/MJDataTableTest2.uasset and b/Content/DataTable/Dialogue/MJDataTableTest2.uasset differ diff --git a/Content/DataTable/Enemy/DT_EnemyDataTable.uasset b/Content/DataTable/Enemy/DT_EnemyDataTable.uasset new file mode 100644 index 00000000..98e2cc69 Binary files /dev/null and b/Content/DataTable/Enemy/DT_EnemyDataTable.uasset differ diff --git a/Content/DataTable/Enemy/DT_GoldDropTable.uasset b/Content/DataTable/Enemy/DT_GoldDropTable.uasset new file mode 100644 index 00000000..5e44520b Binary files /dev/null and b/Content/DataTable/Enemy/DT_GoldDropTable.uasset differ diff --git a/Content/DataTable/Inventory/DT_ItemData_JS.uasset b/Content/DataTable/Inventory/DT_ItemData_JS.uasset new file mode 100644 index 00000000..e87b4e58 Binary files /dev/null and b/Content/DataTable/Inventory/DT_ItemData_JS.uasset differ diff --git a/Content/DataTable/PlayerStat/Curve_CharacterStat_Table.uasset b/Content/DataTable/PlayerStat/Curve_CharacterStat_Table.uasset new file mode 100644 index 00000000..e6a162be Binary files /dev/null and b/Content/DataTable/PlayerStat/Curve_CharacterStat_Table.uasset differ diff --git a/Content/DataTable/Skill/DT_EnemySkillDataTable.uasset b/Content/DataTable/Skill/DT_EnemySkillDataTable.uasset new file mode 100644 index 00000000..bafdee95 Binary files /dev/null and b/Content/DataTable/Skill/DT_EnemySkillDataTable.uasset differ diff --git a/Content/DataTable/Skill/DT_SkillDataTable.uasset b/Content/DataTable/Skill/DT_SkillDataTable.uasset index 9d627fcf..cacf731e 100644 Binary files a/Content/DataTable/Skill/DT_SkillDataTable.uasset and b/Content/DataTable/Skill/DT_SkillDataTable.uasset differ diff --git a/Content/DataTable/Skill/SkillCurveTable/Curve_BasicMeleeAttack_Table.uasset b/Content/DataTable/Skill/SkillCurveTable/Curve_BasicMeleeAttack_Table.uasset deleted file mode 100644 index 2fc7f815..00000000 Binary files a/Content/DataTable/Skill/SkillCurveTable/Curve_BasicMeleeAttack_Table.uasset and /dev/null differ diff --git a/Content/DataTable/Skill/SkillCurveTable/Curve_Catastrophe_Table.uasset b/Content/DataTable/Skill/SkillCurveTable/Curve_Catastrophe_Table.uasset deleted file mode 100644 index 61710287..00000000 Binary files a/Content/DataTable/Skill/SkillCurveTable/Curve_Catastrophe_Table.uasset and /dev/null differ diff --git a/Content/DataTable/Skill/SkillCurveTable/Curve_Sample_Table.uasset b/Content/DataTable/Skill/SkillCurveTable/Curve_Sample_Table.uasset deleted file mode 100644 index 955a08ff..00000000 Binary files a/Content/DataTable/Skill/SkillCurveTable/Curve_Sample_Table.uasset and /dev/null differ diff --git a/Content/Input/Actions/IA_Mouse_Left.uasset b/Content/Input/Actions/IA_Mouse_Left.uasset new file mode 100644 index 00000000..d1edff50 Binary files /dev/null and b/Content/Input/Actions/IA_Mouse_Left.uasset differ diff --git a/Content/Input/Actions/IA_Mouse_Right.uasset b/Content/Input/Actions/IA_Mouse_Right.uasset new file mode 100644 index 00000000..caa0a319 Binary files /dev/null and b/Content/Input/Actions/IA_Mouse_Right.uasset differ diff --git a/Content/Input/Actions/IA_Pause.uasset b/Content/Input/Actions/IA_Pause.uasset new file mode 100644 index 00000000..d1342c59 Binary files /dev/null and b/Content/Input/Actions/IA_Pause.uasset differ diff --git a/Content/Input/Actions/IA_Shift.uasset b/Content/Input/Actions/IA_Shift.uasset new file mode 100644 index 00000000..b4de1551 Binary files /dev/null and b/Content/Input/Actions/IA_Shift.uasset differ diff --git a/Content/Input/Actions/TestInput/IA_K.uasset b/Content/Input/Actions/TestInput/IA_K.uasset new file mode 100644 index 00000000..23211b25 Binary files /dev/null and b/Content/Input/Actions/TestInput/IA_K.uasset differ diff --git a/Content/Input/IMC_Default.uasset b/Content/Input/IMC_Default.uasset index b5621b94..94f65396 100644 Binary files a/Content/Input/IMC_Default.uasset and b/Content/Input/IMC_Default.uasset differ diff --git a/Content/Input/IMC_Dialogue.uasset b/Content/Input/IMC_Dialogue.uasset index 0db67569..e56b0df8 100644 Binary files a/Content/Input/IMC_Dialogue.uasset and b/Content/Input/IMC_Dialogue.uasset differ diff --git a/Content/LevelActor/BP_LevelActorBase.uasset b/Content/LevelActor/BP_LevelActorBase.uasset new file mode 100644 index 00000000..8d5203e2 Binary files /dev/null and b/Content/LevelActor/BP_LevelActorBase.uasset differ diff --git a/Content/LevelActor/ChangeTest.uasset b/Content/LevelActor/ChangeTest.uasset new file mode 100644 index 00000000..30050545 Binary files /dev/null and b/Content/LevelActor/ChangeTest.uasset differ diff --git a/Content/LevelActor/PlayerAnimationBaking.uasset b/Content/LevelActor/PlayerAnimationBaking.uasset new file mode 100644 index 00000000..a8ac9a64 Binary files /dev/null and b/Content/LevelActor/PlayerAnimationBaking.uasset differ diff --git a/Content/LevelActor/Trailer.uasset b/Content/LevelActor/Trailer.uasset new file mode 100644 index 00000000..e91fd69b Binary files /dev/null and b/Content/LevelActor/Trailer.uasset differ diff --git a/Content/LevelActor/Trailer2.uasset b/Content/LevelActor/Trailer2.uasset new file mode 100644 index 00000000..964ac830 Binary files /dev/null and b/Content/LevelActor/Trailer2.uasset differ diff --git a/Content/LevelActor/Trailer3.uasset b/Content/LevelActor/Trailer3.uasset new file mode 100644 index 00000000..3fd31704 Binary files /dev/null and b/Content/LevelActor/Trailer3.uasset differ diff --git a/Content/MJ/AIC/BPAIC_Elemental.uasset b/Content/MJ/AIC/BPAIC_Elemental.uasset new file mode 100644 index 00000000..19d958ae Binary files /dev/null and b/Content/MJ/AIC/BPAIC_Elemental.uasset differ diff --git a/Content/MJ/AIC/BPAIC_Golem.uasset b/Content/MJ/AIC/BPAIC_Golem.uasset new file mode 100644 index 00000000..1cef9ce8 Binary files /dev/null and b/Content/MJ/AIC/BPAIC_Golem.uasset differ diff --git a/Content/MJ/AIC/BPAIC_MJSpawnTest.uasset b/Content/MJ/AIC/BPAIC_MJSpawnTest.uasset deleted file mode 100644 index f571d766..00000000 Binary files a/Content/MJ/AIC/BPAIC_MJSpawnTest.uasset and /dev/null differ diff --git a/Content/MJ/AIC/BPAIC_MeleeFire.uasset b/Content/MJ/AIC/BPAIC_MeleeFire.uasset new file mode 100644 index 00000000..80dcf1bb Binary files /dev/null and b/Content/MJ/AIC/BPAIC_MeleeFire.uasset differ diff --git a/Content/MJ/AIC/BPAIC_MeleeStone.uasset b/Content/MJ/AIC/BPAIC_MeleeStone.uasset new file mode 100644 index 00000000..2e844de6 Binary files /dev/null and b/Content/MJ/AIC/BPAIC_MeleeStone.uasset differ diff --git a/Content/MJ/BB_MJEnemy.uasset b/Content/MJ/BB_MJEnemy.uasset new file mode 100644 index 00000000..0b90c8dd Binary files /dev/null and b/Content/MJ/BB_MJEnemy.uasset differ diff --git a/Content/MJ/BB_MJMonster.uasset b/Content/MJ/BB_MJMonster.uasset deleted file mode 100644 index bea08ea5..00000000 Binary files a/Content/MJ/BB_MJMonster.uasset and /dev/null differ diff --git a/Content/MJ/BP_MJGameModeMJ.uasset b/Content/MJ/BP_MJGameModeMJ.uasset index dc2c4f24..65982050 100644 Binary files a/Content/MJ/BP_MJGameModeMJ.uasset and b/Content/MJ/BP_MJGameModeMJ.uasset differ diff --git a/Content/MJ/BP_MJPlayerCharacterMJ.uasset b/Content/MJ/BP_MJPlayerCharacterMJ.uasset index 3484138c..fe30b040 100644 Binary files a/Content/MJ/BP_MJPlayerCharacterMJ.uasset and b/Content/MJ/BP_MJPlayerCharacterMJ.uasset differ diff --git a/Content/MJ/BP_PlayerTest.uasset b/Content/MJ/BP_PlayerTest.uasset new file mode 100644 index 00000000..2d8d2239 Binary files /dev/null and b/Content/MJ/BP_PlayerTest.uasset differ diff --git a/Content/MJ/BP_ProjectileBall.uasset b/Content/MJ/BP_ProjectileBall.uasset index 303a8c10..c5b67567 100644 Binary files a/Content/MJ/BP_ProjectileBall.uasset and b/Content/MJ/BP_ProjectileBall.uasset differ diff --git a/Content/MJ/BT/BT_MJGolem.uasset b/Content/MJ/BT/BT_MJGolem.uasset new file mode 100644 index 00000000..c6809b4d Binary files /dev/null and b/Content/MJ/BT/BT_MJGolem.uasset differ diff --git a/Content/MJ/BT/BT_MJMeleeFire.uasset b/Content/MJ/BT/BT_MJMeleeFire.uasset new file mode 100644 index 00000000..f843e9b9 Binary files /dev/null and b/Content/MJ/BT/BT_MJMeleeFire.uasset differ diff --git a/Content/MJ/BT/BT_MJMeleeMonster.uasset b/Content/MJ/BT/BT_MJMeleeMonster.uasset index 88ffd6c9..7f731e55 100644 Binary files a/Content/MJ/BT/BT_MJMeleeMonster.uasset and b/Content/MJ/BT/BT_MJMeleeMonster.uasset differ diff --git a/Content/MJ/BT/BT_MJMeleeStone.uasset b/Content/MJ/BT/BT_MJMeleeStone.uasset new file mode 100644 index 00000000..eb29e673 Binary files /dev/null and b/Content/MJ/BT/BT_MJMeleeStone.uasset differ diff --git a/Content/MJ/BT/BT_MJMonster.uasset b/Content/MJ/BT/BT_MJMonster.uasset index 693ec300..b1ec325a 100644 Binary files a/Content/MJ/BT/BT_MJMonster.uasset and b/Content/MJ/BT/BT_MJMonster.uasset differ diff --git a/Content/MJ/BT/BT_MJRangedMonster.uasset b/Content/MJ/BT/BT_MJRangedMonster.uasset index b06bb96a..e8d6f577 100644 Binary files a/Content/MJ/BT/BT_MJRangedMonster.uasset and b/Content/MJ/BT/BT_MJRangedMonster.uasset differ diff --git a/Content/MJ/BT/BT_MJSpawnTest.uasset b/Content/MJ/BT/BT_MJSpawnTest.uasset deleted file mode 100644 index 1b32a265..00000000 Binary files a/Content/MJ/BT/BT_MJSpawnTest.uasset and /dev/null differ diff --git a/Content/MJ/DropSkill/BP_MemoryTransferProjectile.uasset b/Content/MJ/DropSkill/BP_MemoryTransferProjectile.uasset new file mode 100644 index 00000000..d3c82469 Binary files /dev/null and b/Content/MJ/DropSkill/BP_MemoryTransferProjectile.uasset differ diff --git a/Content/MJ/DropSkill/NS_GiveSkillHit.uasset b/Content/MJ/DropSkill/NS_GiveSkillHit.uasset new file mode 100644 index 00000000..40ba4277 Binary files /dev/null and b/Content/MJ/DropSkill/NS_GiveSkillHit.uasset differ diff --git a/Content/MJ/DropSkill/NS_GiveSkillMuzzle.uasset b/Content/MJ/DropSkill/NS_GiveSkillMuzzle.uasset new file mode 100644 index 00000000..0999ce0c Binary files /dev/null and b/Content/MJ/DropSkill/NS_GiveSkillMuzzle.uasset differ diff --git a/Content/MJ/DropSkill/NS_GiveSkillProjectile.uasset b/Content/MJ/DropSkill/NS_GiveSkillProjectile.uasset new file mode 100644 index 00000000..f1787517 Binary files /dev/null and b/Content/MJ/DropSkill/NS_GiveSkillProjectile.uasset differ diff --git a/Content/MJ/Monster/BP_MJMeleeMonster.uasset b/Content/MJ/Monster/BP_MJMeleeMonster.uasset index 1463f27a..83382b95 100644 Binary files a/Content/MJ/Monster/BP_MJMeleeMonster.uasset and b/Content/MJ/Monster/BP_MJMeleeMonster.uasset differ diff --git a/Content/MJ/Monster/BP_MJMonsterBase.uasset b/Content/MJ/Monster/BP_MJMonsterBase.uasset deleted file mode 100644 index 07503658..00000000 Binary files a/Content/MJ/Monster/BP_MJMonsterBase.uasset and /dev/null differ diff --git a/Content/MJ/Monster/BP_MJMonster_Spawn.uasset b/Content/MJ/Monster/BP_MJMonster_Spawn.uasset deleted file mode 100644 index 1607e73e..00000000 Binary files a/Content/MJ/Monster/BP_MJMonster_Spawn.uasset and /dev/null differ diff --git a/Content/MJ/Monster/BP_MJRangedMonster.uasset b/Content/MJ/Monster/BP_MJRangedMonster.uasset index 49c71793..6b867e71 100644 Binary files a/Content/MJ/Monster/BP_MJRangedMonster.uasset and b/Content/MJ/Monster/BP_MJRangedMonster.uasset differ diff --git a/Content/MJ/Task/BTTask_MJAttack.uasset b/Content/MJ/Task/BTTask_MJAttack.uasset deleted file mode 100644 index 66f62d00..00000000 Binary files a/Content/MJ/Task/BTTask_MJAttack.uasset and /dev/null differ diff --git a/Content/MJ/Task/BTTask_RangedAttack.uasset b/Content/MJ/Task/BTTask_RangedAttack.uasset deleted file mode 100644 index 608028fd..00000000 Binary files a/Content/MJ/Task/BTTask_RangedAttack.uasset and /dev/null differ diff --git a/Content/Maps/A_M_Base_DM.umap b/Content/Maps/A_M_Base_DM.umap index cd086ed2..03c92bd5 100644 Binary files a/Content/Maps/A_M_Base_DM.umap and b/Content/Maps/A_M_Base_DM.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle01.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle01.umap index d974e983..e61460b0 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle01.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle01.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle02.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle02.umap index affe0210..382013af 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle02.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle02.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle03.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle03.umap index cb44d7ad..cdaa232e 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle03.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle03.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle04.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle04.umap index 9cf6d86b..14bedce5 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle04.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle04.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle05.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle05.umap index 44562a43..5bfe7817 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle05.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Battle05.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Boss.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Boss.umap index 5b779113..feae91b7 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Boss.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Boss.umap differ diff --git a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Reward.umap b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Reward.umap index 6c0892b1..d5181b83 100644 Binary files a/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Reward.umap and b/Content/Maps/Dungeon_Chunks/Dungeon_Chunk_Reward.umap differ diff --git a/Content/Maps/JH_Test.umap b/Content/Maps/JH_Test.umap index 965b297e..e3ddc207 100644 Binary files a/Content/Maps/JH_Test.umap and b/Content/Maps/JH_Test.umap differ diff --git a/Content/Maps/Loading.umap b/Content/Maps/Loading.umap deleted file mode 100644 index c6973106..00000000 Binary files a/Content/Maps/Loading.umap and /dev/null differ diff --git a/Content/Maps/MJ_AITest.umap b/Content/Maps/MJ_AITest.umap index 2919cbcf..71f876c3 100644 Binary files a/Content/Maps/MJ_AITest.umap and b/Content/Maps/MJ_AITest.umap differ diff --git a/Content/Maps/M_JS.umap b/Content/Maps/M_JS.umap index d532ccc8..5098e6a4 100644 Binary files a/Content/Maps/M_JS.umap and b/Content/Maps/M_JS.umap differ diff --git a/Content/Maps/TG_Dungeon_Deprecated.umap b/Content/Maps/TG_Dungeon_Deprecated.umap deleted file mode 100644 index 2acb60e2..00000000 Binary files a/Content/Maps/TG_Dungeon_Deprecated.umap and /dev/null differ diff --git a/Content/Maps/TG_MainMenu.umap b/Content/Maps/TG_MainMenu.umap new file mode 100644 index 00000000..171089dc Binary files /dev/null and b/Content/Maps/TG_MainMenu.umap differ diff --git a/Content/Maps/TG_Town.umap b/Content/Maps/TG_Town.umap index a53fa146..1b47e775 100644 Binary files a/Content/Maps/TG_Town.umap and b/Content/Maps/TG_Town.umap differ diff --git a/Content/Material/JH/Cliff_LayerInfo.uasset b/Content/Material/JH/Cliff_LayerInfo.uasset new file mode 100644 index 00000000..dcb008ee Binary files /dev/null and b/Content/Material/JH/Cliff_LayerInfo.uasset differ diff --git a/Content/Material/JH/Grass_Cliff_LayerInfo.uasset b/Content/Material/JH/Grass_Cliff_LayerInfo.uasset new file mode 100644 index 00000000..cb69d585 Binary files /dev/null and b/Content/Material/JH/Grass_Cliff_LayerInfo.uasset differ diff --git a/Content/Material/JH/Grass_LayerInfo.uasset b/Content/Material/JH/Grass_LayerInfo.uasset new file mode 100644 index 00000000..90c498c7 Binary files /dev/null and b/Content/Material/JH/Grass_LayerInfo.uasset differ diff --git a/Content/Material/JH/Ground_LayerInfo.uasset b/Content/Material/JH/Ground_LayerInfo.uasset new file mode 100644 index 00000000..ada88fcf Binary files /dev/null and b/Content/Material/JH/Ground_LayerInfo.uasset differ diff --git a/Content/Material/JH/LutTexture.uasset b/Content/Material/JH/LutTexture.uasset new file mode 100644 index 00000000..d97fe2d6 Binary files /dev/null and b/Content/Material/JH/LutTexture.uasset differ diff --git a/Content/Material/JH/MI_MJLandscape_Inst.uasset b/Content/Material/JH/MI_MJLandscape_Inst.uasset new file mode 100644 index 00000000..c59b3d50 Binary files /dev/null and b/Content/Material/JH/MI_MJLandscape_Inst.uasset differ diff --git a/Content/Material/JH/MJ_LandScape_Purple.uasset b/Content/Material/JH/MJ_LandScape_Purple.uasset new file mode 100644 index 00000000..910e8cf6 Binary files /dev/null and b/Content/Material/JH/MJ_LandScape_Purple.uasset differ diff --git a/Content/Material/JH/MJ_Landscape_MAT_Inst.uasset b/Content/Material/JH/MJ_Landscape_MAT_Inst.uasset new file mode 100644 index 00000000..d445ad44 Binary files /dev/null and b/Content/Material/JH/MJ_Landscape_MAT_Inst.uasset differ diff --git a/Content/Material/JH/MJ_StylizedLandscapeAutoMaterial_MAT_Inst.uasset b/Content/Material/JH/MJ_StylizedLandscapeAutoMaterial_MAT_Inst.uasset new file mode 100644 index 00000000..e2f2cbff Binary files /dev/null and b/Content/Material/JH/MJ_StylizedLandscapeAutoMaterial_MAT_Inst.uasset differ diff --git a/Content/Material/JH/MJ_Water_MASTER_MAT_Inst.uasset b/Content/Material/JH/MJ_Water_MASTER_MAT_Inst.uasset new file mode 100644 index 00000000..aa739b09 Binary files /dev/null and b/Content/Material/JH/MJ_Water_MASTER_MAT_Inst.uasset differ diff --git a/Content/Material/JH/M_CilShader.uasset b/Content/Material/JH/M_CilShader.uasset new file mode 100644 index 00000000..d7ceac2c Binary files /dev/null and b/Content/Material/JH/M_CilShader.uasset differ diff --git a/Content/Material/JH/M_CilShader_Inst.uasset b/Content/Material/JH/M_CilShader_Inst.uasset new file mode 100644 index 00000000..65be6b44 Binary files /dev/null and b/Content/Material/JH/M_CilShader_Inst.uasset differ diff --git a/Content/Material/JH/M_CustomStancil.uasset b/Content/Material/JH/M_CustomStancil.uasset new file mode 100644 index 00000000..ed264a9a Binary files /dev/null and b/Content/Material/JH/M_CustomStancil.uasset differ diff --git a/Content/Material/JH/M_LandScape.uasset b/Content/Material/JH/M_LandScape.uasset new file mode 100644 index 00000000..bbfd540f Binary files /dev/null and b/Content/Material/JH/M_LandScape.uasset differ diff --git a/Content/Material/JH/M_OutLIne.uasset b/Content/Material/JH/M_OutLIne.uasset new file mode 100644 index 00000000..b04a722f Binary files /dev/null and b/Content/Material/JH/M_OutLIne.uasset differ diff --git a/Content/Material/JH/M_Point.uasset b/Content/Material/JH/M_Point.uasset index 7fb6a049..a5735287 100644 Binary files a/Content/Material/JH/M_Point.uasset and b/Content/Material/JH/M_Point.uasset differ diff --git a/Content/Material/JH/M_Test.uasset b/Content/Material/JH/M_Test.uasset new file mode 100644 index 00000000..28ea1664 Binary files /dev/null and b/Content/Material/JH/M_Test.uasset differ diff --git a/Content/Material/JH/M_TestA.uasset b/Content/Material/JH/M_TestA.uasset new file mode 100644 index 00000000..fc87d509 Binary files /dev/null and b/Content/Material/JH/M_TestA.uasset differ diff --git a/Content/Material/JH/M_TestC.uasset b/Content/Material/JH/M_TestC.uasset new file mode 100644 index 00000000..6ad71f10 Binary files /dev/null and b/Content/Material/JH/M_TestC.uasset differ diff --git a/Content/Material/TG/MapEdge.uasset b/Content/Material/TG/MapEdge.uasset new file mode 100644 index 00000000..2c3f521e Binary files /dev/null and b/Content/Material/TG/MapEdge.uasset differ diff --git a/Content/Material/TG/MiniMapMaterial.uasset b/Content/Material/TG/MiniMapMaterial.uasset new file mode 100644 index 00000000..e22500cb Binary files /dev/null and b/Content/Material/TG/MiniMapMaterial.uasset differ diff --git a/Content/Material/TG/MiniMapPlaneMaterial.uasset b/Content/Material/TG/MiniMapPlaneMaterial.uasset new file mode 100644 index 00000000..7f52faac Binary files /dev/null and b/Content/Material/TG/MiniMapPlaneMaterial.uasset differ diff --git a/Content/Material/TG/MiniMap_Enemy.uasset b/Content/Material/TG/MiniMap_Enemy.uasset new file mode 100644 index 00000000..a28078a0 Binary files /dev/null and b/Content/Material/TG/MiniMap_Enemy.uasset differ diff --git a/Content/Material/TG/MiniMap_IconPlate.uasset b/Content/Material/TG/MiniMap_IconPlate.uasset new file mode 100644 index 00000000..533bb11c Binary files /dev/null and b/Content/Material/TG/MiniMap_IconPlate.uasset differ diff --git a/Content/Material/TG/MiniMap_Map_Inst.uasset b/Content/Material/TG/MiniMap_Map_Inst.uasset new file mode 100644 index 00000000..b6bf0e6d Binary files /dev/null and b/Content/Material/TG/MiniMap_Map_Inst.uasset differ diff --git a/Content/Material/TG/MiniMap_NextDungeon.uasset b/Content/Material/TG/MiniMap_NextDungeon.uasset new file mode 100644 index 00000000..c26f4cd5 Binary files /dev/null and b/Content/Material/TG/MiniMap_NextDungeon.uasset differ diff --git a/Content/Material/TG/MiniMap_Player.uasset b/Content/Material/TG/MiniMap_Player.uasset new file mode 100644 index 00000000..b5c3b706 Binary files /dev/null and b/Content/Material/TG/MiniMap_Player.uasset differ diff --git a/Content/Material/TG/MiniMap_Portal.uasset b/Content/Material/TG/MiniMap_Portal.uasset new file mode 100644 index 00000000..b93cfd66 Binary files /dev/null and b/Content/Material/TG/MiniMap_Portal.uasset differ diff --git a/Content/Material/TG/MiniMap_Save_Inst1.uasset b/Content/Material/TG/MiniMap_Save_Inst1.uasset new file mode 100644 index 00000000..129b680b Binary files /dev/null and b/Content/Material/TG/MiniMap_Save_Inst1.uasset differ diff --git a/Content/Material/TG/MiniMap_ToTown.uasset b/Content/Material/TG/MiniMap_ToTown.uasset new file mode 100644 index 00000000..1295fd7d Binary files /dev/null and b/Content/Material/TG/MiniMap_ToTown.uasset differ diff --git a/Content/Material/TG/PortalToDungeonImage.uasset b/Content/Material/TG/PortalToDungeonImage.uasset new file mode 100644 index 00000000..177d0eb9 Binary files /dev/null and b/Content/Material/TG/PortalToDungeonImage.uasset differ diff --git a/Content/Material/TG/PortalToTownImage.uasset b/Content/Material/TG/PortalToTownImage.uasset new file mode 100644 index 00000000..6f82778e Binary files /dev/null and b/Content/Material/TG/PortalToTownImage.uasset differ diff --git a/Content/Material/TG/ProceduralMesh_Green.uasset b/Content/Material/TG/ProceduralMesh_Green.uasset new file mode 100644 index 00000000..4b02de97 Binary files /dev/null and b/Content/Material/TG/ProceduralMesh_Green.uasset differ diff --git a/Content/Material/TG/Red_TG.uasset b/Content/Material/TG/Red_TG.uasset deleted file mode 100644 index fcfdcb0d..00000000 Binary files a/Content/Material/TG/Red_TG.uasset and /dev/null differ diff --git a/Content/Material/TG/TG_UI.uasset b/Content/Material/TG/TG_UI.uasset new file mode 100644 index 00000000..2399df60 Binary files /dev/null and b/Content/Material/TG/TG_UI.uasset differ diff --git a/Content/Material/TG/TG_UIRed.uasset b/Content/Material/TG/TG_UIRed.uasset new file mode 100644 index 00000000..2bf6e54d Binary files /dev/null and b/Content/Material/TG/TG_UIRed.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrike.uasset b/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrike.uasset new file mode 100644 index 00000000..4092c4e1 Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrike.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrikeCharging.uasset b/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrikeCharging.uasset new file mode 100644 index 00000000..2d3dcfce Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/AM_AlphaStrikeCharging.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_End.uasset b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_End.uasset new file mode 100644 index 00000000..78d6826a Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_End.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Loop.uasset b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Loop.uasset new file mode 100644 index 00000000..fe5e7670 Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Loop.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Start.uasset b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Start.uasset new file mode 100644 index 00000000..9afde97f Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/AlphaStrike_Start.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/BPGA_ActionAlphaStrike.uasset b/Content/PlayerSkill/Charge/AlphaStrike/BPGA_ActionAlphaStrike.uasset new file mode 100644 index 00000000..8567258f Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/BPGA_ActionAlphaStrike.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/BPGA_AlphaStrike.uasset b/Content/PlayerSkill/Charge/AlphaStrike/BPGA_AlphaStrike.uasset new file mode 100644 index 00000000..d98b8fce Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/BPGA_AlphaStrike.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/Curve_AlphaStrike_Table.uasset b/Content/PlayerSkill/Charge/AlphaStrike/Curve_AlphaStrike_Table.uasset new file mode 100644 index 00000000..6e0b0cf3 Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/Curve_AlphaStrike_Table.uasset differ diff --git a/Content/PlayerSkill/Charge/AlphaStrike/DT_AlphaStrikeAbility.uasset b/Content/PlayerSkill/Charge/AlphaStrike/DT_AlphaStrikeAbility.uasset new file mode 100644 index 00000000..dac71acf Binary files /dev/null and b/Content/PlayerSkill/Charge/AlphaStrike/DT_AlphaStrikeAbility.uasset differ diff --git a/Content/PlayerSkill/Charge/BPGE_ChargeCooldown.uasset b/Content/PlayerSkill/Charge/BPGE_ChargeCooldown.uasset new file mode 100644 index 00000000..9952b6df Binary files /dev/null and b/Content/PlayerSkill/Charge/BPGE_ChargeCooldown.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSlash/BPGA_ActionAirSlash.uasset b/Content/PlayerSkill/Instant/AirSlash/BPGA_ActionAirSlash.uasset new file mode 100644 index 00000000..b3e94260 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSlash/BPGA_ActionAirSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/AM_AirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/AM_AirSwordAura.uasset new file mode 100644 index 00000000..c4da929a Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/AM_AirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGA_ActionAirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_ActionAirSwordAura.uasset new file mode 100644 index 00000000..fc6ca5b8 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_ActionAirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGA_AirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_AirSwordAura.uasset new file mode 100644 index 00000000..7ad86a02 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_AirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGA_MarkerAirSwordProjectile.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_MarkerAirSwordProjectile.uasset new file mode 100644 index 00000000..f8e36db6 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGA_MarkerAirSwordProjectile.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGC_ExplosionTest.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGC_ExplosionTest.uasset new file mode 100644 index 00000000..8f743b12 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGC_ExplosionTest.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGE_DamageAirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGE_DamageAirSwordAura.uasset new file mode 100644 index 00000000..dd11bbb9 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGE_DamageAirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BPGE_ExplosionDamageAirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BPGE_ExplosionDamageAirSwordAura.uasset new file mode 100644 index 00000000..a6df9f46 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BPGE_ExplosionDamageAirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BP_AirSwordAuraProjectile.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BP_AirSwordAuraProjectile.uasset new file mode 100644 index 00000000..35d20fca Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BP_AirSwordAuraProjectile.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BP_ExplosionAirSword.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BP_ExplosionAirSword.uasset new file mode 100644 index 00000000..6862c611 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BP_ExplosionAirSword.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/BP_MovementAirSwordAura.uasset b/Content/PlayerSkill/Instant/AirSwordAura/BP_MovementAirSwordAura.uasset new file mode 100644 index 00000000..4fa102e4 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/BP_MovementAirSwordAura.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/Curve_AirSwordAura_Table.uasset b/Content/PlayerSkill/Instant/AirSwordAura/Curve_AirSwordAura_Table.uasset new file mode 100644 index 00000000..b569fd90 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/Curve_AirSwordAura_Table.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/DT_AirSwordAuraAbility.uasset b/Content/PlayerSkill/Instant/AirSwordAura/DT_AirSwordAuraAbility.uasset new file mode 100644 index 00000000..ae51cd06 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/DT_AirSwordAuraAbility.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_Hit1Test.uasset b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_Hit1Test.uasset new file mode 100644 index 00000000..55af5eb7 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_Hit1Test.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestMark.uasset b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestMark.uasset new file mode 100644 index 00000000..55013238 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestMark.uasset differ diff --git a/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestProjectile.uasset b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestProjectile.uasset new file mode 100644 index 00000000..e740aec5 Binary files /dev/null and b/Content/PlayerSkill/Instant/AirSwordAura/NS_Fire_Magic_TestProjectile.uasset differ diff --git a/Content/PlayerSkill/Instant/BPGE_InstantCooldown.uasset b/Content/PlayerSkill/Instant/BPGE_InstantCooldown.uasset new file mode 100644 index 00000000..33692e1a Binary files /dev/null and b/Content/PlayerSkill/Instant/BPGE_InstantCooldown.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/AM_PoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/AM_PoisonSlash.uasset new file mode 100644 index 00000000..2797ab78 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/AM_PoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/BPGA_ActionPoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/BPGA_ActionPoisonSlash.uasset new file mode 100644 index 00000000..e1ea1543 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/BPGA_ActionPoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/BPGA_PoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/BPGA_PoisonSlash.uasset new file mode 100644 index 00000000..a3264462 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/BPGA_PoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/BPGC_StatusPoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/BPGC_StatusPoisonSlash.uasset new file mode 100644 index 00000000..22be5bc6 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/BPGC_StatusPoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/BPGE_DamagePoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/BPGE_DamagePoisonSlash.uasset new file mode 100644 index 00000000..960e6c75 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/BPGE_DamagePoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/BPGE_StatusPoisonSlash.uasset b/Content/PlayerSkill/Instant/PoisonSlash/BPGE_StatusPoisonSlash.uasset new file mode 100644 index 00000000..fab778a3 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/BPGE_StatusPoisonSlash.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/Curve_PoisonSlash_Table.uasset b/Content/PlayerSkill/Instant/PoisonSlash/Curve_PoisonSlash_Table.uasset new file mode 100644 index 00000000..a9491125 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/Curve_PoisonSlash_Table.uasset differ diff --git a/Content/PlayerSkill/Instant/PoisonSlash/DT_PoisonSlashAbility.uasset b/Content/PlayerSkill/Instant/PoisonSlash/DT_PoisonSlashAbility.uasset new file mode 100644 index 00000000..b58ca230 Binary files /dev/null and b/Content/PlayerSkill/Instant/PoisonSlash/DT_PoisonSlashAbility.uasset differ diff --git a/Content/PlayerSkill/Normal/BPGE_NormalCooldown.uasset b/Content/PlayerSkill/Normal/BPGE_NormalCooldown.uasset new file mode 100644 index 00000000..ca0ddef5 Binary files /dev/null and b/Content/PlayerSkill/Normal/BPGE_NormalCooldown.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/AM_NormalMeleeAttack_.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/AM_NormalMeleeAttack_.uasset new file mode 100644 index 00000000..b0dbccd5 Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/AM_NormalMeleeAttack_.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_ActionNormalMeleeAttack.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_ActionNormalMeleeAttack.uasset new file mode 100644 index 00000000..7f097874 Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_ActionNormalMeleeAttack.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_NormalMeleeAttack.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_NormalMeleeAttack.uasset new file mode 100644 index 00000000..59f78c06 Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGA_NormalMeleeAttack.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGE_DamageNormalMeleeAttack.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGE_DamageNormalMeleeAttack.uasset new file mode 100644 index 00000000..db71443c Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/BPGE_DamageNormalMeleeAttack.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/Curve_NormalMeleeAttack_Table.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/Curve_NormalMeleeAttack_Table.uasset new file mode 100644 index 00000000..fe4d1798 Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/Curve_NormalMeleeAttack_Table.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/DT_NormalMeleeAttackAbility.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/DT_NormalMeleeAttackAbility.uasset new file mode 100644 index 00000000..63e27ebd Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/DT_NormalMeleeAttackAbility.uasset differ diff --git a/Content/PlayerSkill/Normal/NormalMeleeAttack/NewBlueprint.uasset b/Content/PlayerSkill/Normal/NormalMeleeAttack/NewBlueprint.uasset new file mode 100644 index 00000000..80088fdc Binary files /dev/null and b/Content/PlayerSkill/Normal/NormalMeleeAttack/NewBlueprint.uasset differ diff --git a/Content/PlayerSkill/Passive/DamageUp/BPGA_DamageUp.uasset b/Content/PlayerSkill/Passive/DamageUp/BPGA_DamageUp.uasset new file mode 100644 index 00000000..9feaaf39 Binary files /dev/null and b/Content/PlayerSkill/Passive/DamageUp/BPGA_DamageUp.uasset differ diff --git a/Content/PlayerSkill/Passive/DamageUp/BPGE_DamageUp.uasset b/Content/PlayerSkill/Passive/DamageUp/BPGE_DamageUp.uasset new file mode 100644 index 00000000..c51a9391 Binary files /dev/null and b/Content/PlayerSkill/Passive/DamageUp/BPGE_DamageUp.uasset differ diff --git a/Content/PlayerSkill/Passive/DamageUp/Curve_DamageUp_Table.uasset b/Content/PlayerSkill/Passive/DamageUp/Curve_DamageUp_Table.uasset new file mode 100644 index 00000000..4e479a31 Binary files /dev/null and b/Content/PlayerSkill/Passive/DamageUp/Curve_DamageUp_Table.uasset differ diff --git a/Content/PlayerSkill/Passive/DamageUp/DT_DamageUp.uasset b/Content/PlayerSkill/Passive/DamageUp/DT_DamageUp.uasset new file mode 100644 index 00000000..0d3317bc Binary files /dev/null and b/Content/PlayerSkill/Passive/DamageUp/DT_DamageUp.uasset differ diff --git a/Content/Sound/SoundClass/MJGlobal_SoundMix.uasset b/Content/Sound/SoundClass/MJGlobal_SoundMix.uasset new file mode 100644 index 00000000..17771661 Binary files /dev/null and b/Content/Sound/SoundClass/MJGlobal_SoundMix.uasset differ diff --git a/Content/Sound/SoundClass/MJMasterSound.uasset b/Content/Sound/SoundClass/MJMasterSound.uasset new file mode 100644 index 00000000..271d8839 Binary files /dev/null and b/Content/Sound/SoundClass/MJMasterSound.uasset differ diff --git a/Content/Sound/SoundClass/MJMusicSound.uasset b/Content/Sound/SoundClass/MJMusicSound.uasset new file mode 100644 index 00000000..661d1dca Binary files /dev/null and b/Content/Sound/SoundClass/MJMusicSound.uasset differ diff --git a/Content/Sound/SoundClass/MJSFXSound.uasset b/Content/Sound/SoundClass/MJSFXSound.uasset new file mode 100644 index 00000000..2e0bafc7 Binary files /dev/null and b/Content/Sound/SoundClass/MJSFXSound.uasset differ diff --git a/Content/TG/AI/BB_Boss.uasset b/Content/TG/AI/BB_Boss.uasset index bae9055a..f7a66e86 100644 Binary files a/Content/TG/AI/BB_Boss.uasset and b/Content/TG/AI/BB_Boss.uasset differ diff --git a/Content/TG/AI/BPAIC_AIBoss.uasset b/Content/TG/AI/BPAIC_AIBoss.uasset new file mode 100644 index 00000000..1a6b3aa0 Binary files /dev/null and b/Content/TG/AI/BPAIC_AIBoss.uasset differ diff --git a/Content/TG/AI/BP_AIBoss.uasset b/Content/TG/AI/BP_AIBoss.uasset new file mode 100644 index 00000000..44ea2b03 Binary files /dev/null and b/Content/TG/AI/BP_AIBoss.uasset differ diff --git a/Content/TG/AI/BP_AIBossCharacter.uasset b/Content/TG/AI/BP_AIBossCharacter.uasset deleted file mode 100644 index 7481ce69..00000000 Binary files a/Content/TG/AI/BP_AIBossCharacter.uasset and /dev/null differ diff --git a/Content/TG/AI/BP_AIBossCircleExplode.uasset b/Content/TG/AI/BP_AIBossCircleExplode.uasset new file mode 100644 index 00000000..3b55c89f Binary files /dev/null and b/Content/TG/AI/BP_AIBossCircleExplode.uasset differ diff --git a/Content/TG/AI/BP_AIBossPlaneProjectile.uasset b/Content/TG/AI/BP_AIBossPlaneProjectile.uasset new file mode 100644 index 00000000..a04ff3d9 Binary files /dev/null and b/Content/TG/AI/BP_AIBossPlaneProjectile.uasset differ diff --git a/Content/TG/AI/BP_AIBossProjectile.uasset b/Content/TG/AI/BP_AIBossProjectile.uasset new file mode 100644 index 00000000..cf7a98a5 Binary files /dev/null and b/Content/TG/AI/BP_AIBossProjectile.uasset differ diff --git a/Content/TG/AI/BP_MJAIBossAICTG.uasset b/Content/TG/AI/BP_MJAIBossAICTG.uasset deleted file mode 100644 index d0ea0008..00000000 Binary files a/Content/TG/AI/BP_MJAIBossAICTG.uasset and /dev/null differ diff --git a/Content/TG/AI/BP_SpawnPointActor.uasset b/Content/TG/AI/BP_SpawnPointActor.uasset deleted file mode 100644 index 0cb668f0..00000000 Binary files a/Content/TG/AI/BP_SpawnPointActor.uasset and /dev/null differ diff --git a/Content/TG/AI/BT/BT_MJBossPhase1.uasset b/Content/TG/AI/BT/BT_MJBossPhase1.uasset index 993b14f6..3c1a1f75 100644 Binary files a/Content/TG/AI/BT/BT_MJBossPhase1.uasset and b/Content/TG/AI/BT/BT_MJBossPhase1.uasset differ diff --git a/Content/TG/AI/BT/BT_MJBossPhase2.uasset b/Content/TG/AI/BT/BT_MJBossPhase2.uasset index d3d1aec7..08dbd057 100644 Binary files a/Content/TG/AI/BT/BT_MJBossPhase2.uasset and b/Content/TG/AI/BT/BT_MJBossPhase2.uasset differ diff --git a/Content/TG/AI/BT/BT_MJBossPhase3.uasset b/Content/TG/AI/BT/BT_MJBossPhase3.uasset index 1954e909..da3f457d 100644 Binary files a/Content/TG/AI/BT/BT_MJBossPhase3.uasset and b/Content/TG/AI/BT/BT_MJBossPhase3.uasset differ diff --git a/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget.uasset b/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget.uasset index 74e1958e..bee1b24b 100644 Binary files a/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget.uasset and b/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget.uasset differ diff --git a/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget_SpawnCircle.uasset b/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget_SpawnCircle.uasset new file mode 100644 index 00000000..628eef3d Binary files /dev/null and b/Content/TG/AI/EQS/EQS_AIFindRandomPosToTarget_SpawnCircle.uasset differ diff --git a/Content/TG/AI/EQS/EQS_AISpawnPoints.uasset b/Content/TG/AI/EQS/EQS_AISpawnPoints.uasset index 25896b03..5327f0b5 100644 Binary files a/Content/TG/AI/EQS/EQS_AISpawnPoints.uasset and b/Content/TG/AI/EQS/EQS_AISpawnPoints.uasset differ diff --git a/Content/TG/AI/EQS/EQS_TestingPawn.uasset b/Content/TG/AI/EQS/EQS_TestingPawn.uasset index df9621c9..13ee3dbb 100644 Binary files a/Content/TG/AI/EQS/EQS_TestingPawn.uasset and b/Content/TG/AI/EQS/EQS_TestingPawn.uasset differ diff --git a/Content/TG/AI/EnvQueryContext_TargetActor_Player.uasset b/Content/TG/AI/EQS/EnvQueryContext_TargetActor_Player.uasset similarity index 80% rename from Content/TG/AI/EnvQueryContext_TargetActor_Player.uasset rename to Content/TG/AI/EQS/EnvQueryContext_TargetActor_Player.uasset index f62d564c..e2354700 100644 Binary files a/Content/TG/AI/EnvQueryContext_TargetActor_Player.uasset and b/Content/TG/AI/EQS/EnvQueryContext_TargetActor_Player.uasset differ diff --git a/Content/TG/AI/EnvContext_Spawn.uasset b/Content/TG/AI/EnvContext_Spawn.uasset deleted file mode 100644 index 4b312419..00000000 Binary files a/Content/TG/AI/EnvContext_Spawn.uasset and /dev/null differ diff --git a/Content/TG/AI/Service/BTService_CheckState.uasset b/Content/TG/AI/Service/BTService_CheckState.uasset index d0195235..91cf0b9f 100644 Binary files a/Content/TG/AI/Service/BTService_CheckState.uasset and b/Content/TG/AI/Service/BTService_CheckState.uasset differ diff --git a/Content/TG/AI/Service/BTService_CreatePossibility.uasset b/Content/TG/AI/Service/BTService_CreatePossibility.uasset index bd8873cc..7ff47f94 100644 Binary files a/Content/TG/AI/Service/BTService_CreatePossibility.uasset and b/Content/TG/AI/Service/BTService_CreatePossibility.uasset differ diff --git a/Content/TG/AI/Task/BTTask_CheckIsRageEnd.uasset b/Content/TG/AI/Task/BTTask_CheckIsRageEnd.uasset index 14e3e2ed..72293b69 100644 Binary files a/Content/TG/AI/Task/BTTask_CheckIsRageEnd.uasset and b/Content/TG/AI/Task/BTTask_CheckIsRageEnd.uasset differ diff --git a/Content/TG/AI/Task/BTTask_IncrementRageCount.uasset b/Content/TG/AI/Task/BTTask_IncrementRageCount.uasset index 6ee8b9f6..daf83273 100644 Binary files a/Content/TG/AI/Task/BTTask_IncrementRageCount.uasset and b/Content/TG/AI/Task/BTTask_IncrementRageCount.uasset differ diff --git a/Content/TG/AI/Task/BTTask_MeleeAttack.uasset b/Content/TG/AI/Task/BTTask_MeleeAttack.uasset index 2c99e838..2e06fc4e 100644 Binary files a/Content/TG/AI/Task/BTTask_MeleeAttack.uasset and b/Content/TG/AI/Task/BTTask_MeleeAttack.uasset differ diff --git a/Content/TG/AI/Task/BTTask_SetSpeedAndState.uasset b/Content/TG/AI/Task/BTTask_SetSpeedAndState.uasset index bd065cdd..46dd2510 100644 Binary files a/Content/TG/AI/Task/BTTask_SetSpeedAndState.uasset and b/Content/TG/AI/Task/BTTask_SetSpeedAndState.uasset differ diff --git a/Content/TG/AI/Task/BTTask_Spawn360Circle.uasset b/Content/TG/AI/Task/BTTask_Spawn360Circle.uasset new file mode 100644 index 00000000..f0623e56 Binary files /dev/null and b/Content/TG/AI/Task/BTTask_Spawn360Circle.uasset differ diff --git a/Content/TG/AI/Task/BTTask_SpawnAbilityPower.uasset b/Content/TG/AI/Task/BTTask_SpawnAbilityPower.uasset new file mode 100644 index 00000000..e79ffc86 Binary files /dev/null and b/Content/TG/AI/Task/BTTask_SpawnAbilityPower.uasset differ diff --git a/Content/TG/AI/Task/BTTask_SpawnCircle.uasset b/Content/TG/AI/Task/BTTask_SpawnCircle.uasset new file mode 100644 index 00000000..3af87651 Binary files /dev/null and b/Content/TG/AI/Task/BTTask_SpawnCircle.uasset differ diff --git a/Content/TG/AI/WBP_AIBossHpBar.uasset b/Content/TG/AI/WBP_AIBossHpBar.uasset new file mode 100644 index 00000000..67a40c4c Binary files /dev/null and b/Content/TG/AI/WBP_AIBossHpBar.uasset differ diff --git a/Content/TG/AI/WBP_AIBossHpBarTest.uasset b/Content/TG/AI/WBP_AIBossHpBarTest.uasset deleted file mode 100644 index 7e48924b..00000000 Binary files a/Content/TG/AI/WBP_AIBossHpBarTest.uasset and /dev/null differ diff --git a/Content/TG/Actor/BP_AISpwanPoint.uasset b/Content/TG/Actor/BP_AISpwanPoint.uasset new file mode 100644 index 00000000..e622e13f Binary files /dev/null and b/Content/TG/Actor/BP_AISpwanPoint.uasset differ diff --git a/Content/TG/Actor/BP_DummyActor_TG.uasset b/Content/TG/Actor/BP_DummyActor_TG.uasset new file mode 100644 index 00000000..76deb716 Binary files /dev/null and b/Content/TG/Actor/BP_DummyActor_TG.uasset differ diff --git a/Content/TG/BP_DummyObjectTG.uasset b/Content/TG/Actor/BP_DummyObjectTG.uasset similarity index 61% rename from Content/TG/BP_DummyObjectTG.uasset rename to Content/TG/Actor/BP_DummyObjectTG.uasset index 13299768..f5a45e1d 100644 Binary files a/Content/TG/BP_DummyObjectTG.uasset and b/Content/TG/Actor/BP_DummyObjectTG.uasset differ diff --git a/Content/TG/Actor/BP_HealthUp_TG.uasset b/Content/TG/Actor/BP_HealthUp_TG.uasset new file mode 100644 index 00000000..8dcfb78a Binary files /dev/null and b/Content/TG/Actor/BP_HealthUp_TG.uasset differ diff --git a/Content/TG/Actor/BP_MiniMapBackground.uasset b/Content/TG/Actor/BP_MiniMapBackground.uasset new file mode 100644 index 00000000..7b6f134f Binary files /dev/null and b/Content/TG/Actor/BP_MiniMapBackground.uasset differ diff --git a/Content/TG/BP_Plate_TG.uasset b/Content/TG/Actor/BP_Plate_TG.uasset similarity index 82% rename from Content/TG/BP_Plate_TG.uasset rename to Content/TG/Actor/BP_Plate_TG.uasset index 5fddb108..58f1279a 100644 Binary files a/Content/TG/BP_Plate_TG.uasset and b/Content/TG/Actor/BP_Plate_TG.uasset differ diff --git a/Content/TG/Actor/BP_PortalToDungeon.uasset b/Content/TG/Actor/BP_PortalToDungeon.uasset new file mode 100644 index 00000000..f2314c44 Binary files /dev/null and b/Content/TG/Actor/BP_PortalToDungeon.uasset differ diff --git a/Content/TG/Actor/BP_PortalToNextDungeon.uasset b/Content/TG/Actor/BP_PortalToNextDungeon.uasset new file mode 100644 index 00000000..2b0adc43 Binary files /dev/null and b/Content/TG/Actor/BP_PortalToNextDungeon.uasset differ diff --git a/Content/TG/Actor/BP_PortalToTown.uasset b/Content/TG/Actor/BP_PortalToTown.uasset new file mode 100644 index 00000000..e8dcc20c Binary files /dev/null and b/Content/TG/Actor/BP_PortalToTown.uasset differ diff --git a/Content/TG/Actor/BP_SavePoint.uasset b/Content/TG/Actor/BP_SavePoint.uasset new file mode 100644 index 00000000..05c3b003 Binary files /dev/null and b/Content/TG/Actor/BP_SavePoint.uasset differ diff --git a/Content/TG/Actor/BP_TestPortalToDungeon_TG.uasset b/Content/TG/Actor/BP_TestPortalToDungeon_TG.uasset new file mode 100644 index 00000000..a6087848 Binary files /dev/null and b/Content/TG/Actor/BP_TestPortalToDungeon_TG.uasset differ diff --git a/Content/TG/BP_DummyActor_TG.uasset b/Content/TG/BP_DummyActor_TG.uasset deleted file mode 100644 index b6d265a3..00000000 Binary files a/Content/TG/BP_DummyActor_TG.uasset and /dev/null differ diff --git a/Content/TG/BP_DungeonTestSpawner.uasset b/Content/TG/BP_DungeonTestSpawner.uasset new file mode 100644 index 00000000..0e6da91b Binary files /dev/null and b/Content/TG/BP_DungeonTestSpawner.uasset differ diff --git a/Content/TG/BP_HealthUp_TG.uasset b/Content/TG/BP_HealthUp_TG.uasset index 7ec93e0c..3b79f674 100644 Binary files a/Content/TG/BP_HealthUp_TG.uasset and b/Content/TG/BP_HealthUp_TG.uasset differ diff --git a/Content/TG/BP_MJSceneCapture.uasset b/Content/TG/BP_MJSceneCapture.uasset new file mode 100644 index 00000000..4668b21d Binary files /dev/null and b/Content/TG/BP_MJSceneCapture.uasset differ diff --git a/Content/TG/BP_TG_MJPlayerCharacter.uasset b/Content/TG/BP_TG_MJPlayerCharacter.uasset deleted file mode 100644 index 61edb151..00000000 Binary files a/Content/TG/BP_TG_MJPlayerCharacter.uasset and /dev/null differ diff --git a/Content/TG/BP_TestPortalToDungeon_TG.uasset b/Content/TG/BP_TestPortalToDungeon_TG.uasset deleted file mode 100644 index beb6c75d..00000000 Binary files a/Content/TG/BP_TestPortalToDungeon_TG.uasset and /dev/null differ diff --git a/Content/TG/BP_TestPortalToTown_TG.uasset b/Content/TG/BP_TestPortalToTown_TG.uasset deleted file mode 100644 index c5065321..00000000 Binary files a/Content/TG/BP_TestPortalToTown_TG.uasset and /dev/null differ diff --git a/Content/TG/DEPRECATED_BP_TG_MJPlayerCharacter.uasset b/Content/TG/DEPRECATED_BP_TG_MJPlayerCharacter.uasset new file mode 100644 index 00000000..c7ad06f9 Binary files /dev/null and b/Content/TG/DEPRECATED_BP_TG_MJPlayerCharacter.uasset differ diff --git a/Content/TG/GameData/AISpawn_Static_Data.uasset b/Content/TG/GameData/AISpawn_Static_Data.uasset new file mode 100644 index 00000000..6856f5b2 Binary files /dev/null and b/Content/TG/GameData/AISpawn_Static_Data.uasset differ diff --git a/Content/TG/GameData/AISpawn_Wave_Data.uasset b/Content/TG/GameData/AISpawn_Wave_Data.uasset new file mode 100644 index 00000000..396783c0 Binary files /dev/null and b/Content/TG/GameData/AISpawn_Wave_Data.uasset differ diff --git a/Content/TG/GameData/BP_SaveGame_TG.uasset b/Content/TG/GameData/BP_SaveGame_TG.uasset deleted file mode 100644 index 57b4b564..00000000 Binary files a/Content/TG/GameData/BP_SaveGame_TG.uasset and /dev/null differ diff --git a/Content/TG/GameData/MapNames_TG.uasset b/Content/TG/GameData/MapNames_TG.uasset deleted file mode 100644 index bff2092d..00000000 Binary files a/Content/TG/GameData/MapNames_TG.uasset and /dev/null differ diff --git a/Content/TG/StaticMesh/SM_MJArrow.uasset b/Content/TG/StaticMesh/SM_MJArrow.uasset new file mode 100644 index 00000000..53b9b61f Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJArrow.uasset differ diff --git a/Content/TG/StaticMesh/SM_MJMiniMapPortal.uasset b/Content/TG/StaticMesh/SM_MJMiniMapPortal.uasset new file mode 100644 index 00000000..325f615d Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJMiniMapPortal.uasset differ diff --git a/Content/TG/StaticMesh/SM_MJPlane.uasset b/Content/TG/StaticMesh/SM_MJPlane.uasset new file mode 100644 index 00000000..b049c7b6 Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJPlane.uasset differ diff --git a/Content/TG/StaticMesh/SM_MJPortal.uasset b/Content/TG/StaticMesh/SM_MJPortal.uasset new file mode 100644 index 00000000..2b22ddba Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJPortal.uasset differ diff --git a/Content/TG/StaticMesh/SM_MJSock.uasset b/Content/TG/StaticMesh/SM_MJSock.uasset new file mode 100644 index 00000000..24f68c9c Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJSock.uasset differ diff --git a/Content/TG/StaticMesh/SM_MJSphere.uasset b/Content/TG/StaticMesh/SM_MJSphere.uasset new file mode 100644 index 00000000..181e11de Binary files /dev/null and b/Content/TG/StaticMesh/SM_MJSphere.uasset differ diff --git a/Content/TG/StaticMesh/SM_Portal.uasset b/Content/TG/StaticMesh/SM_Portal.uasset new file mode 100644 index 00000000..bff781b5 Binary files /dev/null and b/Content/TG/StaticMesh/SM_Portal.uasset differ diff --git a/Content/TG/TestWBP/WBP_ComboBoxItem.uasset b/Content/TG/TestWBP/WBP_ComboBoxItem.uasset new file mode 100644 index 00000000..25e575f3 Binary files /dev/null and b/Content/TG/TestWBP/WBP_ComboBoxItem.uasset differ diff --git a/Content/TG/TestWBP/WBP_DungeonEndMenu.uasset b/Content/TG/TestWBP/WBP_DungeonEndMenu.uasset new file mode 100644 index 00000000..cc27b7d1 Binary files /dev/null and b/Content/TG/TestWBP/WBP_DungeonEndMenu.uasset differ diff --git a/Content/TG/TestWBP/WBP_DungeonMap.uasset b/Content/TG/TestWBP/WBP_DungeonMap.uasset new file mode 100644 index 00000000..dc57cb0f Binary files /dev/null and b/Content/TG/TestWBP/WBP_DungeonMap.uasset differ diff --git a/Content/TG/TestWBP/WBP_Edge.uasset b/Content/TG/TestWBP/WBP_Edge.uasset new file mode 100644 index 00000000..146dcf12 Binary files /dev/null and b/Content/TG/TestWBP/WBP_Edge.uasset differ diff --git a/Content/TG/TestWBP/WBP_GameFlowHUD.uasset b/Content/TG/TestWBP/WBP_GameFlowHUD.uasset new file mode 100644 index 00000000..59687e4a Binary files /dev/null and b/Content/TG/TestWBP/WBP_GameFlowHUD.uasset differ diff --git a/Content/TG/TestWBP/WBP_LoadGame.uasset b/Content/TG/TestWBP/WBP_LoadGame.uasset new file mode 100644 index 00000000..3b50cd6e Binary files /dev/null and b/Content/TG/TestWBP/WBP_LoadGame.uasset differ diff --git a/Content/TG/TestWBP/WBP_LoadGameSlot.uasset b/Content/TG/TestWBP/WBP_LoadGameSlot.uasset new file mode 100644 index 00000000..27b9472c Binary files /dev/null and b/Content/TG/TestWBP/WBP_LoadGameSlot.uasset differ diff --git a/Content/TG/TestWBP/WBP_LoadingScreen.uasset b/Content/TG/TestWBP/WBP_LoadingScreen.uasset new file mode 100644 index 00000000..94d7c07a Binary files /dev/null and b/Content/TG/TestWBP/WBP_LoadingScreen.uasset differ diff --git a/Content/TG/TestWBP/WBP_MainMenu.uasset b/Content/TG/TestWBP/WBP_MainMenu.uasset new file mode 100644 index 00000000..c1e917e7 Binary files /dev/null and b/Content/TG/TestWBP/WBP_MainMenu.uasset differ diff --git a/Content/TG/TestWBP/WBP_MiniMap.uasset b/Content/TG/TestWBP/WBP_MiniMap.uasset new file mode 100644 index 00000000..5ab281c6 Binary files /dev/null and b/Content/TG/TestWBP/WBP_MiniMap.uasset differ diff --git a/Content/TG/TestWBP/WBP_NewGamePopUp.uasset b/Content/TG/TestWBP/WBP_NewGamePopUp.uasset new file mode 100644 index 00000000..a115c0cd Binary files /dev/null and b/Content/TG/TestWBP/WBP_NewGamePopUp.uasset differ diff --git a/Content/TG/TestWBP/WBP_Node.uasset b/Content/TG/TestWBP/WBP_Node.uasset new file mode 100644 index 00000000..e6a3c645 Binary files /dev/null and b/Content/TG/TestWBP/WBP_Node.uasset differ diff --git a/Content/TG/TestWBP/WBP_PauseMenu.uasset b/Content/TG/TestWBP/WBP_PauseMenu.uasset new file mode 100644 index 00000000..c72982e6 Binary files /dev/null and b/Content/TG/TestWBP/WBP_PauseMenu.uasset differ diff --git a/Content/TG/TestWBP/WBP_PopUpMsgWidget.uasset b/Content/TG/TestWBP/WBP_PopUpMsgWidget.uasset new file mode 100644 index 00000000..29c5b55e Binary files /dev/null and b/Content/TG/TestWBP/WBP_PopUpMsgWidget.uasset differ diff --git a/Content/TG/TestWBP/WBP_PopUpMsgWidget_Notify.uasset b/Content/TG/TestWBP/WBP_PopUpMsgWidget_Notify.uasset new file mode 100644 index 00000000..06d74909 Binary files /dev/null and b/Content/TG/TestWBP/WBP_PopUpMsgWidget_Notify.uasset differ diff --git a/Content/TG/TestWBP/WBP_Settings.uasset b/Content/TG/TestWBP/WBP_Settings.uasset new file mode 100644 index 00000000..fa9bc29a Binary files /dev/null and b/Content/TG/TestWBP/WBP_Settings.uasset differ diff --git a/Content/TG/TestWBP/WBP_TestCursor.uasset b/Content/TG/TestWBP/WBP_TestCursor.uasset new file mode 100644 index 00000000..76f141a1 Binary files /dev/null and b/Content/TG/TestWBP/WBP_TestCursor.uasset differ diff --git a/Content/TG/Texture/Background.uasset b/Content/TG/Texture/Background.uasset new file mode 100644 index 00000000..bc994590 Binary files /dev/null and b/Content/TG/Texture/Background.uasset differ diff --git a/Content/TG/Texture/Cursor.uasset b/Content/TG/Texture/Cursor.uasset new file mode 100644 index 00000000..70460723 Binary files /dev/null and b/Content/TG/Texture/Cursor.uasset differ diff --git a/Content/TG/Texture/MiniMap_Canvas.uasset b/Content/TG/Texture/MiniMap_Canvas.uasset new file mode 100644 index 00000000..96b190dc Binary files /dev/null and b/Content/TG/Texture/MiniMap_Canvas.uasset differ diff --git a/Content/TG/WBP_DungeonMapTest.uasset b/Content/TG/WBP_DungeonMapTest.uasset deleted file mode 100644 index 7d399226..00000000 Binary files a/Content/TG/WBP_DungeonMapTest.uasset and /dev/null differ diff --git a/Content/TG/WBP_LoadingScreen.uasset b/Content/TG/WBP_LoadingScreen.uasset deleted file mode 100644 index cd98621f..00000000 Binary files a/Content/TG/WBP_LoadingScreen.uasset and /dev/null differ diff --git a/Content/TG/WBP_NodeTest.uasset b/Content/TG/WBP_NodeTest.uasset deleted file mode 100644 index 0b73c825..00000000 Binary files a/Content/TG/WBP_NodeTest.uasset and /dev/null differ diff --git a/Content/UI/Font/CookieRun_Black.uasset b/Content/UI/Font/CookieRun_Black.uasset new file mode 100644 index 00000000..67d60c3f Binary files /dev/null and b/Content/UI/Font/CookieRun_Black.uasset differ diff --git a/Content/UI/Font/CookieRun_Black_Font.uasset b/Content/UI/Font/CookieRun_Black_Font.uasset new file mode 100644 index 00000000..33ffd0cf Binary files /dev/null and b/Content/UI/Font/CookieRun_Black_Font.uasset differ diff --git a/Content/UI/Font/CookieRun_Bold.uasset b/Content/UI/Font/CookieRun_Bold.uasset new file mode 100644 index 00000000..ec7bfaf7 Binary files /dev/null and b/Content/UI/Font/CookieRun_Bold.uasset differ diff --git a/Content/UI/Font/CookieRun_Bold_Font.uasset b/Content/UI/Font/CookieRun_Bold_Font.uasset new file mode 100644 index 00000000..6e96e351 Binary files /dev/null and b/Content/UI/Font/CookieRun_Bold_Font.uasset differ diff --git a/Content/UI/Font/CookieRun_Regular.uasset b/Content/UI/Font/CookieRun_Regular.uasset new file mode 100644 index 00000000..cdee5689 Binary files /dev/null and b/Content/UI/Font/CookieRun_Regular.uasset differ diff --git a/Content/UI/Font/CookieRun_Regular_Font.uasset b/Content/UI/Font/CookieRun_Regular_Font.uasset new file mode 100644 index 00000000..12e4b978 Binary files /dev/null and b/Content/UI/Font/CookieRun_Regular_Font.uasset differ diff --git a/Content/UI/Texture/2.png b/Content/UI/Texture/2.png new file mode 100644 index 00000000..9074ff94 Binary files /dev/null and b/Content/UI/Texture/2.png differ diff --git a/Content/UI/Texture/2.uasset b/Content/UI/Texture/2.uasset new file mode 100644 index 00000000..bbf709e0 Binary files /dev/null and b/Content/UI/Texture/2.uasset differ diff --git a/Content/UI/Texture/Critical_strike_nobg.png b/Content/UI/Texture/Critical_strike_nobg.png new file mode 100644 index 00000000..2ec1a042 Binary files /dev/null and b/Content/UI/Texture/Critical_strike_nobg.png differ diff --git a/Content/UI/Texture/Critical_strike_nobg.uasset b/Content/UI/Texture/Critical_strike_nobg.uasset new file mode 100644 index 00000000..4551d237 Binary files /dev/null and b/Content/UI/Texture/Critical_strike_nobg.uasset differ diff --git a/Content/UI/Texture/DialogueBox.uasset b/Content/UI/Texture/DialogueBox.uasset new file mode 100644 index 00000000..05f3a7ee Binary files /dev/null and b/Content/UI/Texture/DialogueBox.uasset differ diff --git a/Content/UI/Texture/Firewood.png b/Content/UI/Texture/Firewood.png new file mode 100644 index 00000000..b97dd8a7 Binary files /dev/null and b/Content/UI/Texture/Firewood.png differ diff --git a/Content/UI/Texture/Firewood.uasset b/Content/UI/Texture/Firewood.uasset new file mode 100644 index 00000000..ac544475 Binary files /dev/null and b/Content/UI/Texture/Firewood.uasset differ diff --git a/Content/UI/Texture/HPPotion.png b/Content/UI/Texture/HPPotion.png new file mode 100644 index 00000000..84fd0352 Binary files /dev/null and b/Content/UI/Texture/HPPotion.png differ diff --git a/Content/UI/Texture/HPPotion.uasset b/Content/UI/Texture/HPPotion.uasset new file mode 100644 index 00000000..d4712076 Binary files /dev/null and b/Content/UI/Texture/HPPotion.uasset differ diff --git a/Content/UI/Texture/MPPotion.png b/Content/UI/Texture/MPPotion.png new file mode 100644 index 00000000..5b7cb050 Binary files /dev/null and b/Content/UI/Texture/MPPotion.png differ diff --git a/Content/UI/Texture/MPPotion.uasset b/Content/UI/Texture/MPPotion.uasset new file mode 100644 index 00000000..9fed2954 Binary files /dev/null and b/Content/UI/Texture/MPPotion.uasset differ diff --git a/Content/UI/Texture/Mageskill_09_meteor.png b/Content/UI/Texture/Mageskill_09_meteor.png new file mode 100644 index 00000000..9ab276fb Binary files /dev/null and b/Content/UI/Texture/Mageskill_09_meteor.png differ diff --git a/Content/UI/Texture/Mageskill_09_meteor.uasset b/Content/UI/Texture/Mageskill_09_meteor.uasset new file mode 100644 index 00000000..8c1f4202 Binary files /dev/null and b/Content/UI/Texture/Mageskill_09_meteor.uasset differ diff --git a/Content/UI/Texture/PlayerMug1.png b/Content/UI/Texture/PlayerMug1.png new file mode 100644 index 00000000..185beab2 Binary files /dev/null and b/Content/UI/Texture/PlayerMug1.png differ diff --git a/Content/UI/Texture/PlayerMug1.uasset b/Content/UI/Texture/PlayerMug1.uasset new file mode 100644 index 00000000..065d1918 Binary files /dev/null and b/Content/UI/Texture/PlayerMug1.uasset differ diff --git a/Content/UI/Texture/PlayerProfile.PNG b/Content/UI/Texture/PlayerProfile.PNG new file mode 100644 index 00000000..96e287da Binary files /dev/null and b/Content/UI/Texture/PlayerProfile.PNG differ diff --git a/Content/UI/Texture/PlayerProfile.uasset b/Content/UI/Texture/PlayerProfile.uasset new file mode 100644 index 00000000..b8ed94e7 Binary files /dev/null and b/Content/UI/Texture/PlayerProfile.uasset differ diff --git a/Content/UI/Texture/Playerill.png b/Content/UI/Texture/Playerill.png new file mode 100644 index 00000000..8a9adce0 Binary files /dev/null and b/Content/UI/Texture/Playerill.png differ diff --git a/Content/UI/Texture/Playerill.uasset b/Content/UI/Texture/Playerill.uasset new file mode 100644 index 00000000..80eca995 Binary files /dev/null and b/Content/UI/Texture/Playerill.uasset differ diff --git a/Content/UI/Texture/Skill_Poison.png b/Content/UI/Texture/Skill_Poison.png new file mode 100644 index 00000000..f411a29c Binary files /dev/null and b/Content/UI/Texture/Skill_Poison.png differ diff --git a/Content/UI/Texture/Skill_Poison.uasset b/Content/UI/Texture/Skill_Poison.uasset new file mode 100644 index 00000000..2f73428c Binary files /dev/null and b/Content/UI/Texture/Skill_Poison.uasset differ diff --git a/Content/UI/Texture/Skill_SwordBlock_nb.png b/Content/UI/Texture/Skill_SwordBlock_nb.png new file mode 100644 index 00000000..54181487 Binary files /dev/null and b/Content/UI/Texture/Skill_SwordBlock_nb.png differ diff --git a/Content/UI/Texture/Skill_SwordBlock_nb.uasset b/Content/UI/Texture/Skill_SwordBlock_nb.uasset new file mode 100644 index 00000000..6cb81ab0 Binary files /dev/null and b/Content/UI/Texture/Skill_SwordBlock_nb.uasset differ diff --git a/Content/UI/Texture/Stones.png b/Content/UI/Texture/Stones.png new file mode 100644 index 00000000..7a355c89 Binary files /dev/null and b/Content/UI/Texture/Stones.png differ diff --git a/Content/UI/Texture/Stones.uasset b/Content/UI/Texture/Stones.uasset new file mode 100644 index 00000000..fe5de709 Binary files /dev/null and b/Content/UI/Texture/Stones.uasset differ diff --git a/Content/UI/Texture/Sword_09.png b/Content/UI/Texture/Sword_09.png new file mode 100644 index 00000000..889d17a9 Binary files /dev/null and b/Content/UI/Texture/Sword_09.png differ diff --git a/Content/UI/Texture/Sword_09.uasset b/Content/UI/Texture/Sword_09.uasset new file mode 100644 index 00000000..c3104517 Binary files /dev/null and b/Content/UI/Texture/Sword_09.uasset differ diff --git a/Content/UI/Texture/Ykey.uasset b/Content/UI/Texture/Ykey.uasset deleted file mode 100644 index 62e174e4..00000000 Binary files a/Content/UI/Texture/Ykey.uasset and /dev/null differ diff --git a/Content/UI/Texture/mong.png b/Content/UI/Texture/mong.png new file mode 100644 index 00000000..93d3afd6 Binary files /dev/null and b/Content/UI/Texture/mong.png differ diff --git a/Content/UI/Texture/mong.uasset b/Content/UI/Texture/mong.uasset new file mode 100644 index 00000000..c4dc96d6 Binary files /dev/null and b/Content/UI/Texture/mong.uasset differ diff --git a/Content/UI/Texture/past.png b/Content/UI/Texture/past.png new file mode 100644 index 00000000..2739405e Binary files /dev/null and b/Content/UI/Texture/past.png differ diff --git a/Content/UI/Texture/past.uasset b/Content/UI/Texture/past.uasset new file mode 100644 index 00000000..abed8378 Binary files /dev/null and b/Content/UI/Texture/past.uasset differ diff --git a/Content/UI/Texture/player.png b/Content/UI/Texture/player.png new file mode 100644 index 00000000..58bad322 Binary files /dev/null and b/Content/UI/Texture/player.png differ diff --git a/Content/UI/Texture/player.uasset b/Content/UI/Texture/player.uasset new file mode 100644 index 00000000..5e6862d2 Binary files /dev/null and b/Content/UI/Texture/player.uasset differ diff --git a/Content/UI/Texture/pngegg.png b/Content/UI/Texture/pngegg.png new file mode 100644 index 00000000..0fcaa8c6 Binary files /dev/null and b/Content/UI/Texture/pngegg.png differ diff --git a/Content/UI/Texture/pngegg.uasset b/Content/UI/Texture/pngegg.uasset new file mode 100644 index 00000000..beb560c8 Binary files /dev/null and b/Content/UI/Texture/pngegg.uasset differ diff --git a/Content/UI/Texture/testImage.uasset b/Content/UI/Texture/testImage.uasset deleted file mode 100644 index 679c2317..00000000 Binary files a/Content/UI/Texture/testImage.uasset and /dev/null differ diff --git a/Content/UI/WBP/Dialogue/BP_MJBacklog.uasset b/Content/UI/WBP/Dialogue/BP_MJBacklog.uasset index cf5fe0b1..158623bb 100644 Binary files a/Content/UI/WBP/Dialogue/BP_MJBacklog.uasset and b/Content/UI/WBP/Dialogue/BP_MJBacklog.uasset differ diff --git a/Content/UI/WBP/Dialogue/BP_MJDialogueWidget.uasset b/Content/UI/WBP/Dialogue/BP_MJDialogueWidget.uasset index be5eae72..a697e083 100644 Binary files a/Content/UI/WBP/Dialogue/BP_MJDialogueWidget.uasset and b/Content/UI/WBP/Dialogue/BP_MJDialogueWidget.uasset differ diff --git a/Content/UI/WBP/Dialogue/WBP_Choice.uasset b/Content/UI/WBP/Dialogue/WBP_Choice.uasset new file mode 100644 index 00000000..f5d68af7 Binary files /dev/null and b/Content/UI/WBP/Dialogue/WBP_Choice.uasset differ diff --git a/Content/UI/WBP/HUD/Bar/WBP_ExpBar.uasset b/Content/UI/WBP/HUD/Bar/WBP_ExpBar.uasset new file mode 100644 index 00000000..e3600d71 Binary files /dev/null and b/Content/UI/WBP/HUD/Bar/WBP_ExpBar.uasset differ diff --git a/Content/UI/WBP/HUD/Bar/WBP_HealthBar.uasset b/Content/UI/WBP/HUD/Bar/WBP_HealthBar.uasset new file mode 100644 index 00000000..8152bd7f Binary files /dev/null and b/Content/UI/WBP/HUD/Bar/WBP_HealthBar.uasset differ diff --git a/Content/UI/WBP/HUD/Bar/WBP_ManaBar.uasset b/Content/UI/WBP/HUD/Bar/WBP_ManaBar.uasset new file mode 100644 index 00000000..4445a04c Binary files /dev/null and b/Content/UI/WBP/HUD/Bar/WBP_ManaBar.uasset differ diff --git a/Content/UI/WBP/HUD/Bar/WBP_StaminaBar.uasset b/Content/UI/WBP/HUD/Bar/WBP_StaminaBar.uasset new file mode 100644 index 00000000..930d4a91 Binary files /dev/null and b/Content/UI/WBP/HUD/Bar/WBP_StaminaBar.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/T_HPPotion.uasset b/Content/UI/WBP/HUD/Inventory/T_HPPotion.uasset new file mode 100644 index 00000000..fd5841ef Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/T_HPPotion.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/T_MPPotion.uasset b/Content/UI/WBP/HUD/Inventory/T_MPPotion.uasset new file mode 100644 index 00000000..06100623 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/T_MPPotion.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/T_Stone.uasset b/Content/UI/WBP/HUD/Inventory/T_Stone.uasset new file mode 100644 index 00000000..ce270d0c Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/T_Stone.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/T_Weapon.uasset b/Content/UI/WBP/HUD/Inventory/T_Weapon.uasset new file mode 100644 index 00000000..b4114970 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/T_Weapon.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/T_Wood.uasset b/Content/UI/WBP/HUD/Inventory/T_Wood.uasset new file mode 100644 index 00000000..ff3eb075 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/T_Wood.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/TestItem.uasset b/Content/UI/WBP/HUD/Inventory/TestItem.uasset new file mode 100644 index 00000000..5a2e16f3 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/TestItem.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/TestItemPotion.uasset b/Content/UI/WBP/HUD/Inventory/TestItemPotion.uasset new file mode 100644 index 00000000..7fd431f0 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/TestItemPotion.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/TestItemPotion2.uasset b/Content/UI/WBP/HUD/Inventory/TestItemPotion2.uasset new file mode 100644 index 00000000..8e003936 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/TestItemPotion2.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/WBP_DragWidget.uasset b/Content/UI/WBP/HUD/Inventory/WBP_DragWidget.uasset new file mode 100644 index 00000000..43d712d3 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/WBP_DragWidget.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/WBP_InventoryWidget.uasset b/Content/UI/WBP/HUD/Inventory/WBP_InventoryWidget.uasset new file mode 100644 index 00000000..825adbc7 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/WBP_InventoryWidget.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/WBP_Slot.uasset b/Content/UI/WBP/HUD/Inventory/WBP_Slot.uasset new file mode 100644 index 00000000..73941ea4 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/WBP_Slot.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/WBP_ToolTip.uasset b/Content/UI/WBP/HUD/Inventory/WBP_ToolTip.uasset new file mode 100644 index 00000000..3c31387b Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/WBP_ToolTip.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/woodcover.uasset b/Content/UI/WBP/HUD/Inventory/woodcover.uasset new file mode 100644 index 00000000..3cef4933 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/woodcover.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/woodcover1.uasset b/Content/UI/WBP/HUD/Inventory/woodcover1.uasset new file mode 100644 index 00000000..18e58972 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/woodcover1.uasset differ diff --git a/Content/UI/WBP/HUD/Inventory/woodcover2.uasset b/Content/UI/WBP/HUD/Inventory/woodcover2.uasset new file mode 100644 index 00000000..412d32f4 Binary files /dev/null and b/Content/UI/WBP/HUD/Inventory/woodcover2.uasset differ diff --git a/Content/UI/WBP/HUD/Skill/WBP_SkillCool.uasset b/Content/UI/WBP/HUD/Skill/WBP_SkillCool.uasset new file mode 100644 index 00000000..be3c0969 Binary files /dev/null and b/Content/UI/WBP/HUD/Skill/WBP_SkillCool.uasset differ diff --git a/Content/UI/WBP/HUD/Skill/WBP_SkillSlot.uasset b/Content/UI/WBP/HUD/Skill/WBP_SkillSlot.uasset new file mode 100644 index 00000000..cccf132f Binary files /dev/null and b/Content/UI/WBP/HUD/Skill/WBP_SkillSlot.uasset differ diff --git a/Content/UI/WBP/HUD/Skill/WBP_SkillWidget.uasset b/Content/UI/WBP/HUD/Skill/WBP_SkillWidget.uasset new file mode 100644 index 00000000..2cbceaa7 Binary files /dev/null and b/Content/UI/WBP/HUD/Skill/WBP_SkillWidget.uasset differ diff --git a/Content/UI/WBP/HUD/Stat/WBP_StatWidget.uasset b/Content/UI/WBP/HUD/Stat/WBP_StatWidget.uasset new file mode 100644 index 00000000..60d697d1 Binary files /dev/null and b/Content/UI/WBP/HUD/Stat/WBP_StatWidget.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_InventorySlotForStore.uasset b/Content/UI/WBP/HUD/Store/WBP_InventorySlotForStore.uasset new file mode 100644 index 00000000..285f82bf Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_InventorySlotForStore.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot.uasset b/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot.uasset new file mode 100644 index 00000000..36f66863 Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot1.uasset b/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot1.uasset new file mode 100644 index 00000000..b581e7a7 Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_MerchandiseSlot1.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_SalesSlot.uasset b/Content/UI/WBP/HUD/Store/WBP_SalesSlot.uasset new file mode 100644 index 00000000..5c8b4cd1 Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_SalesSlot.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_StorePopup.uasset b/Content/UI/WBP/HUD/Store/WBP_StorePopup.uasset new file mode 100644 index 00000000..6dd4d1fe Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_StorePopup.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_StorePopup_Sell.uasset b/Content/UI/WBP/HUD/Store/WBP_StorePopup_Sell.uasset new file mode 100644 index 00000000..2c89dabb Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_StorePopup_Sell.uasset differ diff --git a/Content/UI/WBP/HUD/Store/WBP_StoreWidget.uasset b/Content/UI/WBP/HUD/Store/WBP_StoreWidget.uasset new file mode 100644 index 00000000..81b85b8a Binary files /dev/null and b/Content/UI/WBP/HUD/Store/WBP_StoreWidget.uasset differ diff --git a/Content/UI/WBP/HUD/WBP_HUD.uasset b/Content/UI/WBP/HUD/WBP_HUD.uasset new file mode 100644 index 00000000..a1a44f3e Binary files /dev/null and b/Content/UI/WBP/HUD/WBP_HUD.uasset differ diff --git a/Content/UI/WBP/HUD/WBP_UIQuick.uasset b/Content/UI/WBP/HUD/WBP_UIQuick.uasset new file mode 100644 index 00000000..d4bbc7c4 Binary files /dev/null and b/Content/UI/WBP/HUD/WBP_UIQuick.uasset differ diff --git a/Content/UI/WBP/World/Bar/WBP_EnemyHPBar.uasset b/Content/UI/WBP/World/Bar/WBP_EnemyHPBar.uasset new file mode 100644 index 00000000..17ee448d Binary files /dev/null and b/Content/UI/WBP/World/Bar/WBP_EnemyHPBar.uasset differ diff --git a/Content/UI/WBP/World/WBP_Damage.uasset b/Content/UI/WBP/World/WBP_Damage.uasset new file mode 100644 index 00000000..c19dd42e Binary files /dev/null and b/Content/UI/WBP/World/WBP_Damage.uasset differ diff --git a/Content/UI/WBP/World/WBP_DialogueInteraction.uasset b/Content/UI/WBP/World/WBP_DialogueInteraction.uasset new file mode 100644 index 00000000..91306e45 Binary files /dev/null and b/Content/UI/WBP/World/WBP_DialogueInteraction.uasset differ diff --git a/Content/UI/WBP/World/WBP_ItemNameBar.uasset b/Content/UI/WBP/World/WBP_ItemNameBar.uasset new file mode 100644 index 00000000..c9fb4795 Binary files /dev/null and b/Content/UI/WBP/World/WBP_ItemNameBar.uasset differ diff --git a/Data/Curve_SampleEnemy_Table.csv b/Data/Curve_SampleEnemy_Table.csv new file mode 100644 index 00000000..7595b52e --- /dev/null +++ b/Data/Curve_SampleEnemy_Table.csv @@ -0,0 +1,30 @@ +---,1,30,60 +Experience,0,0,0 +MaxExperience,0,0,0 +DropExperience,0,0,0 +MaxDropExperience,0,0,0 +Health,0,0,0 +MaxHealth,0,0,0 +HealthRegeneration,0,0,0 +Stamina,0,0,0 +MaxStamina,0,0,0 +StaminaRegeneration,0,0,0 +Mana,0,0,0 +MaxMana,0,0,0 +ManaRegeneration,0,0,0 +Focus,0,0,0 +MaxFocus,0,0,0 +FocusRegeneration,0,0,0 +AttackDamage,0,0,0 +AbilityPower,0,0,0 +Armor,0,0,0 +Resistance,0,0,0 +MaxResistance,0,0,0 +AttackSpeed,0,0,0 +MaxAttackSpeed,0,0,0 +SkillCooldown,0,0,0 +MaxSkillCooldown,0,0,0 +CriticalChance,0,0,0 +CriticalDamage,0,0,0 +MovementSpeed,0,0,0 +MaxMovementSpeed,0,0,0 diff --git a/Data/Curve_SampleSkill_Table.csv b/Data/Curve_SampleSkill_Table.csv index bc549e10..0b72b42a 100644 --- a/Data/Curve_SampleSkill_Table.csv +++ b/Data/Curve_SampleSkill_Table.csv @@ -1,41 +1,39 @@ ---,1,2,3,4,5,6,7,8,9 CostStamina,0,0,0,0,0,0,0,0,0 -MaxCostStamina,0,0,0,0,0,0,0,0,0 CostMana,0,0,0,0,0,0,0,0,0 -MaxCostMana,0,0,0,0,0,0,0,0,0 CostFocus,0,0,0,0,0,0,0,0,0 -MaxCostFocus,0,0,0,0,0,0,0,0,0 BaseDamage,0,0,0,0,0,0,0,0,0 -MaxBaseDamage,0,0,0,0,0,0,0,0,0 Healing,0,0,0,0,0,0,0,0,0 -MaxHealing,0,0,0,0,0,0,0,0,0 LifeSteal,0,0,0,0,0,0,0,0,0 -MaxLifeSteal,0,0,0,0,0,0,0,0,0 AttackDamageScaling,0,0,0,0,0,0,0,0,0 -MaxAttackDamageScaling,0,0,0,0,0,0,0,0,0 AbilityPowerScaling,0,0,0,0,0,0,0,0,0 -MaxAbilityPowerScaling,0,0,0,0,0,0,0,0,0 SkillRadius,0,0,0,0,0,0,0,0,0 -MaxSkillRadius,0,0,0,0,0,0,0,0,0 SkillRange,0,0,0,0,0,0,0,0,0 -MaxSkillRange,0,0,0,0,0,0,0,0,0 +SkillAttackLocationOffset,0,0,0,0,0,0,0,0,0 Cooldown,0,0,0,0,0,0,0,0,0 MaxCooldown,0,0,0,0,0,0,0,0,0 SkillAttackRate,0,0,0,0,0,0,0,0,0 MaxSkillAttackRate,0,0,0,0,0,0,0,0,0 CastTime,0,0,0,0,0,0,0,0,0 -MaxCastTime,0,0,0,0,0,0,0,0,0 PreDelay,0,0,0,0,0,0,0,0,0 -MaxPreDelay,0,0,0,0,0,0,0,0,0 PostDelay,0,0,0,0,0,0,0,0,0 -MaxPostDelay,0,0,0,0,0,0,0,0,0 EffectDuration,0,0,0,0,0,0,0,0,0 -MaxEffectDuration,0,0,0,0,0,0,0,0,0 StatusEffectChance,0,0,0,0,0,0,0,0,0 -MaxStatusEffectChance,0,0,0,0,0,0,0,0,0 StatusEffectDuration,0,0,0,0,0,0,0,0,0 -MaxStatusEffectDuration,0,0,0,0,0,0,0,0,0 +StatusBaseDamage,0,0,0,0,0,0,0,0,0 +StatusEffectADScaling,0,0,0,0,0,0,0,0,0 +StatusEffectAPScaling,0,0,0,0,0,0,0,0,0 +StatusEffectMaxStack,0,0,0,0,0,0,0,0,0 +MaxStatusEffectMaxStack,0,0,0,0,0,0,0,0,0 +StatusEffectPeriod,0,0,0,0,0,0,0,0,0 +StatusEffectSlowPercent,0,0,0,0,0,0,0,0,0 ProjectileSpeed,0,0,0,0,0,0,0,0,0 -MaxProjectileSpeed,0,0,0,0,0,0,0,0,0 ProjectileCount,0,0,0,0,0,0,0,0,0 MaxProjectileCount,0,0,0,0,0,0,0,0,0 +ProjectileLifeSpan,0,0,0,0,0,0,0,0,0 +ProjectilePierceCount,0,0,0,0,0,0,0,0,0 +MaxProjectilePierceCount,0,0,0,0,0,0,0,0,0 +ExplosionRadius,0,0,0,0,0,0,0,0,0 +ExplosionBaseDamage,0,0,0,0,0,0,0,0,0 +ExplosionADScaling,0,0,0,0,0,0,0,0,0 +ExplosionAPScaling,0,0,0,0,0,0,0,0,0 diff --git a/ProjectMJ.png b/ProjectMJ.png new file mode 100644 index 00000000..7b7afe9f Binary files /dev/null and b/ProjectMJ.png differ diff --git a/ProjectMJ.uproject b/ProjectMJ.uproject index 63721b0f..91889ae4 100644 --- a/ProjectMJ.uproject +++ b/ProjectMJ.uproject @@ -14,7 +14,8 @@ "GameplayAbilities", "UMG", "CoreUObject", - "AIModule" + "AIModule", + "NiagaraAnimNotifies" ] } ], @@ -29,6 +30,13 @@ { "Name": "GameplayAbilities", "Enabled": true + }, + { + "Name": "MotionWarping", + "Enabled": true } + ], + "TargetPlatforms": [ + "Windows" ] } \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.cpp new file mode 100644 index 00000000..a63512f9 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.cpp @@ -0,0 +1,92 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_AIActionAppearAbility.h" + +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "Character/MJCharacterBase.h" +#include "GameFramework/CharacterMovementComponent.h" + +UMJGA_AIActionAppearAbility::UMJGA_AIActionAppearAbility() +{ +} + +void UMJGA_AIActionAppearAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (AppearanceActionAnimMontage == nullptr) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_None); + + // IsInactivated 태그 제거 - 활성화 + const FGameplayTag InActivatedTag = FGameplayTag::RequestGameplayTag(TEXT("Character.State.IsInactivated")); + AMJCharacter->ASC->RemoveLooseGameplayTag(InActivatedTag); + + UAbilityTask_PlayMontageAndWait* PlayDeathMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAppear"), AppearanceActionAnimMontage, 1.0f); + + PlayDeathMontage->OnCompleted.AddDynamic(this, &UMJGA_AIActionAppearAbility::OnCompleteCallback); + PlayDeathMontage->OnInterrupted.AddDynamic(this, &UMJGA_AIActionAppearAbility::OnInterruptedCallback); + PlayDeathMontage->OnCancelled.AddDynamic(this, &UMJGA_AIActionAppearAbility::OnInterruptedCallback); + PlayDeathMontage->OnBlendOut.AddDynamic(this, &UMJGA_AIActionAppearAbility::OnBlendOutCallback); + + PlayDeathMontage->ReadyForActivation(); +} + +void UMJGA_AIActionAppearAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_AIActionAppearAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } + AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_Walking); +} + +void UMJGA_AIActionAppearAbility::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = false; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); + +} + +void UMJGA_AIActionAppearAbility::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); + +} + +void UMJGA_AIActionAppearAbility::OnBlendOutCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.h new file mode 100644 index 00000000..7a80eaa1 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionAppearAbility.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h" +#include "MJGA_AIActionAppearAbility.generated.h" + +/** + * Class Description: 적 등장 + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_AIActionAppearAbility : public UMJGA_ActionAppearanceAbility +{ + GENERATED_BODY() + +public: + UMJGA_AIActionAppearAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + + UFUNCTION() + void OnBlendOutCallback(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.cpp new file mode 100644 index 00000000..0fb49d3c --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.cpp @@ -0,0 +1,124 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_AIActionChargeAbility.h" + +#include "MotionWarpingComponent.h" +#include "AIController.h" +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "Abilities/Tasks/AbilityTask_WaitDelay.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJAbilityContextComponent.h" +#include "Kismet/KismetMathLibrary.h" +#include "MJ/AI/MJMonsterAIControllerBase.h" + +UMJGA_AIActionChargeAbility::UMJGA_AIActionChargeAbility() +{ + ChargeTime = 0.3f; +} + +void UMJGA_AIActionChargeAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + AMJCharacterBase* SourceCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!SourceCharacter) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SourceCharacter")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + AMJMonsterAIControllerBase* AIController = Cast(SourceCharacter->GetController()); + if (!AIController) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist AIController")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + UBlackboardComponent* BlackboardComponent = AIController->GetBlackboardComponent(); + if (!BlackboardComponent) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist BlackboardComponent")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + AActor* TargetActor = Cast(BlackboardComponent->GetValueAsObject("Target")); + if (!TargetActor) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist TargetActor")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + FVector AimLocation = TargetActor->GetActorLocation(); + + UMJAbilityContextComponent* ContextComponent = SourceCharacter->FindComponentByClass(); + if (ContextComponent) + { + ContextComponent->LastTargetedMouseLocation = AimLocation; + } + + FRotator TargetRotation = UKismetMathLibrary::FindLookAtRotation(SourceCharacter->GetActorLocation(), AimLocation); + TargetRotation.Pitch = 0; + TargetRotation.Roll = 0; + + UMotionWarpingComponent* MotionWarpingComponent = SourceCharacter->FindComponentByClass(); + if (MotionWarpingComponent) + { + FMotionWarpingTarget Target = {}; + Target.Location = TargetActor->GetActorLocation(); + Target.Rotation = TargetRotation; + Target.Name = FName("T"); + + MotionWarpingComponent->AddOrUpdateWarpTarget(Target); + } + + UAbilityTask_PlayMontageAndWait* ChargeTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("Charging"), SkillChargeAnimMontage, 1.0f); + ChargeTask->ReadyForActivation(); + + UAbilityTask_WaitDelay* DelayTask = UAbilityTask_WaitDelay::WaitDelay(this, ChargeTime); + DelayTask->OnFinish.AddDynamic(this, &UMJGA_AIActionChargeAbility::OnChargeDelayFinished); + DelayTask->ReadyForActivation(); +} + +void UMJGA_AIActionChargeAbility::OnChargeDelayFinished() +{ + PlayReleaseMontage(); +} + +void UMJGA_AIActionChargeAbility::OnReleaseMontageComplete() +{ + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + +} + +void UMJGA_AIActionChargeAbility::PlayReleaseMontage() +{ + if (CurrentActorInfo->AnimInstance.IsValid() && SkillChargeAnimMontage) + { + CurrentActorInfo->AnimInstance->Montage_Stop(0.1f, SkillChargeAnimMontage); + } + + if (SkillActionAnimMontage) + { + UAbilityTask_PlayMontageAndWait* ReleaseTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("ActionChargeAttack"), SkillActionAnimMontage, 1.0f); + + ReleaseTask->OnCompleted.AddDynamic(this, &UMJGA_AIActionChargeAbility::OnReleaseMontageComplete); + ReleaseTask->OnInterrupted.AddDynamic(this, &UMJGA_AIActionChargeAbility::OnReleaseMontageComplete); + ReleaseTask->OnCancelled.AddDynamic(this, &UMJGA_AIActionChargeAbility::OnReleaseMontageComplete); + + ReleaseTask->ReadyForActivation(); + } + else + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + } + +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.h new file mode 100644 index 00000000..fa23c1c4 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionChargeAbility.h @@ -0,0 +1,41 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ChargeSkill.h" +#include "MJGA_AIActionChargeAbility.generated.h" + +/** + * Class Description: AI 전용 Charge 스킬 + * Author: 신동민 + * Created Date: 2025.08.09 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_AIActionChargeAbility : public UMJGA_ChargeSkill +{ + GENERATED_BODY() + +public: + UMJGA_AIActionChargeAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + +protected: + + UFUNCTION() + void OnChargeDelayFinished(); + + UFUNCTION() + void OnReleaseMontageComplete(); + + void PlayReleaseMontage(); + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ChargeTime; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.cpp new file mode 100644 index 00000000..e909651f --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.cpp @@ -0,0 +1,91 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_AIActionDeathAbility.h" + +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "Character/MJCharacterBase.h" +#include "MJ/Character/MJMonsterCharacter.h" + +UMJGA_AIActionDeathAbility::UMJGA_AIActionDeathAbility() +{ +} + +void UMJGA_AIActionDeathAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (DeathActionAnimMontage == nullptr) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacter->SetActorEnableCollision(false); + + UAbilityTask_PlayMontageAndWait* PlayDeathMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayDeath"), DeathActionAnimMontage, 1.0f); + + PlayDeathMontage->OnCompleted.AddDynamic(this, &UMJGA_AIActionDeathAbility::OnCompleteCallback); + PlayDeathMontage->OnCancelled.AddDynamic(this, &UMJGA_AIActionDeathAbility::OnInterruptedCallback); + PlayDeathMontage->OnBlendOut.AddDynamic(this, &UMJGA_AIActionDeathAbility::OnBlendOutCallback); + + PlayDeathMontage->ReadyForActivation(); +} + +void UMJGA_AIActionDeathAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_AIActionDeathAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } + + AMJMonsterCharacter* Enemy = Cast(AMJCharacter); + if (Enemy) + { + // Minjin: 경험치 전달, 아이템 드랍 + Enemy->GiveDeathRewardTo(); + } + + // Minjin: Super에서 Destroy + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); +} + +void UMJGA_AIActionDeathAbility::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_AIActionDeathAbility::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_AIActionDeathAbility::OnBlendOutCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.h new file mode 100644 index 00000000..aeb91f78 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionDeathAbility.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ActionDeathAbility.h" +#include "MJGA_AIActionDeathAbility.generated.h" + +/** + * Class Description: Enemy Death Ability. 몽타주 재생 후 캐릭터에서 진행하던 GiveDeathRewardTo() 진행, Destroy + * Author: Kim Minjin + * Created Date: 2025.08.09. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_AIActionDeathAbility : public UMJGA_ActionDeathAbility +{ + GENERATED_BODY() + +public: + UMJGA_AIActionDeathAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + + UFUNCTION() + void OnBlendOutCallback(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.cpp new file mode 100644 index 00000000..e51164e9 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.cpp @@ -0,0 +1,130 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Abilities/MJGA_AIActionInstantAbility.h" + +#include "MotionWarpingComponent.h" +#include "Character/MJCharacterBase.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "Character/Component/MJAbilityContextComponent.h" +#include "Kismet/KismetMathLibrary.h" +#include "MJ/AI/MJMonsterAIControllerBase.h" +#include "MJ/Character/MJMonsterCharacter.h" + +UMJGA_AIActionInstantAbility::UMJGA_AIActionInstantAbility() +{ + +} + +void UMJGA_AIActionInstantAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (!SkillActionAnimMontage) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + // AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_None); + + FVector CharacterLocation = AMJCharacter->GetActorLocation(); + + AMJMonsterCharacter* EnemyCharacter = Cast(AMJCharacter); + if (!EnemyCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJMonsterAIControllerBase* AIController = Cast(EnemyCharacter->GetController()); + if (!AIController) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AActor* TargetActor = Cast(AIController->GetBlackboardComponent()->GetValueAsObject("Target")); + if (!TargetActor) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + FVector AimLocation = TargetActor->GetActorLocation(); + UMJAbilityContextComponent* ContextComponent = AMJCharacter->FindComponentByClass(); + if (ContextComponent) + { + ContextComponent->LastTargetedMouseLocation = AimLocation; + } + + FRotator TargetRotation = UKismetMathLibrary::FindLookAtRotation(CharacterLocation, TargetActor->GetActorLocation()); + TargetRotation.Pitch = 0; + TargetRotation.Roll = 0; + + // Minjin: 현재 Turn 오류 때문에 안 씀 + UMotionWarpingComponent* MotionWarpingComponent = AMJCharacter->FindComponentByClass(); + if (MotionWarpingComponent) + { + FMotionWarpingTarget Target = {}; + Target.Location = TargetActor->GetActorLocation(); + Target.Rotation = TargetRotation; + Target.Name = FName("T"); + + MotionWarpingComponent->AddOrUpdateWarpTarget(Target); + } + + UAbilityTask_PlayMontageAndWait* PlayAttackMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAttack"), SkillActionAnimMontage, 1.0f); + + PlayAttackMontage->OnCompleted.AddDynamic(this, &UMJGA_AIActionInstantAbility::OnCompleteCallback); + PlayAttackMontage->OnInterrupted.AddDynamic(this, &UMJGA_AIActionInstantAbility::OnInterruptedCallback); + PlayAttackMontage->OnCancelled.AddDynamic(this, &UMJGA_AIActionInstantAbility::OnInterruptedCallback); + + PlayAttackMontage->ReadyForActivation(); +} + +void UMJGA_AIActionInstantAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); + +} + +void UMJGA_AIActionInstantAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } + AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_Walking); +} + +void UMJGA_AIActionInstantAbility::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = false; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_AIActionInstantAbility::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.h similarity index 83% rename from Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.h rename to Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.h index fe06f4da..8604f57a 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_AIActionInstantAbility.h @@ -4,22 +4,22 @@ #include "CoreMinimal.h" #include "AbilitySystem/Abilities/MJGA_InstantSkill.h" -#include "MJGA_BasicMeleeAttack.generated.h" +#include "MJGA_AIActionInstantAbility.generated.h" /** - * Class Description: 기본 근접 공격 + * Class Description: AI의 스킬 AM을 재생하는 어빌리티 * Author: 신동민 * Created Date: 2025_06_24 * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ UCLASS() -class PROJECTMJ_API UMJGA_BasicMeleeAttack : public UMJGA_InstantSkill +class PROJECTMJ_API UMJGA_AIActionInstantAbility : public UMJGA_InstantSkill { GENERATED_BODY() public: - UMJGA_BasicMeleeAttack(); + UMJGA_AIActionInstantAbility(); virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; @@ -35,4 +35,5 @@ class PROJECTMJ_API UMJGA_BasicMeleeAttack : public UMJGA_InstantSkill //bool bReplicatedEndAbility = true; //bool bWasCancelled = false; + }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.cpp new file mode 100644 index 00000000..4be44c07 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.cpp @@ -0,0 +1,32 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h" + +#include "ProjectMJ.h" + +UMJGA_ActionAppearanceAbility::UMJGA_ActionAppearanceAbility() +{ +} + +void UMJGA_ActionAppearanceAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) + { + MJ_LOG(LogMJ, Warning, TEXT("CommitAbility is false")); + constexpr bool bReplicateEndAbility = true; + constexpr bool bWasCancelled = true; + EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + } +} + +void UMJGA_ActionAppearanceAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); +} \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h new file mode 100644 index 00000000..dc63eac5 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h @@ -0,0 +1,32 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "MJGA_ActionAppearanceAbility.generated.h" + +/** + * Class Description: 등장 기본. -플레이어와 적 구분. + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_ActionAppearanceAbility : public UMJGA_GameplayAbility +{ + GENERATED_BODY() + +public: + UMJGA_ActionAppearanceAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TObjectPtr AppearanceActionAnimMontage; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.cpp similarity index 58% rename from Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.cpp rename to Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.cpp index fdd99b34..8364dece 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_BasicMeleeAttack.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.cpp @@ -1,24 +1,33 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// ThenOneDayStudio -#include "AbilitySystem/Abilities/MJGA_BasicMeleeAttack.h" +#include "AbilitySystem/Abilities/MJGA_ActionDamageAbility.h" +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" #include "Character/MJCharacterBase.h" #include "GameFramework/CharacterMovementComponent.h" -#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" -UMJGA_BasicMeleeAttack::UMJGA_BasicMeleeAttack() +UMJGA_ActionDamageAbility::UMJGA_ActionDamageAbility() { - // } -void UMJGA_BasicMeleeAttack::ActivateAbility(const FGameplayAbilitySpecHandle Handle, +void UMJGA_ActionDamageAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) { Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) + { + MJ_LOG(LogMJ, Warning, TEXT("CommitAbility is false")); + constexpr bool bReplicateEndAbility = true; + constexpr bool bWasCancelled = true; + EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + } + ///// - if (!SkillActionAnimMontage) + if (HurtActionAnimMontage == nullptr) { EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; @@ -30,45 +39,49 @@ void UMJGA_BasicMeleeAttack::ActivateAbility(const FGameplayAbilitySpecHandle Ha EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; } + AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_None); - UAbilityTask_PlayMontageAndWait* PlayAttackMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAttack"), SkillActionAnimMontage, 1.0f); + + UAbilityTask_PlayMontageAndWait* PlayHurtMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayHurt"), HurtActionAnimMontage, 1.0f); - PlayAttackMontage->OnCompleted.AddDynamic(this, &UMJGA_BasicMeleeAttack::OnCompleteCallback); - PlayAttackMontage->OnInterrupted.AddDynamic(this, &UMJGA_BasicMeleeAttack::OnInterruptedCallback); - PlayAttackMontage->OnCancelled.AddDynamic(this, &UMJGA_BasicMeleeAttack::OnInterruptedCallback); + PlayHurtMontage->OnCompleted.AddDynamic(this, &UMJGA_ActionDamageAbility::OnCompleteCallback); + PlayHurtMontage->OnInterrupted.AddDynamic(this, &UMJGA_ActionDamageAbility::OnInterruptedCallback); + PlayHurtMontage->OnCancelled.AddDynamic(this, &UMJGA_ActionDamageAbility::OnInterruptedCallback); - PlayAttackMontage->ReadyForActivation(); + PlayHurtMontage->ReadyForActivation(); } -void UMJGA_BasicMeleeAttack::CancelAbility(const FGameplayAbilitySpecHandle Handle, +void UMJGA_ActionDamageAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) { Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); - } -void UMJGA_BasicMeleeAttack::EndAbility(const FGameplayAbilitySpecHandle Handle, +void UMJGA_ActionDamageAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) { Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); if (!AMJCharacter) { - EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + return; } + + // Minjin: Nav_Walking 이란 것이 있음 AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_Walking); } -void UMJGA_BasicMeleeAttack::OnCompleteCallback() +void UMJGA_ActionDamageAbility::OnCompleteCallback() { bool bReplicatedEndAbility = true; bool bWasCancelled = false; EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); } -void UMJGA_BasicMeleeAttack::OnInterruptedCallback() +void UMJGA_ActionDamageAbility::OnInterruptedCallback() { bool bReplicatedEndAbility = true; bool bWasCancelled = true; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.h new file mode 100644 index 00000000..119c4f78 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDamageAbility.h @@ -0,0 +1,38 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "MJGA_ActionDamageAbility.generated.h" + +/** + * Class Description: 데미지를 입었을 때. 몽타주 재생동안 IsHurt 태그 부여 + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_ActionDamageAbility : public UMJGA_GameplayAbility +{ + GENERATED_BODY() + +public: + UMJGA_ActionDamageAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TObjectPtr HurtActionAnimMontage; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.cpp new file mode 100644 index 00000000..bd43111d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.cpp @@ -0,0 +1,42 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_ActionDeathAbility.h" + +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "Character/MJCharacterBase.h" + +UMJGA_ActionDeathAbility::UMJGA_ActionDeathAbility() +{ +} + +void UMJGA_ActionDeathAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) + { + MJ_LOG(LogMJ, Warning, TEXT("CommitAbility is false")); + constexpr bool bReplicateEndAbility = true; + constexpr bool bWasCancelled = true; + EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + } +} + +void UMJGA_ActionDeathAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } + + AMJCharacter->Destroy(); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.h new file mode 100644 index 00000000..4959677d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ActionDeathAbility.h @@ -0,0 +1,31 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "MJGA_ActionDeathAbility.generated.h" + +/** + * Class Description: Death Ability + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_ActionDeathAbility : public UMJGA_GameplayAbility +{ + GENERATED_BODY() + +public: + UMJGA_ActionDeathAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TObjectPtr DeathActionAnimMontage; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.cpp index 78317d76..3ed9f798 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.cpp @@ -3,6 +3,195 @@ #include "AbilitySystem/Abilities/MJGA_ChargeSkill.h" +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + UMJGA_ChargeSkill::UMJGA_ChargeSkill() { + InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; + +} + +bool UMJGA_ChargeSkill::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, + const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const +{ + if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags)) + { + return false; + } + + return true; +} + +void UMJGA_ChargeSkill::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);\ + + if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) + { + MJ_LOG(LogMJ, Warning, TEXT("CommitAbility is false")); + constexpr bool bReplicateEndAbility = true; + constexpr bool bWasCancelled = true; + EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + } +} + +void UMJGA_ChargeSkill::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, + const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); } + +void UMJGA_ChargeSkill::InputReleased(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) +{ + Super::InputReleased(Handle, ActorInfo, ActivationInfo); +} + +bool UMJGA_ChargeSkill::CalculateFinalCosts(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, FSkillCost& OutCost) const +{ + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return false; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SkillAttributeSet")); + return false; + } + + OutCost.StaminaCost = SkillAttributeSet->GetCostStamina(); + OutCost.ManaCost = SkillAttributeSet->GetCostMana(); + + // TODO: Focus는 현재 Focus 량을 다 소비하는 메커니즘이라 다르게 해야함 - 동민 - + OutCost.FocusCost = SkillAttributeSet->GetCostFocus(); + + return true; +} + +bool UMJGA_ChargeSkill::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, + FGameplayTagContainer* OptionalRelevantTags) const +{ + FSkillCost FinalCost; + if (!CalculateFinalCosts(Handle, ActorInfo, FinalCost)) + { + MJ_LOG(LogMJ, Warning, TEXT("CalculateFinalCosts is false")); + return false; + } + + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return false; + } + + const UMJCharacterAttributeSet* CharacterAttributeSet = ASC->GetSet(); + if (!CharacterAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CharacterAttributeSet")); + return false; + } + + if (CharacterAttributeSet->GetStamina() < FinalCost.StaminaCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Stamina")); + return false; + } + + if (CharacterAttributeSet->GetMana() < FinalCost.ManaCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Mana")); + return false; + } + + if (CharacterAttributeSet->GetFocus() < FinalCost.FocusCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Focus")); + return false; + } + + return true; +} + +void UMJGA_ChargeSkill::ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, + const FGameplayAbilityActivationInfo ActivationInfo) const +{ + MJ_LOG(LogMJ, Warning, TEXT("ApplyCost")); + + if (!CostGameplayEffectClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CostGameplayEffect")); + return; + } + + FSkillCost FinalCost; + if (!CalculateFinalCosts(Handle, ActorInfo, FinalCost)) + { + MJ_LOG(LogMJ, Warning, TEXT("CalculateFinalCosts is false")); + return; + } + + FGameplayEffectSpecHandle CostSpecHandle = MakeOutgoingGameplayEffectSpec(CostGameplayEffectClass, 1.0f); + if (!CostSpecHandle.Data) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CostSpecHandle.Data")); + return; + } + + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostStamina")), FinalCost.StaminaCost * -1.f); + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostMana")), FinalCost.ManaCost * -1.f); + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostFocus")), FinalCost.FocusCost * -1.f); + + ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, CostSpecHandle); +} + +void UMJGA_ChargeSkill::ApplyCooldown(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const +{ + if (!CooldownGameplayEffectClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CooldownGameplayEffectClass")); + return; + } + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SkillAttributeSet")); + return; + } + + const UMJCharacterAttributeSet* CharacterAttributeSet = ASC->GetSet(); + + float BaseCooldown = SkillAttributeSet->GetCooldown(); + float CharacterSkillCooldown = CharacterAttributeSet->GetSkillCooldown() * 0.01f; + float FinalCooldown = BaseCooldown * (100.f / (100.f + CharacterSkillCooldown)); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(CooldownGameplayEffectClass, 1.f, ASC->MakeEffectContext()); + if (SpecHandle.IsValid()) + { + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.Cooldown")), FinalCooldown); + + ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data); + } + return; +} + diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.h index 52317144..83725c93 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_ChargeSkill.h @@ -7,13 +7,15 @@ #include "MJGA_ChargeSkill.generated.h" /** - * Class Description: ÷̾ ų Instant, Charging, Passive - * Charge Ŭ ߵϴ ų - * Author: ŵ - * Created Date: 2025_06_24 - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) + * Class Description: 플레이어의 스킬을 Instant, Charging, Passive로 구분 + * Charge는 우클릭을 길게 눌렀다 땠 을때 발동하는 스킬 + * Author: 신동민 + * Created Date: 2025.06.24 + * Description of Change: 차징 애니메이션 추가 + * Modified By: 신동민 + * Modified Date: 2025.08.06 */ + UCLASS() class PROJECTMJ_API UMJGA_ChargeSkill : public UMJGA_GameplayAbility { @@ -22,4 +24,25 @@ class PROJECTMJ_API UMJGA_ChargeSkill : public UMJGA_GameplayAbility public: UMJGA_ChargeSkill(); + virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr, FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + + virtual void InputReleased(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) override; + +protected: + + virtual bool CalculateFinalCosts(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FSkillCost& OutCost) const; + virtual bool CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; + virtual void ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override; + + virtual void ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override; + +protected: + UPROPERTY(EditAnywhere, Category="Animation") + TObjectPtr SkillChargeAnimMontage; + + UPROPERTY(EditAnywhere, Category = "Animation") + TObjectPtr SkillActionAnimMontage; + }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.cpp new file mode 100644 index 00000000..ead8032d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.cpp @@ -0,0 +1,65 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_DrawMarker.h" + +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "Character/Component/MJAbilityContextComponent.h" + +UMJGA_DrawMarker::UMJGA_DrawMarker() +{ + InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; +} + +void UMJGA_DrawMarker::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + MJ_LOG(LogMJ, Warning, TEXT("AAAAAAA")); + + AActor* Character = GetAvatarActorFromActorInfo(); + if (!Character) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist Character")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + UWorld* World = Character->GetWorld(); + if (!World) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist World")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + if (!NiagaraSystem) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist NiagaraSystem")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + FVector TargetWorldLocation = FVector::ZeroVector; + FVector GroundNormal = FVector::UpVector; + + UMJAbilityContextComponent* ContextComponent = Character->FindComponentByClass(); + if (ContextComponent) + { + TargetWorldLocation = ContextComponent->LastTargetedMouseLocation; + } + + const FVector SpawnLocation = TargetWorldLocation + GroundNormal * ZOffset; + const FRotator SpawnRotation = FRotationMatrix::MakeFromZ(GroundNormal).Rotator(); + + UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(World, NiagaraSystem, SpawnLocation, SpawnRotation); + if (NiagaraComponent) + { + NiagaraComponent->SetAutoDestroy(true); + } + + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.h new file mode 100644 index 00000000..c27f0b8d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_DrawMarker.h @@ -0,0 +1,38 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "MJGA_DrawMarker.generated.h" + +/** + * Class Description: 플레이어의 Charge 스킬 + * Author: 신동민 + * Created Date: 2025.08.06 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UNiagaraSystem; + +UCLASS() +class PROJECTMJ_API UMJGA_DrawMarker : public UMJGA_GameplayAbility +{ + GENERATED_BODY() + +public: + UMJGA_DrawMarker(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + +protected: + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + TObjectPtr NiagaraSystem; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + float ZOffset = 1.5f; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.cpp index 562a710c..b9df35f6 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.cpp @@ -7,6 +7,10 @@ #include "AbilitySystem/MJAbilitySystemComponent.h" #include "MJGA_GameplayAbility.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + void UMJGA_GameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) { Super::OnGiveAbility(ActorInfo, Spec); @@ -19,6 +23,14 @@ void UMJGA_GameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* Actor } } +void UMJGA_GameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + +} + void UMJGA_GameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) { Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.h index 6756ba4b..3c683606 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_GameplayAbility.h @@ -15,12 +15,21 @@ enum class EMJAbilityActivationPolicy :uint8 OnTriggered, OnGiven }; + +struct FSkillCost +{ + float StaminaCost = 0.0f; + float ManaCost = 0.0f; + float FocusCost = 0.0f; +}; + /** * Class Description: * Author: Lee JuHyeon * Created Date: 2025_06_11 - * Last Modified By: Add CombatComponent Data - * Last Modified Date: 2025_06_18 + * Description of Change: 자원 소모 쿨타임 감소 추가 + * Modified By: 신동민 + * Modified Date: 2025.07.23 */ UCLASS() class PROJECTMJ_API UMJGA_GameplayAbility : public UGameplayAbility @@ -29,7 +38,8 @@ class PROJECTMJ_API UMJGA_GameplayAbility : public UGameplayAbility protected: virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; - virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)override; + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; UPROPERTY(EditDefaultsOnly, Category = "Ability") EMJAbilityActivationPolicy AbilityActivationPolicy = EMJAbilityActivationPolicy::OnTriggered; @@ -39,4 +49,6 @@ class PROJECTMJ_API UMJGA_GameplayAbility : public UGameplayAbility UFUNCTION(BlueprintPure, Category = "Player|Combat") UMJAbilitySystemComponent* GetAbilitySysteamComponent() const; + + }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.cpp index 50c49bf5..d0ad35a5 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.cpp @@ -3,7 +3,192 @@ #include "AbilitySystem/Abilities/MJGA_InstantSkill.h" +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + UMJGA_InstantSkill::UMJGA_InstantSkill() { InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; } + +bool UMJGA_InstantSkill::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, + const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const +{ + if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags)) + { + return false; + } + + return true; +} + +void UMJGA_InstantSkill::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + // UGameplayAbility::ActivateAbility의 주석에 있음 - 동민 - + if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) + { + MJ_LOG(LogMJ, Warning, TEXT("CommitAbility is false")); + constexpr bool bReplicateEndAbility = true; + constexpr bool bWasCancelled = true; + EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + } +} + +void UMJGA_InstantSkill::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, + const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + +} + +bool UMJGA_InstantSkill::CalculateFinalCosts(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, FSkillCost& OutCost) const +{ + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return false; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SkillAttributeSet")); + return false; + } + + OutCost.StaminaCost = SkillAttributeSet->GetCostStamina(); + OutCost.ManaCost = SkillAttributeSet->GetCostMana(); + + // TODO: Focus는 현재 Focus 량을 다 소비하는 메커니즘이라 다르게 해야함 - 동민 - + OutCost.FocusCost = SkillAttributeSet->GetCostFocus(); + + return true; +} + +bool UMJGA_InstantSkill::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, + FGameplayTagContainer* OptionalRelevantTags) const +{ + // 원본 무시 + // 우린 비용이 미리 정해지지 않은 방식을 사용하기 때문에 - 동민 - + + FSkillCost FinalCost; + if (!CalculateFinalCosts(Handle, ActorInfo, FinalCost)) + { + MJ_LOG(LogMJ, Warning, TEXT("CalculateFinalCosts is false")); + return false; + } + + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return false; + } + + const UMJCharacterAttributeSet* CharacterAttributeSet = ASC->GetSet(); + if (!CharacterAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CharacterAttributeSet")); + return false; + } + + if (CharacterAttributeSet->GetStamina() < FinalCost.StaminaCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Stamina")); + return false; + } + + if (CharacterAttributeSet->GetMana() < FinalCost.ManaCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Mana")); + return false; + } + + if (CharacterAttributeSet->GetFocus() < FinalCost.FocusCost) + { + MJ_LOG(LogMJ, Warning, TEXT("Need Focus")); + return false; + } + + return true; +} + + +void UMJGA_InstantSkill::ApplyCost(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const +{ + MJ_LOG(LogMJ, Warning, TEXT("ApplyCost")); + + if (!CostGameplayEffectClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CostGameplayEffect")); + return; + } + + FSkillCost FinalCost; + if (!CalculateFinalCosts(Handle, ActorInfo, FinalCost)) + { + MJ_LOG(LogMJ, Warning, TEXT("CalculateFinalCosts is false")); + return; + } + + FGameplayEffectSpecHandle CostSpecHandle = MakeOutgoingGameplayEffectSpec(CostGameplayEffectClass, 1.0f); + if (!CostSpecHandle.Data) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CostSpecHandle.Data")); + return; + } + + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostStamina")), FinalCost.StaminaCost * -1.f); + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostMana")), FinalCost.ManaCost * -1.f); + CostSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostFocus")), FinalCost.FocusCost * -1.f); + + ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, CostSpecHandle); +} + +void UMJGA_InstantSkill::ApplyCooldown(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const +{ + if (!CooldownGameplayEffectClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CooldownGameplayEffectClass")); + return; + } + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + return; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SkillAttributeSet")); + return; + } + + const UMJCharacterAttributeSet* CharacterAttributeSet = ASC->GetSet(); + + float BaseCooldown = SkillAttributeSet->GetCooldown(); + float CharacterSkillCooldown = CharacterAttributeSet->GetSkillCooldown() * 0.01f; + float FinalCooldown = BaseCooldown * (100.f / (100.f + CharacterSkillCooldown)); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(CooldownGameplayEffectClass, 1.f, ASC->MakeEffectContext()); + if (SpecHandle.IsValid()) + { + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.Cooldown")), FinalCooldown); + + ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data); + } + return; +} \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.h index 1c85d3eb..f016d140 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_InstantSkill.h @@ -7,9 +7,9 @@ #include "MJGA_InstantSkill.generated.h" /** - * Class Description: ÷̾ ų Instant, Charging, Passive - * Instant Ŭ £ Īϴ ų + ⺻ - * Author: ŵ + * Class Description: 플레이어의 스킬을 Instant, Charging, Passive로 구분 + * Instant는 우클릭 딸깍에 매칭하는 스킬 + 기본 공격 + * Author: 신동민 * Created Date: 2025_06_24 * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) @@ -26,7 +26,20 @@ class PROJECTMJ_API UMJGA_InstantSkill : public UMJGA_GameplayAbility public: UMJGA_InstantSkill(); + virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr, FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + + virtual bool CalculateFinalCosts(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FSkillCost& OutCost) const; + virtual bool CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; + virtual void ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override; + + // CalculateFinalCooldown 도 만들어서 적용하면 어떨까? + virtual void ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const override; protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") TObjectPtr SkillActionAnimMontage; + }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.cpp index 48fa7083..7eaa6bf3 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.cpp @@ -29,43 +29,87 @@ void UMJGA_MeleeAttackHitCheck::ActivateAbility(const FGameplayAbilitySpecHandle void UMJGA_MeleeAttackHitCheck::OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle) { - if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetDataHandle, 0)) + TArray HitActors = UAbilitySystemBlueprintLibrary::GetActorsFromTargetData(TargetDataHandle,0); + + if (HitActors.Num() > 0) { - FHitResult HitResult = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(TargetDataHandle, 0); - // 이거 가까운거 하나만 검사하는건데 나중에 수정할 필요 있어보임 + if (!DamageGameplayEffectClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist DamageGameplayEffectClass")); - // UAbilitySystemComponent* SourceASC = GetAbilitySystemComponentFromActorInfo_Checked(); - // UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(HitResult.GetActor()); - // null check 해야함 - // GameplayCue 에 사용함 + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false); + return; + } - // const UMJCharacterAttributeSet* SourceCharacterAttributeSet = SourceASC->GetSet(); - // const UMJCharacterSkillAttributeSet* SourceCharacterSkillAttributeSet = SourceASC->GetSet(); - // null check 해야함 - // GE 없이 테스트 할 때 사용해 볼 것 + UAbilitySystemComponent* SourceASC = GetAbilitySystemComponentFromActorInfo_Checked(); + if (!SourceASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SourceASC")); - FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(AttackDamageEffect); - if (EffectSpecHandle.IsValid()) + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false); + return; + } + + const UMJCharacterSkillAttributeSet* SourceCharacterSkillAttributeSet = SourceASC->GetSet(); + + if (!SourceCharacterSkillAttributeSet) { - ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, EffectSpecHandle, TargetDataHandle); + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SourceCharacterSkillAttributeSet")); - // TODO : GameplayCue + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false); + return; } - } - else if (UAbilitySystemBlueprintLibrary::TargetDataHasActor(TargetDataHandle, 0)) - { - // Gameplay Cue 에 사용 - // UAbilitySystemComponent* SourceASC = GetAbilitySystemComponentFromActorInfo_Checked(); - FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(AttackDamageEffect); + if (DamageGameplayEffectClass) + { + FGameplayEffectSpecHandle DamageEffectSpecHandle = MakeOutgoingGameplayEffectSpec(DamageGameplayEffectClass); + if (DamageEffectSpecHandle.IsValid()) + { + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.BaseDamage")), SourceCharacterSkillAttributeSet->GetBaseDamage()); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AttackDamageScaling")), SourceCharacterSkillAttributeSet->GetAttackDamageScaling()); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AbilityPowerScaling")), SourceCharacterSkillAttributeSet->GetAbilityPowerScaling()); + + ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, DamageEffectSpecHandle, TargetDataHandle); + + for (AActor* HitActor : HitActors) + { + UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(HitActor); + if (TargetASC) + { + FGameplayCueParameters CueParams; + CueParams.EffectContext = UAbilitySystemBlueprintLibrary::GetEffectContext(DamageEffectSpecHandle); + TargetASC->ExecuteGameplayCue(GameplayCueTag, CueParams); + } + } + } + } - if (EffectSpecHandle.IsValid()) + if (StatusGameplayEffectClass) { - ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, EffectSpecHandle, TargetDataHandle); + FGameplayEffectSpecHandle StatusEffectSpecHandle = MakeOutgoingGameplayEffectSpec(StatusGameplayEffectClass); + if (StatusEffectSpecHandle.IsValid()) + { + float Chance = SourceCharacterSkillAttributeSet->GetStatusEffectChance(); + + if (FMath::FRandRange(0.0f, 100.0f) <= Chance) + { + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectDuration")), SourceCharacterSkillAttributeSet->GetStatusEffectDuration()); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusBaseDamage")), SourceCharacterSkillAttributeSet->GetStatusBaseDamage()); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectADScaling")), SourceCharacterSkillAttributeSet->GetStatusEffectADScaling()); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectAPScaling")), SourceCharacterSkillAttributeSet->GetStatusEffectAPScaling()); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectMaxStack")), SourceCharacterSkillAttributeSet->GetStatusEffectMaxStack()); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectPeriod")), SourceCharacterSkillAttributeSet->GetStatusEffectPeriod()); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectSlowPercent")), SourceCharacterSkillAttributeSet->GetStatusEffectSlowPercent()); - // TODO : GameplayCue + ApplyGameplayEffectSpecToTarget(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, StatusEffectSpecHandle, TargetDataHandle); + } + } } } + bool bReplicatedEndAbility = true; bool bWasCancelled = false; EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.h index 87f770e1..45135ab7 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_MeleeAttackHitCheck.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "GameplayTagContainer.h" #include "MJGA_MeleeAttackHitCheck.generated.h" /** @@ -33,18 +34,15 @@ class PROJECTMJ_API UMJGA_MeleeAttackHitCheck : public UMJGA_GameplayAbility UFUNCTION() void OnTraceResultCallback(const FGameplayAbilityTargetDataHandle& TargetDataHandle); - UPROPERTY(EditAnywhere, Category = "GAS") - TSubclassOf AttackDamageEffect; - - // 사용할 어빌리티들 적용을 해야하나 - // 버프/디버프 등 + UPROPERTY(EditDefaultsOnly, Category = "GAS|Effects") + TSubclassOf DamageGameplayEffectClass; - // 데이터로 따로 관리할 가능성이 높음 - // 그래서 추후 사용할지도 - // 명시 안해주면 1.f긴 함 - // float CurrentLevel; + UPROPERTY(EditDefaultsOnly, Category = "GAS|Effects") + TSubclassOf StatusGameplayEffectClass; UPROPERTY(EditAnywhere, Category = "GAS") TSubclassOf TargetActorClass; + UPROPERTY(EditAnywhere, Category = "GameplayCue" , meta = (Categories = "GameplayCue")) + FGameplayTag GameplayCueTag; }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.cpp index a907ea29..81915642 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.cpp +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.cpp @@ -3,6 +3,75 @@ #include "AbilitySystem/Abilities/MJGA_PassiveSkill.h" +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" + UMJGA_PassiveSkill::UMJGA_PassiveSkill() { + InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; } + +void UMJGA_PassiveSkill::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); +} + +void UMJGA_PassiveSkill::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) +{ + Super::OnGiveAbility(ActorInfo, Spec); + + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + for (TSubclassOf EffectClass : PassiveEffects) + { + MJ_LOG(LogMJ, Error, TEXT("AAAAA")); + + if (EffectClass) + { + FGameplayEffectContextHandle EffectContext = ASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(EffectClass, 1.0f, EffectContext); + if (SpecHandle.IsValid()) + { + FActiveGameplayEffectHandle ActiveGameplayEffectHandle = ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get()); + if (ActiveGameplayEffectHandle.WasSuccessfullyApplied()) + { + AppliedEffects.Add(ActiveGameplayEffectHandle); + } + } + } + } + +} + +void UMJGA_PassiveSkill::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) +{ + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + for (FActiveGameplayEffectHandle ActiveHandle : AppliedEffects) + { + if (ActiveHandle.IsValid()) + { + ASC->RemoveActiveGameplayEffect(ActiveHandle); + } + } + + AppliedEffects.Empty(); + + Super::OnRemoveAbility(ActorInfo, Spec); + +} + diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.h index 07e388fc..8519edcc 100644 --- a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.h +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PassiveSkill.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #pragma once @@ -7,9 +7,9 @@ #include "MJGA_PassiveSkill.generated.h" /** - * Class Description: ÷̾ ų Instant, Charging, Passive - * Passive нú - * Author: ŵ + * Class Description: 플레이어의 스킬을 Instant, Charging, Passive로 구분 + * Passive는 항상 영향을 주는 버프 또는 디버프 같은 스킬, 기본 지속 효과 라고도 함 + * Author: 신동민 * Created Date: 2025_06_24 * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) @@ -22,6 +22,16 @@ class PROJECTMJ_API UMJGA_PassiveSkill : public UMJGA_GameplayAbility public: UMJGA_PassiveSkill(); + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + + virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; + virtual void OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; + protected: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Passive|Effects") + TArray> PassiveEffects; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Passive|Effects") + TArray AppliedEffects; }; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.cpp new file mode 100644 index 00000000..acfeb651 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.cpp @@ -0,0 +1,83 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.h" + +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "Character/MJCharacterBase.h" +#include "GameFramework/CharacterMovementComponent.h" + +UMJGA_PlayerActionAppearAbility::UMJGA_PlayerActionAppearAbility() +{ +} + +void UMJGA_PlayerActionAppearAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (AppearanceActionAnimMontage == nullptr) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + UAbilityTask_PlayMontageAndWait* PlayDeathMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAttack"), AppearanceActionAnimMontage, 1.0f); + + PlayDeathMontage->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionAppearAbility::OnCompleteCallback); + PlayDeathMontage->OnInterrupted.AddDynamic(this, &UMJGA_PlayerActionAppearAbility::OnInterruptedCallback); + PlayDeathMontage->OnCancelled.AddDynamic(this, &UMJGA_PlayerActionAppearAbility::OnInterruptedCallback); + PlayDeathMontage->OnBlendOut.AddDynamic(this, &UMJGA_PlayerActionAppearAbility::OnBlendOutCallback); + + PlayDeathMontage->ReadyForActivation(); +} + +void UMJGA_PlayerActionAppearAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_PlayerActionAppearAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } + AMJCharacter->GetCharacterMovement()->SetMovementMode(MOVE_Walking); +} + +void UMJGA_PlayerActionAppearAbility::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = false; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionAppearAbility::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionAppearAbility::OnBlendOutCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.h new file mode 100644 index 00000000..dbae432e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionAppearAbility.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ActionAppearanceAbility.h" +#include "MJGA_PlayerActionAppearAbility.generated.h" + +/** + * Class Description: 플레이어 등장 + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_PlayerActionAppearAbility : public UMJGA_ActionAppearanceAbility +{ + GENERATED_BODY() + +public: + UMJGA_PlayerActionAppearAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + + UFUNCTION() + void OnBlendOutCallback(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.cpp new file mode 100644 index 00000000..80c86001 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.cpp @@ -0,0 +1,150 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.h" + +#include "MJGA_PlayerActionInstantSkill.h" +#include "MotionWarpingComponent.h" +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJAbilityContextComponent.h" +#include "Kismet/KismetMathLibrary.h" + +UMJGA_PlayerActionChargeSkill::UMJGA_PlayerActionChargeSkill() +{ + +} + +void UMJGA_PlayerActionChargeSkill::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ASC")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + ASC->AddLooseGameplayTag(FGameplayTag::RequestGameplayTag("Character.State.IsCharging")); + + UMJAT_GetMousePosition* GetMousePositionTask = UMJAT_GetMousePosition::CreateTask(this); + GetMousePositionTask->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionChargeSkill::OnMousePositionReady); + GetMousePositionTask->OnFailed.AddDynamic(this, &UMJGA_PlayerActionChargeSkill::OnMousePositionFailed); + + GetMousePositionTask->ReadyForActivation(); +} + +void UMJGA_PlayerActionChargeSkill::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_PlayerActionChargeSkill::InputReleased(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) +{ + Super::InputReleased(Handle, ActorInfo, ActivationInfo); + + MJ_LOG(LogMJ, Warning, TEXT("BB")); + + PlayReleaseMontage(); +} + +void UMJGA_PlayerActionChargeSkill::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + if (UMJAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent)) + { + ASC->RemoveLooseGameplayTag(FGameplayTag::RequestGameplayTag("Character.State.IsCharging")); + } + + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionChargeSkill::OnMousePositionReady(const FVector& MouseLocation) +{ + if (!SkillChargeAnimMontage) + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(CurrentActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + return; + } + + UMJAbilityContextComponent* MJAbilityContextComponent = AMJCharacter->FindComponentByClass(); + if (MJAbilityContextComponent) + { + MJAbilityContextComponent->LastTargetedMouseLocation = MouseLocation; + } + + FVector CharacterLocation = AMJCharacter->GetActorLocation(); + FRotator TargetRotation = UKismetMathLibrary::FindLookAtRotation(CharacterLocation, MouseLocation); + TargetRotation.Pitch = 0; + TargetRotation.Roll = 0; + + UMotionWarpingComponent* MotionWarpingComponent = AMJCharacter->FindComponentByClass(); + if (MotionWarpingComponent) + { + FMotionWarpingTarget Target = {}; + Target.Location = MouseLocation; + Target.Rotation = TargetRotation; + Target.Name = FName("Target"); + + MotionWarpingComponent->AddOrUpdateWarpTarget(Target); + } + + + UAbilityTask_PlayMontageAndWait* PlayChargeMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayCharge"), SkillChargeAnimMontage, 1.0f); + + PlayChargeMontage->ReadyForActivation(); +} + +void UMJGA_PlayerActionChargeSkill::OnMousePositionFailed(const FVector& IgnoreLocation) +{ + OnMousePositionReady(GetAvatarActorFromActorInfo()->GetActorForwardVector() * 100.f + GetAvatarActorFromActorInfo()->GetActorLocation()); +} + +void UMJGA_PlayerActionChargeSkill::OnReleaseMontageComplete() +{ + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false); +} + +void UMJGA_PlayerActionChargeSkill::PlayReleaseMontage() +{ + MJ_LOG(LogMJ, Warning, TEXT("AA")); + + if (CurrentActorInfo->AnimInstance.IsValid()) + { + CurrentActorInfo->AnimInstance->Montage_Stop(0.1f, SkillChargeAnimMontage); + } + + // Release몽타주 재생 + if (SkillActionAnimMontage) + { + UAbilityTask_PlayMontageAndWait* ReleaseMontageTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAction"), SkillActionAnimMontage); + + ReleaseMontageTask->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionChargeSkill::OnReleaseMontageComplete); + ReleaseMontageTask->OnInterrupted.AddDynamic(this, &UMJGA_PlayerActionChargeSkill::OnReleaseMontageComplete); + ReleaseMontageTask->OnCancelled.AddDynamic(this, &UMJGA_PlayerActionChargeSkill::OnReleaseMontageComplete); + + ReleaseMontageTask->ReadyForActivation(); + } + else + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.h new file mode 100644 index 00000000..c060e6bd --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionChargeSkill.h @@ -0,0 +1,40 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ChargeSkill.h" +#include "MJGA_PlayerActionChargeSkill.generated.h" + +/** + * Class Description: 플레이어의 Charge 스킬 + * Author: 신동민 + * Created Date: 2025.08.06 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_PlayerActionChargeSkill : public UMJGA_ChargeSkill +{ + GENERATED_BODY() +public: + UMJGA_PlayerActionChargeSkill(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void InputReleased(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + UFUNCTION() + void OnMousePositionReady(const FVector& MouseLocation); + + UFUNCTION() + void OnMousePositionFailed(const FVector& IgnoreLocation); + + UFUNCTION() + void OnReleaseMontageComplete(); + + void PlayReleaseMontage(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.cpp new file mode 100644 index 00000000..b8990dbe --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.cpp @@ -0,0 +1,78 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.h" + +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "Character/MJCharacterBase.h" + +UMJGA_PlayerActionDeathAbility::UMJGA_PlayerActionDeathAbility() +{ +} + +void UMJGA_PlayerActionDeathAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + if (DeathActionAnimMontage == nullptr) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, false); + return; + } + + UAbilityTask_PlayMontageAndWait* PlayDeathMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayDeath"), DeathActionAnimMontage, 1.0f); + + PlayDeathMontage->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionDeathAbility::OnCompleteCallback); + PlayDeathMontage->OnCancelled.AddDynamic(this, &UMJGA_PlayerActionDeathAbility::OnInterruptedCallback); + PlayDeathMontage->OnBlendOut.AddDynamic(this, &UMJGA_PlayerActionDeathAbility::OnBlendOutCallback); + + PlayDeathMontage->ReadyForActivation(); +} + +void UMJGA_PlayerActionDeathAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_PlayerActionDeathAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + // Minjin: Super 호출 전 필요한 거 작업 + + + // Minjin: Super에서 Destroy하는데 바꿔도 됨 + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionDeathAbility::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionDeathAbility::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionDeathAbility::OnBlendOutCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.h new file mode 100644 index 00000000..49a11bb4 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionDeathAbility.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_ActionDeathAbility.h" +#include "MJGA_PlayerActionDeathAbility.generated.h" + +/** + * Class Description: Player Death Ability. + * Author: Kim Minjin + * Created Date: 2025.08.09. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_PlayerActionDeathAbility : public UMJGA_ActionDeathAbility +{ + GENERATED_BODY() + +public: + UMJGA_PlayerActionDeathAbility(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + + UFUNCTION() + void OnBlendOutCallback(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.cpp new file mode 100644 index 00000000..520e3877 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.cpp @@ -0,0 +1,118 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.h" + +#include "MJGA_AIActionInstantAbility.h" +#include "MotionWarpingComponent.h" +#include "ProjectMJ.h" +#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h" +#include "AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJAbilityContextComponent.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "Kismet/KismetMathLibrary.h" + +UMJGA_PlayerActionInstantSkill::UMJGA_PlayerActionInstantSkill() +{ + +} + +void UMJGA_PlayerActionInstantSkill::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + + UMJAT_GetMousePosition* GetMousePositionTask = UMJAT_GetMousePosition::CreateTask(this); + GetMousePositionTask->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionInstantSkill::OnMousePositionReady); + GetMousePositionTask->OnFailed.AddDynamic(this, &UMJGA_PlayerActionInstantSkill::OnMousePositionFailed); + + GetMousePositionTask->ReadyForActivation(); +} + +void UMJGA_PlayerActionInstantSkill::CancelAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateCancelAbility) +{ + Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility); +} + +void UMJGA_PlayerActionInstantSkill::EndAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + AMJCharacterBase* AMJCharacter = Cast(ActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + return; + } +} + +void UMJGA_PlayerActionInstantSkill::OnMousePositionReady(const FVector& MouseLocation) +{ + if (!SkillActionAnimMontage) + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + return; + } + + AMJCharacterBase* AMJCharacter = Cast(CurrentActorInfo->AvatarActor.Get()); + if (!AMJCharacter) + { + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true); + return; + } + + UMJAbilityContextComponent* MJAbilityContextComponent = AMJCharacter->FindComponentByClass(); + if (MJAbilityContextComponent) + { + MJAbilityContextComponent->LastTargetedMouseLocation = MouseLocation; + } + + FVector CharacterLocation = AMJCharacter->GetActorLocation(); + FRotator TargetRotation = UKismetMathLibrary::FindLookAtRotation(CharacterLocation, MouseLocation); + TargetRotation.Pitch = 0; + TargetRotation.Roll = 0; + + UMotionWarpingComponent* MotionWarpingComponent = AMJCharacter->FindComponentByClass(); + if (MotionWarpingComponent) + { + FMotionWarpingTarget Target = {}; + Target.Location = MouseLocation; + Target.Rotation = TargetRotation; + Target.Name = FName("Target"); + + MotionWarpingComponent->AddOrUpdateWarpTarget(Target); + } + + UAbilityTask_PlayMontageAndWait* PlayAttackMontage = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAttack"), SkillActionAnimMontage, 1.0f); + + PlayAttackMontage->OnCompleted.AddDynamic(this, &UMJGA_PlayerActionInstantSkill::OnCompleteCallback); + PlayAttackMontage->OnInterrupted.AddDynamic(this, &UMJGA_PlayerActionInstantSkill::OnInterruptedCallback); + PlayAttackMontage->OnCancelled.AddDynamic(this, &UMJGA_PlayerActionInstantSkill::OnInterruptedCallback); + + PlayAttackMontage->ReadyForActivation(); +} + +void UMJGA_PlayerActionInstantSkill::OnMousePositionFailed(const FVector& IgnoreLocation) +{ + // 마우스가 없으면 앞을 향한 동작인데. AI까지 적용시키기엔 문제가 많음 + OnMousePositionReady(GetAvatarActorFromActorInfo()->GetActorForwardVector() * 100.f + GetAvatarActorFromActorInfo()->GetActorLocation()); +} + +void UMJGA_PlayerActionInstantSkill::OnCompleteCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = false; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} + +void UMJGA_PlayerActionInstantSkill::OnInterruptedCallback() +{ + bool bReplicatedEndAbility = true; + bool bWasCancelled = true; + EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.h new file mode 100644 index 00000000..5e0e7f98 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_PlayerActionInstantSkill.h @@ -0,0 +1,42 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_InstantSkill.h" +#include "MJGA_PlayerActionInstantSkill.generated.h" + +/** + * Class Description: AM 을 실행시키는 어빌리티. 플레이어는 마우스 방향을 바라보고 스킬을 사용한다 + * Author: 신동민 + * Created Date: 2025.07.18 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGA_PlayerActionInstantSkill : public UMJGA_InstantSkill +{ + GENERATED_BODY() + +public: + UMJGA_PlayerActionInstantSkill(); + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; + +protected: + UFUNCTION() + void OnMousePositionReady(const FVector& MouseLocation); + + UFUNCTION() + void OnMousePositionFailed(const FVector& IgnoreLocation); + + UFUNCTION() + void OnCompleteCallback(); + + UFUNCTION() + void OnInterruptedCallback(); + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.cpp b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.cpp new file mode 100644 index 00000000..66e93874 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.cpp @@ -0,0 +1,142 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Abilities/MJGA_SpawnProjectile.h" + +#include "MJGA_InstantSkill.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJAbilityContextComponent.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "Kismet/GameplayStatics.h" + +UMJGA_SpawnProjectile::UMJGA_SpawnProjectile() +{ + +} + +void UMJGA_SpawnProjectile::ActivateAbility(const FGameplayAbilitySpecHandle Handle, + const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, + const FGameplayEventData* TriggerEventData) +{ + + + Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); + AMJCharacterBase* OwnerCharacter = Cast(GetAvatarActorFromActorInfo()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerCharacter")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + UAbilitySystemComponent* SourceASC = GetAbilitySystemComponentFromActorInfo(); + if (!SourceASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SourceASC")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = SourceASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist SkillAttributeSet")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + const FGameplayAbilitySpec* CurrentAbilitySpec = GetCurrentAbilitySpec(); + if (!CurrentAbilitySpec) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist CurrentAbilitySpec")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + if (!ProjectileClass) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ProjectileClass")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + if (!MovementBehavior) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist MovementBehavior")); + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + UMJProjectileMovementBehaviorBase* MovementStrategy = MovementBehavior->GetDefaultObject (); + if (!MovementStrategy) + { + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); + return; + } + + FVector MouseLocation = FVector::ZeroVector; + if (UMJAbilityContextComponent* ContextComponent = OwnerCharacter->FindComponentByClass()) + { + MouseLocation = ContextComponent->LastTargetedMouseLocation; + ContextComponent->LastTargetedMouseLocation = FVector::ZeroVector; + } + + if (MouseLocation == FVector::ZeroVector) + { + MouseLocation = OwnerCharacter->GetActorLocation() + OwnerCharacter->GetActorForwardVector() * 1000.f; + } + + const FTransform SpawnTransform = MovementStrategy->CalculateSpawnTransform(this, MouseLocation, SpawnSocketName); + + AMJProjectileBase* Projectile = GetWorld()->SpawnActorDeferred(ProjectileClass, SpawnTransform, GetOwningActorFromActorInfo(), OwnerCharacter, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); + + if (Projectile) + { + FMJSkillProjectileParams ProjectileParams; + ProjectileParams.TargetLocation = MouseLocation; + + ProjectileParams.SourceASC = SourceASC; + + ProjectileParams.DamageGameplayEffectClass = DamageGameplayEffectClass; + ProjectileParams.StatusGameplayEffectClass = StatusGameplayEffectClass; + ProjectileParams.ExplosionDamageGameplayEffectClass = ExplosionDamageGameplayEffectClass; + + ProjectileParams.BaseDamage = SkillAttributeSet->GetBaseDamage(); + ProjectileParams.AttackDamageScaling = SkillAttributeSet->GetAttackDamageScaling(); + ProjectileParams.AbilityPowerScaling = SkillAttributeSet->GetAbilityPowerScaling(); + ProjectileParams.LifeSteal = SkillAttributeSet->GetLifeSteal(); + ProjectileParams.SkillRadius = SkillAttributeSet->GetSkillRadius(); + ProjectileParams.SkillRange = SkillAttributeSet->GetSkillRange(); + + ProjectileParams.StatusEffectChance = SkillAttributeSet->GetStatusEffectChance(); + ProjectileParams.StatusEffectDuration = SkillAttributeSet->GetStatusEffectDuration(); + ProjectileParams.StatusEffectBaseDamage = SkillAttributeSet->GetStatusBaseDamage(); + ProjectileParams.StatusEffectADScaling = SkillAttributeSet->GetStatusEffectADScaling(); + ProjectileParams.StatusEffectAPScaling = SkillAttributeSet->GetStatusEffectAPScaling(); + ProjectileParams.StatusEffectMaxStack = SkillAttributeSet->GetStatusEffectMaxStack(); + ProjectileParams.StatusEffectPeriod = SkillAttributeSet->GetStatusEffectPeriod(); + ProjectileParams.StatusEffectSlowPercent = SkillAttributeSet->GetStatusEffectSlowPercent(); + + ProjectileParams.ExplosionRadius = SkillAttributeSet->GetExplosionRadius(); + ProjectileParams.ExplosionBaseDamage = SkillAttributeSet->GetExplosionBaseDamage(); + ProjectileParams.ExplosionADScaling = SkillAttributeSet->GetExplosionADScaling(); + ProjectileParams.ExplosionAPScaling = SkillAttributeSet->GetExplosionAPScaling(); + + ProjectileParams.ProjectileSpeed = SkillAttributeSet->GetProjectileSpeed(); + ProjectileParams.ProjectileCount = SkillAttributeSet->GetProjectileCount(); + ProjectileParams.LifeSpan = SkillAttributeSet->GetProjectileLifeSpan(); + + ProjectileParams.PierceCount = SkillAttributeSet->GetProjectilePierceCount(); + + Projectile->InitProjectile(ProjectileParams, MovementBehavior, ReactionBehaviors); + + UGameplayStatics::FinishSpawningActor(Projectile, SpawnTransform); + + } + + EndAbility(Handle, ActorInfo, ActivationInfo, true, true); +} diff --git a/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.h b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.h new file mode 100644 index 00000000..6aa434c4 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Abilities/MJGA_SpawnProjectile.h @@ -0,0 +1,57 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" +#include "AbilitySystem/Struct/MJSkillProjectileParams.h" +#include "MJGA_SpawnProjectile.generated.h" + +/** + * Class Description: Spawn Projectile Ability + * Author: 신동민 + * Created Date: 2025.07.03 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UMJProjectileMovementBehaviorBase; +class UMJProjectileReactionBehaviorBase; +class AMJProjectileBase; + +UCLASS() +class PROJECTMJ_API UMJGA_SpawnProjectile : public UMJGA_GameplayAbility +{ + GENERATED_BODY() + +public: + UMJGA_SpawnProjectile(); + +protected: + + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + +protected: + UPROPERTY(EditDefaultsOnly, Category = "GAS|Effects") + TSubclassOf DamageGameplayEffectClass; + + UPROPERTY(EditDefaultsOnly, Category = "GAS|Effects") + TSubclassOf StatusGameplayEffectClass; + + UPROPERTY(EditDefaultsOnly, Category = "GAS|Effects") + TSubclassOf ExplosionDamageGameplayEffectClass; + + UPROPERTY(EditDefaultsOnly, Category = "Projectile") + FName SpawnSocketName; + + UPROPERTY(EditDefaultsOnly, Category = "Projectile") + TSubclassOf ProjectileClass; + + UPROPERTY(EditDefaultsOnly, Category = "Behavior") + TSubclassOf MovementBehavior; + + UPROPERTY(EditDefaultsOnly, Category = "Behavior") + TArray> ReactionBehaviors; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.cpp b/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.cpp new file mode 100644 index 00000000..42dc9cad --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.cpp @@ -0,0 +1,50 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h" + +#include "Physics/MJCollision.h" + +UMJAT_GetMousePosition::UMJAT_GetMousePosition() +{ +} + +UMJAT_GetMousePosition* UMJAT_GetMousePosition::CreateTask(UGameplayAbility* OwningAbility) +{ + UMJAT_GetMousePosition* NewTask = NewAbilityTask(OwningAbility); + return NewTask; + +} + +void UMJAT_GetMousePosition::Activate() +{ + Super::Activate(); + + APlayerController* PlayerController = Ability->GetCurrentActorInfo()->PlayerController.Get(); + if (!PlayerController) + { + if (ShouldBroadcastAbilityTaskDelegates()) + { + OnFailed.Broadcast(FVector::ZeroVector); + } + EndTask(); + return; + } + FHitResult HitResult; + + // TODO: ̰ TraceChannel ϴ°ɷ  + PlayerController->GetHitResultUnderCursor(CCHANNEL_MJGROUND, false, HitResult); + + if (ShouldBroadcastAbilityTaskDelegates()) + { + if (HitResult.bBlockingHit) + { + OnCompleted.Broadcast(HitResult.Location); + } + else + { + OnFailed.Broadcast(FVector::ZeroVector); + } + } + EndTask(); +} \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h b/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h new file mode 100644 index 00000000..5a8fd33d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/AbilityTasks/MJAT_GetMousePosition.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Abilities/Tasks/AbilityTask.h" +#include "MJAT_GetMousePosition.generated.h" + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMousePositionResultDelegate, const FVector&, MouseLocation); + +/** + * Class Description: 마우스 위치를 받아오는 AbilityTask + * Author: 신동민 + * Created Date: 2025.07.18 + * Description of Change: + * Modified By: + * Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJAT_GetMousePosition : public UAbilityTask +{ + GENERATED_BODY() +public: + UMJAT_GetMousePosition(); + + UFUNCTION(BlueprintCallable, Category = "AbilityTasks", meta = (DisplayName = "GetMousePosition", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE")) + static UMJAT_GetMousePosition* CreateTask(UGameplayAbility* OwningAbility); + + virtual void Activate() override; + +public: + UPROPERTY(BlueprintAssignable) + FMousePositionResultDelegate OnCompleted; + + UPROPERTY(BlueprintAssignable) + FMousePositionResultDelegate OnFailed; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.cpp new file mode 100644 index 00000000..976706d1 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.cpp @@ -0,0 +1,134 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.h" + +#include "AbilitySystemBlueprintLibrary.h" +#include "AbilitySystemComponent.h" +#include "GameplayEffectTypes.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" +#include "Character/MJCharacterBase.h" +#include "Kismet/GameplayStatics.h" + +UMJProjectileExplodeReaction::UMJProjectileExplodeReaction() +{ +} + +void UMJProjectileExplodeReaction::InitReaction(AMJProjectileBase* InProjectile) +{ + OwnerProjectile = InProjectile; + +} +void UMJProjectileExplodeReaction::OnProjectileReact(AMJProjectileBase* InProjectile, AActor* HitActor, const FHitResult& Hit) +{ + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + Explode(); +} + +void UMJProjectileExplodeReaction::Explode() +{ + const FVector ExplosionLocation = OwnerProjectile->GetActorLocation(); + const float ExplosionRadius = OwnerProjectile->ProjectileParams.ExplosionRadius; + + // 이거 Cue 억지로 쓴 느낌 + // 그냥 Niagara System 달아줘서 재생했어도 될거 같음 + FGameplayCueParameters CueParams; + CueParams.Location = ExplosionLocation; + CueParams.RawMagnitude = ExplosionRadius; + + OwnerProjectile->ProjectileParams.SourceASC->ExecuteGameplayCue(GameplayCueTag, CueParams); + + TArray OverlappedActors; + TArray ActorsToIgnore; + + ActorsToIgnore.Add(OwnerProjectile); + ActorsToIgnore.Add(OwnerProjectile->ProjectileParams.SourceASC->GetAvatarActor()); + + + + UKismetSystemLibrary::SphereOverlapActors( + OwnerProjectile->GetWorld(), + ExplosionLocation, + ExplosionRadius, + TArray>(), + AMJCharacterBase::StaticClass(), + ActorsToIgnore, + OverlappedActors); + + /*DrawDebugSphere( + OwnerProjectile->GetWorld(), + ExplosionLocation, + ExplosionRadius, + 24, + FColor::Red, + false, + 5.0f, + 0, + 1.0f + );*/ + + MJ_LOG(LogMJ, Warning, TEXT("A")); + for (AActor* OverlappedActor : OverlappedActors) + { + // Minjin: Enemey의 경우 같은 Enemy를 공격하면 안 된다. + TSubclassOf TargetClass = GetParentNativeClass(OverlappedActor->GetClass()); + TSubclassOf OwnerClass = GetParentNativeClass(OwnerProjectile->ProjectileParams.SourceASC->GetAvatarActor()->GetClass()); + if ((TargetClass && OwnerClass)&& (TargetClass == OwnerClass)) + { + MJ_LOG(LogMJ, Warning, TEXT("The same class as Target")); + continue; + } + + UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OverlappedActor); + if (!TargetASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist TargetASC")); + continue; + } + MJ_LOG(LogMJ, Warning, TEXT("AA")); + + FGameplayEffectContextHandle EffectContext = OwnerProjectile->ProjectileParams.SourceASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + const TSubclassOf DamageEffectClass = OwnerProjectile->ProjectileParams.ExplosionDamageGameplayEffectClass; + if (DamageEffectClass) + { + FGameplayEffectSpecHandle DamageEffectSpecHandle = OwnerProjectile->ProjectileParams.SourceASC->MakeOutgoingSpec(DamageEffectClass, 1.0f, EffectContext); + if (DamageEffectSpecHandle.IsValid()) + { + // 전용 데미지 계산 만든게 아니라서 넘겨주는 태그가 같다 + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.BaseDamage")), OwnerProjectile->ProjectileParams.ExplosionBaseDamage); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AttackDamageScaling")), OwnerProjectile->ProjectileParams.ExplosionADScaling); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AbilityPowerScaling")), OwnerProjectile->ProjectileParams.ExplosionAPScaling); + + OwnerProjectile->ProjectileParams.SourceASC->ApplyGameplayEffectSpecToTarget(*DamageEffectSpecHandle.Data.Get(), TargetASC); + } + } + + const TSubclassOf StatusEffectClass = OwnerProjectile->ProjectileParams.StatusGameplayEffectClass; + if (StatusEffectClass) + { + FGameplayEffectSpecHandle StatusEffectSpecHandle = OwnerProjectile->ProjectileParams.SourceASC->MakeOutgoingSpec(StatusEffectClass, 1.0f, EffectContext); + if (StatusEffectSpecHandle.IsValid()) + { + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectDuration")), OwnerProjectile->ProjectileParams.StatusEffectDuration); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusBaseDamage")), OwnerProjectile->ProjectileParams.StatusEffectBaseDamage); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectADScaling")), OwnerProjectile->ProjectileParams.StatusEffectADScaling); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectAPScaling")), OwnerProjectile->ProjectileParams.StatusEffectAPScaling); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectMaxStack")), OwnerProjectile->ProjectileParams.StatusEffectMaxStack); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectPeriod")), OwnerProjectile->ProjectileParams.StatusEffectPeriod); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectSlowPercent")), OwnerProjectile->ProjectileParams.StatusEffectSlowPercent); + OwnerProjectile->ProjectileParams.SourceASC->ApplyGameplayEffectSpecToTarget(*StatusEffectSpecHandle.Data.Get(), TargetASC); + } + } + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.h new file mode 100644 index 00000000..cbd65114 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileExplodeReaction.h @@ -0,0 +1,38 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h" +#include "MJProjectileExplodeReaction.generated.h" + +/** + * Class Description: 투사체 폭발 + * Author: 신동민 + * Created Date: 2025.07.29 + * Description of Change: 적끼리 공격 못하게 수정 + * Modified By: 김민진 + * Modified Date: 2025.08.18. + */ + +UCLASS() +class PROJECTMJ_API UMJProjectileExplodeReaction : public UMJProjectileReactionBehaviorBase +{ + GENERATED_BODY() +public: + UMJProjectileExplodeReaction(); + + virtual void InitReaction(AMJProjectileBase* InProjectile) override; + virtual void OnProjectileReact(AMJProjectileBase* InProjectile, AActor* HitActor, const FHitResult& Hit) override; + +protected: + void Explode(); + +public: + UPROPERTY() + TObjectPtr OwnerProjectile; + + UPROPERTY(EditAnywhere, Category = "GameplayCue", meta = (Categories = "GameplayCue")) + FGameplayTag GameplayCueTag; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.cpp new file mode 100644 index 00000000..5c8aeb6a --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.cpp @@ -0,0 +1,93 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.h" + +#include "AbilitySystemBlueprintLibrary.h" +#include "AbilitySystemComponent.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" +#include "ProjectMJ.h" + +UMJProjectileImpactReaction::UMJProjectileImpactReaction() +{ +} + +void UMJProjectileImpactReaction::InitReaction(AMJProjectileBase* Projectile) +{ + OwnerProjectile = Projectile; +} + +void UMJProjectileImpactReaction::OnProjectileReact(AMJProjectileBase* Projectile, AActor* HitActor, + const FHitResult& Hit) +{ + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + if (!HitActor) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist HitActor")); + return; + } + + if (HitActor == OwnerProjectile->ProjectileParams.SourceASC->GetAvatarActor()) + { + MJ_LOG(LogMJ, Warning, TEXT("HitActor is User")); + return; + } + + // Minjin: Enemey의 경우 같은 Enemy를 공격하면 안 된다. + TSubclassOf TargetClass = GetParentNativeClass(HitActor->GetClass()); + TSubclassOf OwnerClass = GetParentNativeClass(OwnerProjectile->ProjectileParams.SourceASC->GetAvatarActor()->GetClass()); + if ((TargetClass && OwnerClass)&& (TargetClass == OwnerClass)) + { + MJ_LOG(LogMJ, Warning, TEXT("The same class as Target")); + return; + } + + UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(HitActor); + if (!TargetASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist TargetASC")); + return; + } + + FGameplayEffectContextHandle EffectContext = OwnerProjectile->ProjectileParams.SourceASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + const TSubclassOf DamageEffectClass = OwnerProjectile->ProjectileParams.DamageGameplayEffectClass; + if (DamageEffectClass) + { + FGameplayEffectSpecHandle DamageEffectSpecHandle = OwnerProjectile->ProjectileParams.SourceASC->MakeOutgoingSpec(DamageEffectClass, 1.0f, EffectContext); + if (DamageEffectSpecHandle.IsValid()) + { + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.BaseDamage")), OwnerProjectile->ProjectileParams.BaseDamage); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AttackDamageScaling")), OwnerProjectile->ProjectileParams.AttackDamageScaling); + DamageEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AbilityPowerScaling")), OwnerProjectile->ProjectileParams.AbilityPowerScaling); + + OwnerProjectile->ProjectileParams.SourceASC->ApplyGameplayEffectSpecToTarget(*DamageEffectSpecHandle.Data.Get(), TargetASC); + } + } + + const TSubclassOf StatusEffectClass = OwnerProjectile->ProjectileParams.StatusGameplayEffectClass; + if (StatusEffectClass) + { + FGameplayEffectSpecHandle StatusEffectSpecHandle = OwnerProjectile->ProjectileParams.SourceASC->MakeOutgoingSpec(StatusEffectClass, 1.0f, EffectContext); + if (StatusEffectSpecHandle.IsValid()) + { + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectDuration")), OwnerProjectile->ProjectileParams.StatusEffectDuration); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusBaseDamage")), OwnerProjectile->ProjectileParams.StatusEffectBaseDamage); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectADScaling")), OwnerProjectile->ProjectileParams.StatusEffectADScaling); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectAPScaling")), OwnerProjectile->ProjectileParams.StatusEffectAPScaling); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectMaxStack")), OwnerProjectile->ProjectileParams.StatusEffectMaxStack); + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectPeriod")), OwnerProjectile->ProjectileParams.StatusEffectPeriod); + + StatusEffectSpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectSlowPercent")), OwnerProjectile->ProjectileParams.StatusEffectSlowPercent); + OwnerProjectile->ProjectileParams.SourceASC->ApplyGameplayEffectSpecToTarget(*StatusEffectSpecHandle.Data.Get(), TargetASC); + } + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.h new file mode 100644 index 00000000..beeeaf86 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileImpactReaction.h @@ -0,0 +1,34 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h" +#include "MJProjectileImpactReaction.generated.h" + +/** + * Class Description: 기본적인 충돌 + * Author: 신동민 + * Created Date: 2025.07.31 + * Description of Change: 적끼리 공격하지 못하게 수정 + * Modified By: 김민진 + * Modified Date: 2025.08.18. + */ + + +UCLASS() +class PROJECTMJ_API UMJProjectileImpactReaction : public UMJProjectileReactionBehaviorBase +{ + GENERATED_BODY() + +public: + UMJProjectileImpactReaction(); + +public: + virtual void InitReaction(AMJProjectileBase* Projectile) override; + virtual void OnProjectileReact(AMJProjectileBase* Projectile, AActor* HitActor, const FHitResult& Hit) override; + +protected: + UPROPERTY() + TObjectPtr OwnerProjectile; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.cpp new file mode 100644 index 00000000..dac8e28c --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.cpp @@ -0,0 +1,5 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h" + diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h new file mode 100644 index 00000000..c5f60e2f --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h @@ -0,0 +1,32 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "MJProjectileMovementBehaviorBase.generated.h" + +/** + * Class Description: 투사체 이동 로직 + * Author: 신동민 + * Created Date: 2025.07.29 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UGameplayAbility; +class AMJProjectileBase; + +UCLASS(Abstract, Blueprintable) +class PROJECTMJ_API UMJProjectileMovementBehaviorBase : public UObject +{ + GENERATED_BODY() + +public: + + virtual FTransform CalculateSpawnTransform(UGameplayAbility* OwningAbility, const FVector& TargetLocation, FName SpawnSocketName) PURE_VIRTUAL(UMJProjectileMovementBehaviorBase::CalculateSpawnTransform, return FTransform::Identity;); + virtual void InitMovement(AMJProjectileBase* InProjectile) PURE_VIRTUAL(UMJProjectileMovementBehaviorBase::InitMovement, ); + virtual void Move(AMJProjectileBase* InProjectile, float DeltaSeconds) PURE_VIRTUAL(UMJProjectileMovementBehaviorBase::Move, ); + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.cpp new file mode 100644 index 00000000..43ca1944 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.cpp @@ -0,0 +1,62 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementFall.h" + +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" + +UMJProjectileMovementFall::UMJProjectileMovementFall() +{ + +} + +FTransform UMJProjectileMovementFall::CalculateSpawnTransform(UGameplayAbility* OwningAbility, + const FVector& TargetLocation, FName SpawnSocketName) +{ + FVector SpawnLocation = TargetLocation; + + SpawnLocation.Z += SpawnHeightOffset; + + const FRotator SpawnRotation = FRotator(-90.0f, 0.0f, 0.0f); + + return FTransform(SpawnRotation.Quaternion(), SpawnLocation); + +} + +void UMJProjectileMovementFall::InitMovement(AMJProjectileBase* InProjectile) +{ + OwnerProjectile = InProjectile; + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + TimeSinceSpawn = 0.0f; + + float MoveSpeed = OwnerProjectile->ProjectileParams.ProjectileSpeed; + + Velocity = OwnerProjectile->GetActorForwardVector() * MoveSpeed; +} + +void UMJProjectileMovementFall::Move(AMJProjectileBase* InProjectile, float DeltaSeconds) +{ + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + TimeSinceSpawn += DeltaSeconds; + + if (TimeSinceSpawn < DelayTime) + { + OwnerProjectile->SetActorLocation(OwnerProjectile->GetActorLocation()); + return; + } + + FVector NewLocation = OwnerProjectile->GetActorLocation() + Velocity * DeltaSeconds; + OwnerProjectile->SetActorLocation(NewLocation, true); + +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.h new file mode 100644 index 00000000..a336af90 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementFall.h @@ -0,0 +1,45 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h" +#include "MJProjectileMovementFall.generated.h" + +/** + * Class Description: 하늘에서 떨어지는 투사체 (중력 적용 X) + * Author: 신동민 + * Created Date: 2025.08.02 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJProjectileMovementFall : public UMJProjectileMovementBehaviorBase +{ + GENERATED_BODY() + +public: + UMJProjectileMovementFall(); + + virtual FTransform CalculateSpawnTransform(UGameplayAbility* OwningAbility, const FVector& TargetLocation, FName SpawnSocketName) override; + virtual void InitMovement(AMJProjectileBase* InProjectile) override; + virtual void Move(AMJProjectileBase* InProjectile, float DeltaSeconds) override; + +protected: + UPROPERTY() + TObjectPtr OwnerProjectile; + + UPROPERTY() + FVector Velocity; + + UPROPERTY() + float TimeSinceSpawn; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile") + float SpawnHeightOffset; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile") + float DelayTime; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.cpp new file mode 100644 index 00000000..b5a3594b --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.cpp @@ -0,0 +1,26 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.h" + +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" + +UMJProjectileMovementGravityFall::UMJProjectileMovementGravityFall() +{ +} + +void UMJProjectileMovementGravityFall::Move(AMJProjectileBase* InProjectile, float DeltaSeconds) +{ + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + FVector Gravity = FVector(0.0f, 0.0f, GetWorld()->GetGravityZ()); + Velocity += Gravity * DeltaSeconds; + + FVector NewLocation = OwnerProjectile->GetActorLocation() + Velocity * DeltaSeconds; + OwnerProjectile->SetActorLocation(NewLocation, true); +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.h new file mode 100644 index 00000000..5adccd78 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementGravityFall.h @@ -0,0 +1,28 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementFall.h" +#include "MJProjectileMovementGravityFall.generated.h" + +/** + * Class Description: 하늘에서 떨어지는 투사체 (중력 적용) + * Author: 신동민 + * Created Date: 2025.08.04 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJProjectileMovementGravityFall : public UMJProjectileMovementFall +{ + GENERATED_BODY() + +public: + UMJProjectileMovementGravityFall(); + + virtual void Move(AMJProjectileBase* InProjectile, float DeltaSeconds) override; +protected: + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.cpp new file mode 100644 index 00000000..f4489f4e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.cpp @@ -0,0 +1,65 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.h" + +#include "ProjectMJ.h" +#include "Abilities/GameplayAbility.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" +#include "Character/MJCharacterBase.h" + +UMJProjectileMovementLinear::UMJProjectileMovementLinear() +{ +} + +FTransform UMJProjectileMovementLinear::CalculateSpawnTransform(UGameplayAbility* OwningAbility, + const FVector& TargetLocation, FName SpawnSocketName) +{ + if (!OwningAbility) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwningAbility")); + return FTransform::Identity; + } + + AMJCharacterBase* ProjectileSpawner = Cast(OwningAbility->GetAvatarActorFromActorInfo()); + + FVector SpawnLocation = FVector::ZeroVector; + USkeletalMeshComponent* CharacterMesh = ProjectileSpawner->GetMesh(); + if (SpawnSocketName != NAME_None && CharacterMesh && CharacterMesh->DoesSocketExist(SpawnSocketName)) + { + SpawnLocation = CharacterMesh->GetSocketLocation(SpawnSocketName); + } + else + { + SpawnLocation = ProjectileSpawner->GetActorLocation() + ProjectileSpawner->GetActorForwardVector() * 100.0f; + } + const FRotator SpawnRotation = (TargetLocation - SpawnLocation).Rotation(); + + return FTransform(SpawnRotation, SpawnLocation); +} + +void UMJProjectileMovementLinear::InitMovement(AMJProjectileBase* InProjectile) +{ + OwnerProjectile = InProjectile; + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + float MoveSpeed = OwnerProjectile->ProjectileParams.ProjectileSpeed; + + Velocity = OwnerProjectile->GetActorForwardVector() * MoveSpeed; +} + +void UMJProjectileMovementLinear::Move(AMJProjectileBase* InProjectile, float DeltaSeconds) +{ + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + const float MoveSpeed = OwnerProjectile->ProjectileParams.ProjectileSpeed; + const FVector NewLocation = OwnerProjectile->GetActorLocation() + Velocity * DeltaSeconds; + OwnerProjectile->SetActorLocation(NewLocation); +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.h new file mode 100644 index 00000000..d09d7b39 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinear.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementBehaviorBase.h" +#include "MJProjectileMovementLinear.generated.h" + +/** + * Class Description: 투사체 직선 이동 + * Author: 신동민 + * Created Date: 2025.07.31 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class AMJProjectileBase; + +UCLASS() +class PROJECTMJ_API UMJProjectileMovementLinear : public UMJProjectileMovementBehaviorBase +{ + GENERATED_BODY() + +public: + UMJProjectileMovementLinear(); + +public: + virtual FTransform CalculateSpawnTransform(UGameplayAbility* OwningAbility, const FVector& TargetLocation, FName SpawnSocketName) override; + virtual void InitMovement(AMJProjectileBase* InProjectile) override; + virtual void Move(AMJProjectileBase* InProjectile, float DeltaSeconds) override; + +protected: + UPROPERTY() + TObjectPtr OwnerProjectile; + + UPROPERTY() + FVector Velocity; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.cpp new file mode 100644 index 00000000..8afcd55d --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.cpp @@ -0,0 +1,37 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.h" + +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" + +UMJProjectileMovementLinearZLock::UMJProjectileMovementLinearZLock() +{ +} + +void UMJProjectileMovementLinearZLock::InitMovement(AMJProjectileBase* InProjectile) +{ + Super::InitMovement(InProjectile); + + InitialZLocation = OwnerProjectile->GetActorLocation().Z; + +} + +void UMJProjectileMovementLinearZLock::Move(AMJProjectileBase* InProjectile, float DeltaSeconds) +{ + OwnerProjectile = InProjectile; + if (!OwnerProjectile) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerProjectile")); + return; + } + + const float MoveSpeed = OwnerProjectile->ProjectileParams.ProjectileSpeed; + + FVector NewLocation = OwnerProjectile->GetActorLocation() + Velocity * DeltaSeconds; + + NewLocation.Z = InitialZLocation; + + OwnerProjectile->SetActorLocation(NewLocation, true); +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.h new file mode 100644 index 00000000..3ade519e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileMovementLinearZLock.h @@ -0,0 +1,33 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJProjectileMovementLinear.h" +#include "MJProjectileMovementLinearZLock.generated.h" + +/** + * Class Description: Z 좌표 고정 이동 전략 + * Author: 신동민 + * Created Date: 2025.08.02 + * Description of Change: + * Modified By: + * Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJProjectileMovementLinearZLock : public UMJProjectileMovementLinear +{ + GENERATED_BODY() + +public: + UMJProjectileMovementLinearZLock(); + + virtual void InitMovement(AMJProjectileBase* InProjectile) override; + virtual void Move(AMJProjectileBase* InProjectile, float DeltaSeconds) override; + +protected: + + UPROPERTY() + float InitialZLocation; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.cpp new file mode 100644 index 00000000..b4d4c9fd --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.cpp @@ -0,0 +1,5 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h" + diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h new file mode 100644 index 00000000..48370905 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h @@ -0,0 +1,27 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "MJProjectileReactionBehaviorBase.generated.h" + +class AMJProjectileBase; +/** + * Class Description: 투사체 충돌 반응 로직 + * Author: 신동민 + * Created Date: 2025.07.29 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS(Abstract, Blueprintable) +class PROJECTMJ_API UMJProjectileReactionBehaviorBase : public UObject +{ + GENERATED_BODY() + +public: + virtual void OnProjectileReact(AMJProjectileBase* InProjectile, AActor* HitActor, const FHitResult& Hit) PURE_VIRTUAL(UMJProjectileReactionBehaviorBase::OnProjectileReact, ); + virtual void InitReaction(AMJProjectileBase* InProjectile) PURE_VIRTUAL(UMJProjectileReactionBehaviorBase::InitReaction, ); + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.cpp b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.cpp new file mode 100644 index 00000000..4a8f9846 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.cpp @@ -0,0 +1,20 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.h" + +UMJProjectileReactionPierce::UMJProjectileReactionPierce() +{ +} + +void UMJProjectileReactionPierce::InitReaction(AMJProjectileBase* InProjectile) +{ + Super::InitReaction(InProjectile); +} + +void UMJProjectileReactionPierce::OnProjectileReact(AMJProjectileBase* InProjectile, AActor* HitActor, + const FHitResult& Hit) +{ + Super::OnProjectileReact(InProjectile, HitActor, Hit); +} + diff --git a/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.h b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.h new file mode 100644 index 00000000..3f8f5bc9 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/Behavior/MJProjectileReactionPierce.h @@ -0,0 +1,35 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Actor/Behavior/MJProjectileReactionBehaviorBase.h" +#include "MJProjectileReactionPierce.generated.h" + +/** + * Class Description: 관통 여부 + * Author: 신동민 + * Created Date: 2025.08.02 + * Description of Change: + * Modified By: + * Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJProjectileReactionPierce : public UMJProjectileReactionBehaviorBase +{ + GENERATED_BODY() + +public: + UMJProjectileReactionPierce(); + + virtual void InitReaction(AMJProjectileBase* InProjectile) override; + virtual void OnProjectileReact(AMJProjectileBase* InProjectile, AActor* HitActor, const FHitResult& Hit) override; + +protected: + UPROPERTY() + TObjectPtr OwnerProjectile; + + UPROPERTY() + int32 RemainingPierceCount = 0; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.cpp b/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.cpp new file mode 100644 index 00000000..957f9820 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.cpp @@ -0,0 +1,161 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Actor/MJProjectileBase.h" + +#include "AbilitySystemBlueprintLibrary.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "Components/SphereComponent.h" +#include "Physics/MJCollision.h" +#include "GameplayTagContainer.h" +#include "NiagaraComponent.h" +#include "ProjectMJ.h" +#include "Behavior/MJProjectileReactionBehaviorBase.h" +#include "Behavior/MJProjectileMovementBehaviorBase.h" +#include "Character/MJCharacterBase.h" + +AMJProjectileBase::AMJProjectileBase() +{ + PrimaryActorTick.bCanEverTick = true; + + // Sphere Section + Sphere = CreateDefaultSubobject("Projectile"); + SetRootComponent(Sphere); + + Sphere->SetCollisionObjectType(CCHANNEL_MJPROJECTILE); + Sphere->SetCollisionProfileName(CRPOFILE_MJPROJECTILE); + + // Niagara Section + NiagaraComponent = CreateDefaultSubobject("NiagaraComponent"); + NiagaraComponent->SetupAttachment(RootComponent); +} + +void AMJProjectileBase::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (MovementBehavior) + { + MovementBehavior->Move(this, DeltaSeconds); + } +} + +void AMJProjectileBase::InitProjectile(const FMJSkillProjectileParams& InParams, + TSubclassOf InMovementBehaviorClass, + const TArray>& InReactionBehaviorClasses) +{ + ProjectileParams = InParams; + + if (InMovementBehaviorClass) + { + MovementBehavior = NewObject(this, InMovementBehaviorClass); + if (MovementBehavior) + { + MovementBehavior->InitMovement(this); + } + } + + for (const auto& ReactionBehaviorClass : InReactionBehaviorClasses) + { + if (ReactionBehaviorClass) + { + UMJProjectileReactionBehaviorBase* ReactionBehavior = NewObject(this, ReactionBehaviorClass); + if (ReactionBehavior) + { + ReactionBehavior->InitReaction(this); + ReactionBehaviors.Add(ReactionBehavior); + } + } + } + + if (ProjectileParams.SkillRadius > 0.0f) + { + Sphere->SetSphereRadius(ProjectileParams.SkillRadius); + + } + + if (NiagaraComponent) + { + const float NiagaraScale = ProjectileParams.SkillRadius / VFXRatio; + NiagaraComponent->SetFloatParameter(TEXT("Scale_All"), NiagaraScale); + } +} + +void AMJProjectileBase::BeginPlay() +{ + Super::BeginPlay(); + Sphere->OnComponentBeginOverlap.AddDynamic(this, &AMJProjectileBase::OnSphereOverlap); + Sphere->OnComponentHit.AddDynamic(this, &AMJProjectileBase::OnSphereHit); + + if (ProjectileParams.LifeSpan > 0.0f) + { + SetLifeSpan(ProjectileParams.LifeSpan); + } +} + +void AMJProjectileBase::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, + UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) +{ + AMJCharacterBase* OwnerActor = Cast(ProjectileParams.SourceASC->GetAvatarActor()); + + if (!OwnerActor) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist OwnerActor")); + return; + } + + if (OtherActor == this || (OwnerActor && OtherActor == OwnerActor)) + { + MJ_LOG(LogMJ, Warning, TEXT("Target is User")); + return; + } + + bool bIsLastPierce = false; + + if (ProjectileParams.PierceCount > 0) + { + --ProjectileParams.PierceCount; + if (ProjectileParams.PierceCount <= 0) + { + bIsLastPierce = true; + } + } + else + { + bIsLastPierce = true; + } + + for (const TObjectPtr ReactionBehavior : ReactionBehaviors) + { + if (ReactionBehavior) + { + ReactionBehavior->OnProjectileReact(this, OtherActor, SweepResult); + } + } + UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OtherActor); + if (!TargetASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist TargetASC")); + Destroy(); + return; + } + + if (bIsLastPierce) + { + Destroy(); + return; + } +} + +void AMJProjectileBase::OnSphereHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, + UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) +{ + for (const TObjectPtr ReactionBehavior : ReactionBehaviors) + { + if (ReactionBehavior) + { + ReactionBehavior->OnProjectileReact(this, OtherActor, Hit); + } + } + Destroy(); +} diff --git a/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.h b/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.h new file mode 100644 index 00000000..c2f5ccb4 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Actor/MJProjectileBase.h @@ -0,0 +1,77 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/Struct/MJSkillProjectileParams.h" +#include "GameplayTagContainer.h" +#include "GameFramework/Actor.h" +#include "MJProjectileBase.generated.h" + +/** + * Class Description: Projectile Actor + * Author: 신동민 + * Created Date: 2025.07.03 + * Last Modified By: + * Last Modified Date: + */ + +class UMJProjectileReactionBehaviorBase; +class UMJProjectileMovementBehaviorBase; +class UNiagaraComponent; +class UProjectileMovementComponent; +class USphereComponent; +class UNiagaraSystem; +class USoundBase; + +UCLASS() +class PROJECTMJ_API AMJProjectileBase : public AActor +{ + GENERATED_BODY() + +public: + AMJProjectileBase(); + + virtual void Tick(float DeltaSeconds) override; + + void InitProjectile(const FMJSkillProjectileParams& InParams, TSubclassOf InMovementBehaviorClass, const TArray>& InReactionBehaviorClasses); + + FORCEINLINE USphereComponent* GetSphere() const { return Sphere; } + +protected: + virtual void BeginPlay() override; + + UFUNCTION() + virtual void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); + + UFUNCTION() + virtual void OnSphereHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit); + +public: + UPROPERTY(BlueprintReadWrite) + FMJSkillProjectileParams ProjectileParams; + +protected: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Behavior") + TObjectPtr MovementBehavior; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Behavior") + TArray> ReactionBehaviors; + +private: + UPROPERTY(EditDefaultsOnly, Category = "Projectile") + TObjectPtr Sphere; + + UPROPERTY(EditDefaultsOnly, Category = "Projectile", meta = (Categories = "GameplayCue")) + FGameplayTag HitGameplayCueTag; + + UPROPERTY(EditDefaultsOnly, Category = "Projectile", meta = (AllowPrivateAccess = "true")) + TObjectPtr NiagaraComponent; + + UPROPERTY(EditDefaultsOnly, Category = "Projectile", meta = (AllowPrivateAccess = "true")) + float VFXRatio = 1.0f; + + UPROPERTY(EditAnywhere, Category = "SFX") + TObjectPtr HitSFX; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.cpp b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.cpp new file mode 100644 index 00000000..a7eb4e77 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.cpp @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Animation/AnimNotify_SpawnAreaZone.h" +#include "AbilitySystemBlueprintLibrary.h" + +UAnimNotify_SpawnAreaZone::UAnimNotify_SpawnAreaZone() +{ +} + +FString UAnimNotify_SpawnAreaZone::GetNotifyName_Implementation() const +{ + return TEXT("Spawn AreaZone Notify"); +} + +void UAnimNotify_SpawnAreaZone::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, + const FAnimNotifyEventReference& EventReference) +{ + Super::Notify(MeshComp, Animation, EventReference); + + if (MeshComp) + { + AActor* OwnerActor = MeshComp->GetOwner(); + if (OwnerActor) + { + FGameplayEventData PayloadData; + UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(OwnerActor, TriggerGameplayTag, PayloadData); + } + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.h b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.h new file mode 100644 index 00000000..a8d79955 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnAreaZone.h @@ -0,0 +1,33 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimNotifies/AnimNotify.h" +#include "GameplayTagContainer.h" +#include "AnimNotify_SpawnAreaZone.generated.h" + +/** + * Class Description: MeleeAttackHitCheck과 현재 기능은 똑같지만 분리와 확장을 고려해서 만듦 + * Author: 신동민 + * Created Date: 2025_07_02 + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +UCLASS() +class PROJECTMJ_API UAnimNotify_SpawnAreaZone : public UAnimNotify +{ + GENERATED_BODY() + +public: + UAnimNotify_SpawnAreaZone(); + +protected: + virtual FString GetNotifyName_Implementation() const override; + + virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override; + +protected: + UPROPERTY(EditAnywhere, Meta = (Categories = Event)) + FGameplayTag TriggerGameplayTag; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.cpp b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.cpp new file mode 100644 index 00000000..11a4c510 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.cpp @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Animation/AnimNotify_SpawnProjectile.h" +#include "AbilitySystemBlueprintLibrary.h" + +UAnimNotify_SpawnProjectile::UAnimNotify_SpawnProjectile() +{ +} + +FString UAnimNotify_SpawnProjectile::GetNotifyName_Implementation() const +{ + return TEXT("Spawn Projectile Notify"); +} + +void UAnimNotify_SpawnProjectile::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, + const FAnimNotifyEventReference& EventReference) +{ + + Super::Notify(MeshComp, Animation, EventReference); + if (MeshComp) + { + AActor* OwnerActor = MeshComp->GetOwner(); + if (OwnerActor) + { + FGameplayEventData PayloadData; + UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(OwnerActor, TriggerGameplayTag, PayloadData); + } + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.h b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.h new file mode 100644 index 00000000..2c3ef1cb --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Animation/AnimNotify_SpawnProjectile.h @@ -0,0 +1,33 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimNotifies/AnimNotify.h" +#include "GameplayTagContainer.h" +#include "AnimNotify_SpawnProjectile.generated.h" + +/** + * Class Description: MeleeAttackHitCheck과 현재 기능은 똑같지만 분리와 확장을 고려해서 만듦 + * Author: 신동민 + * Created Date: 2025_07_02 + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +UCLASS() +class PROJECTMJ_API UAnimNotify_SpawnProjectile : public UAnimNotify +{ + GENERATED_BODY() + +public: + UAnimNotify_SpawnProjectile(); + +protected: + virtual FString GetNotifyName_Implementation() const override; + + virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override; + +protected: + UPROPERTY(EditAnywhere, Meta = (Categories = Event)) + FGameplayTag TriggerGameplayTag; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.cpp b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.cpp index 1045400e..6e60eebc 100644 --- a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.cpp +++ b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.cpp @@ -3,11 +3,15 @@ #include "MJCharacterAttributeSet.h" #include "GameplayEffectExtension.h" +#include "ProjectMJ.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJStatComponentBase.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "Perception/AISense_Damage.h" UMJCharacterAttributeSet::UMJCharacterAttributeSet() - :Level(1.0f) - , MaxLevel(99.0f) - , Experience(0.0f) + : Experience(0.0f) , MaxExperience(999999.0f) , DropExperience(0.0f) , MaxDropExperience(999.0f) @@ -15,26 +19,19 @@ UMJCharacterAttributeSet::UMJCharacterAttributeSet() , Health(100.0f) , MaxHealth(100.0f) , HealthRegeneration(0.0f) - , MaxHealthRegeneration(100.0f) , Stamina(100.0f) , MaxStamina(100.0f) , StaminaRegeneration(0.0f) - , MaxStaminaRegeneration(100.0f) , Mana(100.0f) , MaxMana(100.0f) , ManaRegeneration(0.0f) - , MaxManaRegeneration(100.0f) , Focus(100.0f) , MaxFocus(100.0f) , FocusRegeneration(0.0f) - , MaxFocusRegeneration(100.0f) , AttackDamage(10.0f) - , MaxAttackDamage(999.0f) , AbilityPower(10.0f) - , MaxAbilityPower(999.0f) , Armor(0.0f) - , MaxArmor(999.0f) , Resistance(0.0f) , MaxResistance(999.0f) @@ -44,14 +41,11 @@ UMJCharacterAttributeSet::UMJCharacterAttributeSet() , MaxSkillCooldown(0.0f) , CriticalChance(0.0f) - , MaxCriticalChance(100.0f) , CriticalDamage(0.0f) - , MaxCriticalDamage(500.0f) , MovementSpeed(600.0f) , MaxMovementSpeed(1200.0f) - , Damage(0.0f) { InitHealth(GetMaxHealth()); InitStamina(GetMaxStamina()); @@ -62,12 +56,99 @@ UMJCharacterAttributeSet::UMJCharacterAttributeSet() void UMJCharacterAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { Super::PreAttributeChange(Attribute, NewValue); + + if (Attribute == GetHealthAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth()); + } + else if (Attribute == GetStaminaAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxStamina()); + } + else if (Attribute == GetManaAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana()); + } + else if (Attribute == GetFocusAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxFocus()); + } + // + else if (Attribute == GetExperienceAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxExperience()); + } + else if (Attribute == GetDropExperienceAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxDropExperience()); + } + // + else if (Attribute == GetAttackSpeedAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxAttackSpeed()); + } + else if (Attribute == GetSkillCooldownAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxSkillCooldown()); + } + else if (Attribute == GetResistanceAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxResistance()); + } + else if (Attribute == GetMovementSpeedAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMovementSpeed()); + } + } void UMJCharacterAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) { Super::PostAttributeChange(Attribute, OldValue, NewValue); + if (Attribute == GetMaxHealthAttribute()) + { + SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth())); + } + else if (Attribute == GetMaxStaminaAttribute()) + { + SetStamina(FMath::Clamp(GetStamina(), 0.f, GetMaxStamina())); + } + else if (Attribute == GetMaxManaAttribute()) + { + SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana())); + } + else if (Attribute == GetMaxFocusAttribute()) + { + SetFocus(FMath::Clamp(GetFocus(), 0.f, GetMaxFocus())); + } + // + else if (Attribute == GetMaxExperienceAttribute()) + { + SetExperience(FMath::Clamp(GetExperience(), 0.f, GetMaxExperience())); + } + else if (Attribute == GetMaxDropExperienceAttribute()) + { + SetDropExperience(FMath::Clamp(GetDropExperience(), 0.f, GetMaxDropExperience())); + } + // + else if (Attribute == GetMaxAttackSpeedAttribute()) + { + SetAttackSpeed(FMath::Clamp(GetAttackSpeed(), 0.f, GetMaxAttackSpeed())); + } + else if (Attribute == GetMaxSkillCooldownAttribute()) + { + SetSkillCooldown(FMath::Clamp(GetSkillCooldown(), 0.f, GetMaxSkillCooldown())); + } + else if (Attribute == GetMaxResistanceAttribute()) + { + SetResistance(FMath::Clamp(GetResistance(), 0.f, GetMaxResistance())); + } + else if (Attribute == GetMaxMovementSpeedAttribute()) + { + SetMovementSpeed(FMath::Clamp(GetMovementSpeed(), 0.f, GetMaxMovementSpeed())); + } + } bool UMJCharacterAttributeSet::PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data) @@ -84,5 +165,122 @@ bool UMJCharacterAttributeSet::PreGameplayEffectExecute(FGameplayEffectModCallba void UMJCharacterAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) { Super::PostGameplayEffectExecute(Data); + // Effect 적용 후 + + if (Data.Target.GetAvatarActor() == nullptr) + { + return; + } + + if (UMJStatComponentBase* StatComp = Data.Target.GetAvatarActor()->FindComponentByClass()) + { + if (Data.EvaluatedData.Attribute == GetHealthAttribute()) + { + if (GetHealth() <= 0) + { + if (!StatComp->GetbIsDead()) + { + // Minjin: 데미지를 입힌 상대 전달 + //StatComp->OnDeath.Broadcast(Data.EffectSpec.GetEffectContext().GetEffectCauser()); + StatComp->OnDead(Data.EffectSpec.GetEffectContext().GetEffectCauser()); + } + } + + if (Data.EvaluatedData.Magnitude < 0.f) //이렇게 하면 힐이 들어와도 ondamage가 실행되는 불상사를 막을 수 있는 거 같다.! + { + bool bIsCritical; + FGameplayTag IsCriticalTag = FGameplayTag::RequestGameplayTag(FName("Data.Character.IsCritical")); + if (Data.EffectSpec.GetDynamicAssetTags().HasTag(IsCriticalTag)) + { + bIsCritical = true; + } + else + { + bIsCritical = false; + } + StatComp->OnDamaged(Data.EvaluatedData.Magnitude, bIsCritical); + + // Minjin: Damage Perception - Data 사용함. TODO.위치 수정하기 + // EventLocation으로 들어가는 값이 Stimulus Location이다.- HitResult값이 nullptr이라서 Target의 Location 넣어놓음 + AActor* Target = Data.Target.GetAvatarActor(); + FVector TargetLocation = Target->GetActorLocation(); + UAISense_Damage::ReportDamageEvent(Target->GetWorld(), Target, + Data.EffectSpec.GetEffectContext().GetEffectCauser(), Data.EvaluatedData.Magnitude, + Target->GetActorLocation(), FVector::Zero()/*Data.EffectSpec.GetEffectContext().GetHitResult()->Location*/ + ); + + // Minjin: Damage Ability + AMJCharacterBase* Character = Cast(Target); + if (Character == nullptr) + { + return; + } + FGameplayTag DamageTag = FGameplayTag::RequestGameplayTag(FName("Character.State.IsHurt")); + if (Data.EffectSpec.GetDynamicAssetTags().HasTag(DamageTag)) + { + FGameplayEventData EventData; + EventData.EventTag = FGameplayTag::RequestGameplayTag(FName("Event.Character.Hurt")); + Character->ASC->HandleGameplayEvent(EventData.EventTag, &EventData); + } + } + } + + } + + if (Data.EvaluatedData.Attribute == GetHealthAttribute()) + { + SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth())); + } + else if (Data.EvaluatedData.Attribute == GetStaminaAttribute()) + { + SetStamina(FMath::Clamp(GetStamina(), 0.f, GetMaxStamina())); + } + else if (Data.EvaluatedData.Attribute == GetManaAttribute()) + { + SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana())); + } + else if (Data.EvaluatedData.Attribute == GetFocusAttribute()) + { + SetFocus(FMath::Clamp(GetFocus(), 0.f, GetMaxFocus())); + } + // + else if (Data.EvaluatedData.Attribute == GetExperienceAttribute()) + { + SetExperience(FMath::Clamp(GetExperience(), 0.f, GetMaxExperience())); + } + else if (Data.EvaluatedData.Attribute == GetDropExperienceAttribute()) + { + SetDropExperience(FMath::Clamp(GetDropExperience(), 0.f, GetMaxDropExperience())); + } + // + else if (Data.EvaluatedData.Attribute == GetAttackSpeedAttribute()) + { + SetAttackSpeed(FMath::Clamp(GetAttackSpeed(), 0.f, GetMaxAttackSpeed())); + } + else if (Data.EvaluatedData.Attribute == GetSkillCooldownAttribute()) + { + SetSkillCooldown(FMath::Clamp(GetSkillCooldown(), 0.f, GetMaxSkillCooldown())); + } + else if (Data.EvaluatedData.Attribute == GetResistanceAttribute()) + { + SetResistance(FMath::Clamp(GetResistance(), 0.f, GetMaxResistance())); + } + else if (Data.EvaluatedData.Attribute == GetMovementSpeedAttribute()) + { + SetMovementSpeed(FMath::Clamp(GetMovementSpeed(), 0.f, GetMaxMovementSpeed())); + } + // 이동속도 변경 바로 반영 + if(Data.EvaluatedData.Attribute == GetMaxMovementSpeedAttribute() || Data.EvaluatedData.Attribute == GetMovementSpeedAttribute()) + { + AMJCharacterBase* Character = Cast(Data.Target.GetAvatarActor()); + if (Character) + { + UCharacterMovementComponent* MoveComponent = Character->GetCharacterMovement(); + if (MoveComponent) + { + MoveComponent->MaxWalkSpeed = GetMaxMovementSpeed(); + } + } + } } \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.h b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.h index bf674e42..0d9e53c5 100644 --- a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.h +++ b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterAttributeSet.h @@ -19,6 +19,14 @@ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) * Created Date: 미상 * Last Modified By: 신동민 * Last Modified Date: 2025-06-19 + * Last Modified By: 김민진 + * Last Modified Date: (2025.07.22.) FOnDeathDelegate EffectCauser를 전달하게 수정 + * Last Modified Date: (2025.08.06.) StatComp로 Delegate 이동 + * Last Modified By: 김민진 + * Last Modified Date: (2025.08.08.) 데미지 어빌리티 추가 + * Description of Change: 안쓰는 Max 지우기 + * Modified By: 신동민 + * Modified Date: 2025.08.11 */ UCLASS() class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet @@ -29,9 +37,6 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet UMJCharacterAttributeSet(); // Level - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Level) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxLevel) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Experience) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxExperience) @@ -43,36 +48,29 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxHealth) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, HealthRegeneration) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxHealthRegeneration) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Stamina) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxStamina) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, StaminaRegeneration) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxStaminaRegeneration) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Mana) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxMana) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, ManaRegeneration) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxManaRegeneration) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Focus) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxFocus) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, FocusRegeneration) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxFocusRegeneration) // Attack / Ability ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, AttackDamage) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxAttackDamage) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, AbilityPower) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxAbilityPower) // Armor / Resistance (for future) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Armor) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxArmor) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Resistance) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxResistance) @@ -87,31 +85,29 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet // Critical ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, CriticalChance) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxCriticalChance) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, CriticalDamage) - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxCriticalDamage) // MovementSpeed ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MovementSpeed) ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, MaxMovementSpeed) - // --- Damage --- - ATTRIBUTE_ACCESSORS(UMJCharacterAttributeSet, Damage) - virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override; virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override; virtual bool PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data) override; virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override; + + + // EC에서 접근하게 해주기 위해서 + // 이거 아니면 접근지정자를 public으로 하는건데 Aura는 Public, 보통은 캡슐화를 위해서 protected(friend 하긴 하지만 그래도 최소한의 캡슐화) + friend struct FMJEC_SkillDamageStatics; + friend struct FMJEC_DamageOverTimeStatics; + friend struct FMJEC_MovementSpeedModifierStatics; + friend struct FMJEC_StaminaRegenStatics; protected: // Level - UPROPERTY(BlueprintReadOnly, Category = "Stat|Level", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData Level; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Level", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxLevel; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Level", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Experience; UPROPERTY(BlueprintReadOnly, Category = "Stat|Level", Meta = (AllowPrivateAccess = true)) @@ -130,8 +126,6 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData HealthRegeneration; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxHealthRegeneration; UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Stamina; @@ -140,8 +134,6 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData StaminaRegeneration; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxStaminaRegeneration; UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Mana; @@ -150,8 +142,6 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData ManaRegeneration; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxManaRegeneration; UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Focus; @@ -160,25 +150,17 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData FocusRegeneration; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Resource", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxFocusRegeneration; // Attack / Ability UPROPERTY(BlueprintReadOnly, Category = "Stat|Attack/Ability", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData AttackDamage; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Attack/Ability", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxAttackDamage; UPROPERTY(BlueprintReadOnly, Category = "Stat|Attack/Ability", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData AbilityPower; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Attack/Ability", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxAbilityPower; // Armor / Resistance UPROPERTY(BlueprintReadOnly, Category = "Stat|Armor/Resistance", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Armor; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Armor/Resistance", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxArmor; UPROPERTY(BlueprintReadOnly, Category = "Stat|Armor/Resistance", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Resistance; @@ -200,13 +182,9 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet // Critical UPROPERTY(BlueprintReadOnly, Category = "Stat|Critical", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CriticalChance; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Critical", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCriticalChance; UPROPERTY(BlueprintReadOnly, Category = "Stat|Critical", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CriticalDamage; - UPROPERTY(BlueprintReadOnly, Category = "Stat|Critical", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCriticalDamage; // MovementSpeed UPROPERTY(BlueprintReadOnly, Category = "Stat|MovementSpeed", Meta = (AllowPrivateAccess = true)) @@ -215,8 +193,4 @@ class PROJECTMJ_API UMJCharacterAttributeSet : public UAttributeSet FGameplayAttributeData MaxMovementSpeed; // Penetration (For future implementation) - - // --- Damage --- - UPROPERTY(BlueprintReadOnly, Category = "Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData Damage; }; diff --git a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.cpp b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.cpp index 3e26b7aa..61f9c224 100644 --- a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.cpp +++ b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.cpp @@ -3,61 +3,62 @@ #include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" -UMJCharacterSkillAttributeSet::UMJCharacterSkillAttributeSet() - : SkillLevel(1.0f) - , MaxSkillLevel(5.0f) +#include "GameplayEffectExtension.h" +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +UMJCharacterSkillAttributeSet::UMJCharacterSkillAttributeSet() + : // Cost - , CostStamina(0.0f) - , MaxCostStamina(100.0f) - , CostMana(0.0f) - , MaxCostMana(100.0f) + CostStamina(0.0f) + , CostMana(0.0f) , CostFocus(0.0f) - , MaxCostFocus(100.0f) // Damage / Scaling - , BaseDamage(10.0f) - , MaxBaseDamage(999.0f) + , BaseDamage(0.0f) , Healing(0.0f) - , MaxHealing(999.0f) , LifeSteal(0.0f) - , MaxLifeSteal(1.0f) - , AttackDamageScaling(1.0f) - , MaxAttackDamageScaling(10.0f) - , AbilityPowerScaling(1.0f) - , MaxAbilityPowerScaling(10.0f) + , AttackDamageScaling(0.0f) + , AbilityPowerScaling(0.0f) // Range - , SkillRadius(100.0f) - , MaxSkillRadius(1000.0f) - , SkillRange(500.0f) - , MaxSkillRange(5000.0f) + , SkillRadius(0.0f) + , SkillRange(0.0f) + , SkillAttackLocationOffset(0.0f) // Time - , Cooldown(1.0f) - , MaxCooldown(60.0f) - , SkillAttackRate(1.0f) - , MaxSkillAttackRate(10.0f) + , Cooldown(0.0f) + , MaxCooldown(0.0f) + , SkillAttackRate(0.0f) + , MaxSkillAttackRate(0.0f) , CastTime(0.0f) - , MaxCastTime(10.0f) , PreDelay(0.0f) - , MaxPreDelay(5.0f) , PostDelay(0.0f) - , MaxPostDelay(5.0f) , EffectDuration(0.0f) - , MaxEffectDuration(60.0f) - // Status effect + // Status Effect , StatusEffectChance(0.0f) - , MaxStatusEffectChance(1.0f) , StatusEffectDuration(0.0f) - , MaxStatusEffectDuration(60.0f) + , StatusEffectADScaling(0.0f) + , StatusEffectAPScaling(0.0f) + , StatusEffectMaxStack(0.0f) + , MaxStatusEffectMaxStack(0.0f) + , StatusEffectPeriod(0.0f) + , StatusEffectSlowPercent(0.0f) // Projectile - , ProjectileSpeed(1000.0f) - , MaxProjectileSpeed(10000.0f) - , ProjectileCount(1.0f) - , MaxProjectileCount(10.0f) + , ProjectileSpeed(0.0f) + , ProjectileCount(0.0f) + , MaxProjectileCount(0.0f) + , ProjectileLifeSpan(0.0f) + , ProjectilePierceCount(0.0f) + , MaxProjectilePierceCount(0.0f) + + // Explosion + , ExplosionRadius(0.0f) + , ExplosionBaseDamage(0.0f) + , ExplosionADScaling(0.0f) + , ExplosionAPScaling(0.0f) { } @@ -65,25 +66,56 @@ UMJCharacterSkillAttributeSet::UMJCharacterSkillAttributeSet() void UMJCharacterSkillAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { - Super::PreAttributeChange(Attribute, NewValue); - - // TODO: 필요한 Attribute들 다 Clamp로 구간 잡아줘야 할거 같음 - - if (Attribute == GetSkillRangeAttribute()) - { - NewValue = FMath::Clamp(NewValue, 0.1f, GetMaxSkillRange()); - } - else if (Attribute == GetSkillAttackRateAttribute()) - { - NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxSkillAttackRate()); - } - + Super::PreAttributeChange(Attribute, NewValue); + + if (Attribute == GetCooldownAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxCooldown()); + } + else if (Attribute == GetSkillAttackRateAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxSkillAttackRate()); + } + else if (Attribute == GetStatusEffectMaxStackAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxStatusEffectMaxStack()); + } + else if (Attribute == GetProjectileCountAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxProjectileCount()); + } + else if (Attribute == GetProjectilePierceCountAttribute()) + { + NewValue = FMath::Clamp(NewValue, 0.f, GetMaxProjectilePierceCount()); + } } void UMJCharacterSkillAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) { Super::PostAttributeChange(Attribute, OldValue, NewValue); + + if (Attribute == GetMaxCooldownAttribute()) + { + SetCooldown(FMath::Clamp(GetCooldown(), 0.f, GetMaxCooldown())); + } + else if (Attribute == GetMaxSkillAttackRateAttribute()) + { + SetSkillAttackRate(FMath::Clamp(GetSkillAttackRate(), 0.f, GetMaxSkillAttackRate())); + } + else if (Attribute == GetMaxStatusEffectMaxStackAttribute()) + { + SetStatusEffectMaxStack(FMath::Clamp(GetStatusEffectMaxStack(), 0.f, GetMaxStatusEffectMaxStack())); + } + else if (Attribute == GetMaxProjectileCountAttribute()) + { + SetProjectileCount(FMath::Clamp(GetProjectileCount(), 0.f, GetMaxProjectileCount())); + } + else if (Attribute == GetMaxProjectilePierceCountAttribute()) + { + SetProjectilePierceCount(FMath::Clamp(GetProjectilePierceCount(), 0.f, GetMaxProjectilePierceCount())); + } + } bool UMJCharacterSkillAttributeSet::PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data) @@ -94,4 +126,24 @@ bool UMJCharacterSkillAttributeSet::PreGameplayEffectExecute(FGameplayEffectModC void UMJCharacterSkillAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) { Super::PostGameplayEffectExecute(Data); -} + if (Data.EvaluatedData.Attribute == GetCooldownAttribute()) + { + SetCooldown(FMath::Clamp(GetCooldown(), 0.f, GetMaxCooldown())); + } + else if (Data.EvaluatedData.Attribute == GetSkillAttackRateAttribute()) + { + SetSkillAttackRate(FMath::Clamp(GetSkillAttackRate(), 0.f, GetMaxSkillAttackRate())); + } + else if (Data.EvaluatedData.Attribute == GetStatusEffectMaxStackAttribute()) + { + SetStatusEffectMaxStack(FMath::Clamp(GetStatusEffectMaxStack(), 0.f, GetMaxStatusEffectMaxStack())); + } + else if (Data.EvaluatedData.Attribute == GetProjectileCountAttribute()) + { + SetProjectileCount(FMath::Clamp(GetProjectileCount(), 0.f, GetMaxProjectileCount())); + } + else if (Data.EvaluatedData.Attribute == GetProjectilePierceCountAttribute()) + { + SetProjectilePierceCount(FMath::Clamp(GetProjectilePierceCount(), 0.f, GetMaxProjectilePierceCount())); + } + } \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h index 1be4221d..3f73547b 100644 --- a/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h +++ b/Source/ProjectMJ/AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h @@ -16,55 +16,53 @@ /** * Class Description: 스킬 어트리뷰트 세트 * Author: 신동민 - * Created Date: 2025_06_18 - * Last Modified By: 신동민 - * Last Modified Date: 2025_06_19 + * Created Date: 2025.06.18 + * Description of Change: 상태이상 값 추가 + * Modified By: 신동민 + * Modified Date: 2025.07.28 + * * Description of Change: 투사체 값 추가 + * Modified By: 신동민 + * Modified Date: 2025.07.31 + * Description of Change: 트레이스 위치 오프셋 값 추가 + * Modified By: 김민진 + * Modified Date: 2025.08.05. + * Description of Change: 안쓰는 Max 지우기 + * Modified By: 신동민 + * Modified Date: 2025.08.11 */ UCLASS() class PROJECTMJ_API UMJCharacterSkillAttributeSet : public UAttributeSet { GENERATED_BODY() -public: + public: UMJCharacterSkillAttributeSet(); - // Level - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, SkillLevel) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxSkillLevel) - // Cost ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, CostStamina) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxCostStamina) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, CostMana) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxCostMana) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, CostFocus) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxCostFocus) // Damage / Scaling ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, BaseDamage) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxBaseDamage) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, Healing) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxHealing) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, LifeSteal) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxLifeSteal) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, AttackDamageScaling) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxAttackDamageScaling) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, AbilityPowerScaling) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxAbilityPowerScaling) // Range ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, SkillRadius) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxSkillRadius) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, SkillRange) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxSkillRange) + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, SkillAttackLocationOffset) + // Time ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, Cooldown) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxCooldown) @@ -73,95 +71,93 @@ class PROJECTMJ_API UMJCharacterSkillAttributeSet : public UAttributeSet ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxSkillAttackRate) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, CastTime) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxCastTime) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, PreDelay) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxPreDelay) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, PostDelay) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxPostDelay) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, EffectDuration) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxEffectDuration) // Status effect ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectChance) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxStatusEffectChance) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectDuration) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxStatusEffectDuration) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusBaseDamage) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectADScaling) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectAPScaling) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectMaxStack) + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxStatusEffectMaxStack) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectPeriod) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, StatusEffectSlowPercent); // Projectile ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ProjectileSpeed) - ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxProjectileSpeed) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ProjectileCount) ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxProjectileCount) + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ProjectileLifeSpan) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ProjectilePierceCount) + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, MaxProjectilePierceCount) + + // Explosion + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ExplosionRadius) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ExplosionBaseDamage) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ExplosionADScaling) + + ATTRIBUTE_ACCESSORS(UMJCharacterSkillAttributeSet, ExplosionAPScaling) + virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override; virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override; virtual bool PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data) override; virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override; protected: - // Level - UPROPERTY(BlueprintReadOnly, Category = "Skill|Level", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData SkillLevel; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Level", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxSkillLevel; // Cost UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CostStamina; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCostStamina; UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CostMana; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCostMana; UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CostFocus; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Cost", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCostFocus; // Damage / Scaling UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData BaseDamage; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxBaseDamage; UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData Healing; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxHealing; UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData LifeSteal; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxLifeSteal; UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData AttackDamageScaling; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxAttackDamageScaling; UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData AbilityPowerScaling; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Damage", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxAbilityPowerScaling; // Range UPROPERTY(BlueprintReadOnly, Category = "Skill|Range", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData SkillRadius; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Range", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxSkillRadius; UPROPERTY(BlueprintReadOnly, Category = "Skill|Range", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData SkillRange; + UPROPERTY(BlueprintReadOnly, Category = "Skill|Range", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxSkillRange; + FGameplayAttributeData SkillAttackLocationOffset; // Time UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) @@ -176,44 +172,71 @@ class PROJECTMJ_API UMJCharacterSkillAttributeSet : public UAttributeSet UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData CastTime; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxCastTime; UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData PreDelay; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxPreDelay; UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData PostDelay; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxPostDelay; UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData EffectDuration; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Time", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxEffectDuration; // Status effect UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData StatusEffectChance; - UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxStatusEffectChance; UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData StatusEffectDuration; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData StatusEffectADScaling; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData StatusBaseDamage; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData StatusEffectAPScaling; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData StatusEffectMaxStack; + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData MaxStatusEffectMaxStack; + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxStatusEffectDuration; + FGameplayAttributeData StatusEffectPeriod; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|StatusEffect", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData StatusEffectSlowPercent; // Projectile UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData ProjectileSpeed; - UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) - FGameplayAttributeData MaxProjectileSpeed; UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData ProjectileCount; UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) FGameplayAttributeData MaxProjectileCount; + UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ProjectileLifeSpan; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ProjectilePierceCount; + UPROPERTY(BlueprintReadOnly, Category = "Skill|Projectile", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData MaxProjectilePierceCount; + + // Explosion + UPROPERTY(BlueprintReadOnly, Category = "Skill|Explosion", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ExplosionRadius; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|Explosion", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ExplosionBaseDamage; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|Explosion", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ExplosionADScaling; + + UPROPERTY(BlueprintReadOnly, Category = "Skill|Explosion", Meta = (AllowPrivateAccess = true)) + FGameplayAttributeData ExplosionAPScaling; + }; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.cpp new file mode 100644 index 00000000..3f48a81b --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.cpp @@ -0,0 +1,82 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJEC_DamageOverTime.h" + +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + +struct FMJEC_DamageOverTimeStatics +{ + DECLARE_ATTRIBUTE_CAPTUREDEF(Health) + + DECLARE_ATTRIBUTE_CAPTUREDEF(AttackDamage) + DECLARE_ATTRIBUTE_CAPTUREDEF(AbilityPower) + + FMJEC_DamageOverTimeStatics() + { + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, Health, Target, false) + + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, AttackDamage, Source, true) + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, AbilityPower, Source, true) + } +}; + +static const FMJEC_DamageOverTimeStatics& DamageOverTimeStatics() +{ + static FMJEC_DamageOverTimeStatics Statics; + return Statics; +} + +UMJEC_DamageOverTime::UMJEC_DamageOverTime() +{ + RelevantAttributesToCapture.Add(DamageOverTimeStatics().HealthDef); + + RelevantAttributesToCapture.Add(DamageOverTimeStatics().AttackDamageDef); + RelevantAttributesToCapture.Add(DamageOverTimeStatics().AbilityPowerDef); +} + +void UMJEC_DamageOverTime::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, + FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const +{ + Super::Execute_Implementation(ExecutionParams, OutExecutionOutput); + + const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); + + const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); + const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags(); + + FAggregatorEvaluateParameters EvaluationParameters; + + EvaluationParameters.SourceTags = SourceTags; + EvaluationParameters.TargetTags = TargetTags; + + float SourceAttackDamage = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageOverTimeStatics().AttackDamageDef, EvaluationParameters, SourceAttackDamage); + + float SourceAbilityPower = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageOverTimeStatics().AbilityPowerDef, EvaluationParameters, SourceAbilityPower); + + + float BaseDamage = 0.0f; + BaseDamage = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusBaseDamage")), false, BaseDamage); + + float AttackDamageScaling = 0.0f; + AttackDamageScaling = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectADScaling")), false, AttackDamageScaling); + + float AbilityPowerScaling = 0.0f; + AbilityPowerScaling = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.StatusEffectAPScaling")), false, AbilityPowerScaling); + + float TickDamage = BaseDamage + (SourceAttackDamage * AttackDamageScaling) + (SourceAbilityPower * AbilityPowerScaling); + + + int32 StackCount = Spec.GetStackCount(); + if (StackCount > 1) + { + TickDamage *= StackCount; + } + + if (TickDamage > 0.f) + { + OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UMJCharacterAttributeSet::GetHealthAttribute(), EGameplayModOp::Additive, -TickDamage)); + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.h b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.h new file mode 100644 index 00000000..ce6704cf --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_DamageOverTime.h @@ -0,0 +1,26 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffectExecutionCalculation.h" +#include "MJEC_DamageOverTime.generated.h" + +/** + * Class Description: DOT 주기적인 피해를 계산하는 클래스 + * Author: 신동민 + * Created Date: 2025.07.28 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJEC_DamageOverTime : public UGameplayEffectExecutionCalculation +{ + GENERATED_BODY() +public: + UMJEC_DamageOverTime(); + + virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.cpp new file mode 100644 index 00000000..466975ed --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.cpp @@ -0,0 +1,52 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJEC_MovementSpeedModifier.h" + +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + +struct FMJEC_MovementSpeedModifierStatics +{ + DECLARE_ATTRIBUTE_CAPTUREDEF(MovementSpeed) + + FMJEC_MovementSpeedModifierStatics() + { + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, MovementSpeed, Target, false) + } +}; + +static const FMJEC_MovementSpeedModifierStatics& MovementSpeedModifierStatics() +{ + static FMJEC_MovementSpeedModifierStatics Statics; + return Statics; +} + +UMJEC_MovementSpeedModifier::UMJEC_MovementSpeedModifier() +{ + RelevantAttributesToCapture.Add(MovementSpeedModifierStatics().MovementSpeedDef); +} + +void UMJEC_MovementSpeedModifier::Execute_Implementation( + const FGameplayEffectCustomExecutionParameters& ExecutionParams, + FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const +{ + Super::Execute_Implementation(ExecutionParams, OutExecutionOutput); + + const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); + + const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); + const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags(); + + FAggregatorEvaluateParameters EvaluationParameters; + + EvaluationParameters.SourceTags = SourceTags; + EvaluationParameters.TargetTags = TargetTags; + + float SlowPercent = 0.0f; + SlowPercent = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag("Data.Skill.StatusEffectSlowPercent"), true, SlowPercent); + if (SlowPercent != 0.f) + { + float NewSpeedMult = FMath::Clamp(1.f - SlowPercent * 0.01f, 0.f, 1.f); + OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UMJCharacterAttributeSet::GetMaxMovementSpeedAttribute(), EGameplayModOp::Multiplicitive, NewSpeedMult)); + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.h b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.h new file mode 100644 index 00000000..becb178e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_MovementSpeedModifier.h @@ -0,0 +1,28 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffectExecutionCalculation.h" +#include "MJEC_MovementSpeedModifier.generated.h" + +/** + * Class Description: DOT 주기적인 피해를 계산하는 클래스 + * Author: 신동민 + * Created Date: 2025.07.28 + * Description of Change: + * Modified By: + * Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJEC_MovementSpeedModifier : public UGameplayEffectExecutionCalculation +{ + GENERATED_BODY() + +public: + UMJEC_MovementSpeedModifier(); + + virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.cpp new file mode 100644 index 00000000..bab59b17 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.cpp @@ -0,0 +1,105 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Effect/MJEC_SkillDamage.h" + +#include "ProjectMJ.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + +// TODO: 지금은 주는 순수 데미지만 계산하는데, 버프 디버프 관련 공식은 빠져있다. + 난수도 빠져 있다 +struct FMJEC_SkillDamageStatics +{ + + DECLARE_ATTRIBUTE_CAPTUREDEF(Health) + + DECLARE_ATTRIBUTE_CAPTUREDEF(AttackDamage) + DECLARE_ATTRIBUTE_CAPTUREDEF(AbilityPower) + DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalChance) + DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalDamage) + + FMJEC_SkillDamageStatics() + { + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, Health, Target, false) + + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, AttackDamage, Source, true) + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, AbilityPower, Source, true) + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, CriticalChance, Source, true) + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, CriticalDamage, Source, true) + } +}; + +static const FMJEC_SkillDamageStatics& DamageStatics() +{ + static FMJEC_SkillDamageStatics DStatics; + return DStatics; +} + +UMJEC_SkillDamage::UMJEC_SkillDamage() +{ + RelevantAttributesToCapture.Add(DamageStatics().HealthDef); + + RelevantAttributesToCapture.Add(DamageStatics().AttackDamageDef); + RelevantAttributesToCapture.Add(DamageStatics().AbilityPowerDef); + RelevantAttributesToCapture.Add(DamageStatics().CriticalChanceDef); + RelevantAttributesToCapture.Add(DamageStatics().CriticalDamageDef); +} + + +void UMJEC_SkillDamage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, + FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const +{ + // Super::Execute_Implementation(ExecutionParams, OutExecutionOutput); + + const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); + + const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); + const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags(); + + FAggregatorEvaluateParameters EvaluationParameters; + + EvaluationParameters.SourceTags = SourceTags; + EvaluationParameters.TargetTags = TargetTags; + + float SourceAttackDamage = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().AttackDamageDef, EvaluationParameters, SourceAttackDamage); + + float SourceAbilityPower = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().AbilityPowerDef, EvaluationParameters, SourceAbilityPower); + + float CriticalChance = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalChanceDef, EvaluationParameters, CriticalChance); + + float CriticalDamage = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalDamageDef, EvaluationParameters, CriticalDamage); + + + float BaseDamage = 0.0f; + BaseDamage = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.BaseDamage")), true, BaseDamage); + + float AttackDamageScaling = 0.0f; + AttackDamageScaling = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AttackDamageScaling")), true, AttackDamageScaling); + + float AbilityPowerScaling = 0.0f; + AbilityPowerScaling = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.AbilityPowerScaling")), true, AbilityPowerScaling); + + float FinalDamage = BaseDamage + ((AttackDamageScaling / 100) * SourceAttackDamage) + ((AbilityPowerScaling / 100) * SourceAbilityPower); + + // Minjin: Damage Tag 부여 + FGameplayTag DamageTag = FGameplayTag::RequestGameplayTag(FName("Character.State.IsHurt")); + ExecutionParams.GetOwningSpecForPreExecuteMod()->AddDynamicAssetTag(DamageTag); + + bool bIsCritical = FMath::FRandRange(0.f, 100.f) < CriticalChance; + if (bIsCritical) + { + FinalDamage *= (1.0f + CriticalDamage / 100.0f); + // TODO:크리티컬 연출/태그 등 처리 + + FGameplayTag IsCriticalTag = FGameplayTag::RequestGameplayTag(FName("Data.Character.IsCritical")); + ExecutionParams.GetOwningSpecForPreExecuteMod()->AddDynamicAssetTag(IsCriticalTag); + } + + // TODO: 난수 추가 + OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UMJCharacterAttributeSet::GetHealthAttribute(), EGameplayModOp::Additive, -FinalDamage)); + +} \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.h b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.h new file mode 100644 index 00000000..60996431 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_SkillDamage.h @@ -0,0 +1,26 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffectExecutionCalculation.h" +#include "MJEC_SkillDamage.generated.h" + +/** + * Class Description: 데미지 계산을 하는 이펙트 + * Author: 신동민 + * Created Date: 2025.07.07 + * Last Modified By: 김민진 + * Last Modified Date: (25.08.08.) 데미지 태그 부여 + */ + +UCLASS() +class PROJECTMJ_API UMJEC_SkillDamage : public UGameplayEffectExecutionCalculation +{ + GENERATED_BODY() +public: + UMJEC_SkillDamage(); + + virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.cpp new file mode 100644 index 00000000..6a9c6127 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.cpp @@ -0,0 +1,68 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJEC_StaminaRegen.h" + +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + +struct FMJEC_StaminaRegenStatics +{ + DECLARE_ATTRIBUTE_CAPTUREDEF(StaminaRegeneration); + DECLARE_ATTRIBUTE_CAPTUREDEF(Stamina); + DECLARE_ATTRIBUTE_CAPTUREDEF(MaxStamina); + + FMJEC_StaminaRegenStatics() + { + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, StaminaRegeneration, Target, false); + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, Stamina, Target, false); + DEFINE_ATTRIBUTE_CAPTUREDEF(UMJCharacterAttributeSet, MaxStamina, Target, false); + } +}; +static FMJEC_StaminaRegenStatics& RegenStatics() +{ + static FMJEC_StaminaRegenStatics RStatics; + return RStatics; +} + +UMJEC_StaminaRegen::UMJEC_StaminaRegen() +{ + RelevantAttributesToCapture.Add(RegenStatics().StaminaRegenerationDef); + RelevantAttributesToCapture.Add(RegenStatics().StaminaDef); + RelevantAttributesToCapture.Add(RegenStatics().MaxStaminaDef); +} + +void UMJEC_StaminaRegen::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, + FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const +{ + Super::Execute_Implementation(ExecutionParams, OutExecutionOutput); + + const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); + + const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); + + FAggregatorEvaluateParameters EvaluationParameters; + + EvaluationParameters.SourceTags = SourceTags; + + float RegenPerSec = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(RegenStatics().StaminaRegenerationDef, FAggregatorEvaluateParameters(), RegenPerSec); + + float CurrentStamina = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(RegenStatics().StaminaDef, FAggregatorEvaluateParameters(), CurrentStamina); + + float MaxStamina = 0.0f; + ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(RegenStatics().MaxStaminaDef, FAggregatorEvaluateParameters(), MaxStamina); + + // TODO: 하드코딩 개선 + const float Period = 1.0f; + + // Attribute에서 Clamp 했지만 여기서도 처리하는게 좋음 - 동민 - + const float Missing = FMath::Max(0.f, MaxStamina - CurrentStamina); + const float Gain = FMath::Max(0.f, RegenPerSec) * Period; + const float Final = FMath::Min(Gain, Missing); + + if (Final > 0.f) + { + OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UMJCharacterAttributeSet::GetStaminaAttribute(), EGameplayModOp::Additive, Final)); + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.h b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.h new file mode 100644 index 00000000..1ac2e477 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJEC_StaminaRegen.h @@ -0,0 +1,27 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffectExecutionCalculation.h" +#include "MJEC_StaminaRegen.generated.h" + +/** + * Class Description: 스태미너 재생 계산 + * Author: 신동민 + * Created Date: 2025.08.12 + * Description of Change: + * Modified By: + * Modified Date: + */ + + +UCLASS() +class PROJECTMJ_API UMJEC_StaminaRegen : public UGameplayEffectExecutionCalculation +{ + GENERATED_BODY() +public: + + UMJEC_StaminaRegen(); + virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_AttackDamage.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_AttackDamage.cpp index 0ea309f9..f4df7064 100644 --- a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_AttackDamage.cpp +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_AttackDamage.cpp @@ -2,9 +2,14 @@ #include "AbilitySystem/Effect/MJGE_AttackDamage.h" -#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Effect/MJEC_SkillDamage.h" UMJGE_AttackDamage::UMJGE_AttackDamage() { DurationPolicy = EGameplayEffectDurationType::Instant; + + FGameplayEffectExecutionDefinition ExecutionDefinition; + ExecutionDefinition.CalculationClass = UMJEC_SkillDamage::StaticClass(); + + Executions.Add(ExecutionDefinition); } diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.cpp new file mode 100644 index 00000000..46246f47 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.cpp @@ -0,0 +1,28 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJGE_Frozen.h" + +#include "MJEC_MovementSpeedModifier.h" + +UMJGE_Frozen::UMJGE_Frozen() +{ + DurationPolicy = EGameplayEffectDurationType::HasDuration; + + FGameplayEffectExecutionDefinition ExecutionDefinition; + ExecutionDefinition.CalculationClass = UMJEC_MovementSpeedModifier::StaticClass(); + + Executions.Add(ExecutionDefinition); + + FSetByCallerFloat DurationCaller; + DurationCaller.DataTag = FGameplayTag::RequestGameplayTag("Data.Skill.StatusEffectDuration"); + DurationMagnitude = FGameplayEffectModifierMagnitude(DurationCaller); + + // Period 도 SetByCaller로 넣을 수 있나 + Period = 1.0f; + + StackLimitCount = 1; + StackingType = EGameplayEffectStackingType::AggregateBySource; + StackDurationRefreshPolicy = EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; + StackPeriodResetPolicy = EGameplayEffectStackingPeriodPolicy::NeverReset; +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.h b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.h new file mode 100644 index 00000000..9afbe702 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Frozen.h @@ -0,0 +1,24 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffect.h" +#include "MJGE_Frozen.generated.h" + +/** + * Class Description: 동상 상태이상 - 느려짐(Skill에서 StatusEffectSlowPercent 값 설정 필요) + * Author: Kim Minjin + * Created Date: 2025.08.12. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGE_Frozen : public UGameplayEffect +{ + GENERATED_BODY() + +public: + UMJGE_Frozen(); +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.cpp new file mode 100644 index 00000000..96a8a6fa --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.cpp @@ -0,0 +1,28 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJGE_Poison.h" + +#include "MJEC_DamageOverTime.h" + +UMJGE_Poison::UMJGE_Poison() +{ + DurationPolicy = EGameplayEffectDurationType::HasDuration; + + FGameplayEffectExecutionDefinition ExecutionDefinition; + ExecutionDefinition.CalculationClass = UMJEC_DamageOverTime::StaticClass(); + + Executions.Add(ExecutionDefinition); + + FSetByCallerFloat DurationCaller; + DurationCaller.DataTag = FGameplayTag::RequestGameplayTag("Data.Skill.StatusEffectDuration"); + DurationMagnitude = FGameplayEffectModifierMagnitude(DurationCaller); + + // Period 도 SetByCaller로 넣을 수 있나 + Period = 1.0f; + + StackLimitCount = 9; + StackingType = EGameplayEffectStackingType::AggregateBySource; + StackDurationRefreshPolicy = EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; + StackPeriodResetPolicy = EGameplayEffectStackingPeriodPolicy::NeverReset; +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.h b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.h new file mode 100644 index 00000000..45c380be --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_Poison.h @@ -0,0 +1,25 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffect.h" +#include "MJGE_Poison.generated.h" + +/** + * Class Description: 독 상태이상 + * Author: 신동민 + * Created Date: 2025.07.28 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGE_Poison : public UGameplayEffect +{ + GENERATED_BODY() +public: + UMJGE_Poison(); + + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.cpp new file mode 100644 index 00000000..fe9ddb61 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.cpp @@ -0,0 +1,90 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + +UMJGE_SetCharacterAttributeSet::UMJGE_SetCharacterAttributeSet() +{ + DurationPolicy = EGameplayEffectDurationType::Instant; + + struct FAttributeSetter + { + FGameplayAttribute Attribute; + FName TagName; + }; + + // TODO: Max가 먼저 와야하는 문제 + const FAttributeSetter Attributes[] = { + // Level + { UMJCharacterAttributeSet::GetMaxExperienceAttribute(), FName("Data.Character.MaxExperience") }, + { UMJCharacterAttributeSet::GetExperienceAttribute(), FName("Data.Character.Experience") }, + + { UMJCharacterAttributeSet::GetMaxDropExperienceAttribute(), FName("Data.Character.MaxDropExperience") }, + { UMJCharacterAttributeSet::GetDropExperienceAttribute(), FName("Data.Character.DropExperience") }, + + // Resource + { UMJCharacterAttributeSet::GetMaxHealthAttribute(), FName("Data.Character.MaxHealth") }, + { UMJCharacterAttributeSet::GetHealthAttribute(), FName("Data.Character.Health") }, + + { UMJCharacterAttributeSet::GetHealthRegenerationAttribute(), FName("Data.Character.HealthRegeneration") }, + + { UMJCharacterAttributeSet::GetMaxStaminaAttribute(), FName("Data.Character.MaxStamina") }, + { UMJCharacterAttributeSet::GetStaminaAttribute(), FName("Data.Character.Stamina") }, + + { UMJCharacterAttributeSet::GetStaminaRegenerationAttribute(), FName("Data.Character.StaminaRegeneration") }, + + { UMJCharacterAttributeSet::GetMaxManaAttribute(), FName("Data.Character.MaxMana") }, + + { UMJCharacterAttributeSet::GetManaAttribute(), FName("Data.Character.Mana") }, + + { UMJCharacterAttributeSet::GetManaRegenerationAttribute(), FName("Data.Character.ManaRegeneration") }, + + { UMJCharacterAttributeSet::GetMaxFocusAttribute(), FName("Data.Character.MaxFocus") }, + { UMJCharacterAttributeSet::GetFocusAttribute(), FName("Data.Character.Focus") }, + + { UMJCharacterAttributeSet::GetFocusRegenerationAttribute(), FName("Data.Character.FocusRegeneration") }, + + // Attack / Ability + { UMJCharacterAttributeSet::GetAttackDamageAttribute(), FName("Data.Character.AttackDamage") }, + + { UMJCharacterAttributeSet::GetAbilityPowerAttribute(), FName("Data.Character.AbilityPower") }, + + // Armor / Resistance + { UMJCharacterAttributeSet::GetArmorAttribute(), FName("Data.Character.Armor") }, + + { UMJCharacterAttributeSet::GetMaxResistanceAttribute(), FName("Data.Character.MaxResistance") }, + { UMJCharacterAttributeSet::GetResistanceAttribute(), FName("Data.Character.Resistance") }, + + // Attack Speed + { UMJCharacterAttributeSet::GetMaxAttackSpeedAttribute(), FName("Data.Character.MaxAttackSpeed") }, + { UMJCharacterAttributeSet::GetAttackSpeedAttribute(), FName("Data.Character.AttackSpeed") }, + + + // Skill Cooldown + { UMJCharacterAttributeSet::GetMaxSkillCooldownAttribute(), FName("Data.Character.MaxSkillCooldown") }, + { UMJCharacterAttributeSet::GetSkillCooldownAttribute(), FName("Data.Character.SkillCooldown") }, + + // Critical + { UMJCharacterAttributeSet::GetCriticalChanceAttribute(), FName("Data.Character.CriticalChance") }, + + { UMJCharacterAttributeSet::GetCriticalDamageAttribute(), FName("Data.Character.CriticalDamage") }, + + // MovementSpeed + { UMJCharacterAttributeSet::GetMaxMovementSpeedAttribute(), FName("Data.Character.MaxMovementSpeed") }, + { UMJCharacterAttributeSet::GetMovementSpeedAttribute(), FName("Data.Character.MovementSpeed") }, + + }; + + for (const auto& Iter : Attributes) + { + FGameplayModifierInfo ModifierInfo; + ModifierInfo.Attribute = Iter.Attribute; + ModifierInfo.ModifierOp = EGameplayModOp::Override; + + FSetByCallerFloat SetByCaller; + SetByCaller.DataTag = FGameplayTag::RequestGameplayTag(Iter.TagName); + ModifierInfo.ModifierMagnitude = SetByCaller; + Modifiers.Add(ModifierInfo); + } +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h new file mode 100644 index 00000000..5aa18c03 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h @@ -0,0 +1,24 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffect.h" +#include "MJGE_SetCharacterAttributeSet.generated.h" + +/** + * Class Description: Set Character Attribute Setter Using GameplayEffect + * Author: 신동민 + * Created Date: 2025.07.17 + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGE_SetCharacterAttributeSet : public UGameplayEffect +{ + GENERATED_BODY() + +public: + UMJGE_SetCharacterAttributeSet(); + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetSkillAttributeSet.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetSkillAttributeSet.cpp index 302e9a13..3bbcd56d 100644 --- a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetSkillAttributeSet.cpp +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SetSkillAttributeSet.cpp @@ -16,60 +16,61 @@ UMJGE_SetSkillAttributeSet::UMJGE_SetSkillAttributeSet() const FAttributeSetter Attributes[] = { // Level - { UMJCharacterSkillAttributeSet::GetSkillLevelAttribute(), FName("Data.Skill.SkillLevel") }, - { UMJCharacterSkillAttributeSet::GetMaxSkillLevelAttribute(), FName("Data.Skill.MaxSkillLevel") }, // Cost { UMJCharacterSkillAttributeSet::GetCostStaminaAttribute(), FName("Data.Skill.CostStamina") }, - { UMJCharacterSkillAttributeSet::GetMaxCostStaminaAttribute(), FName("Data.Skill.MaxCostStamina") }, { UMJCharacterSkillAttributeSet::GetCostManaAttribute(), FName("Data.Skill.CostMana") }, - { UMJCharacterSkillAttributeSet::GetMaxCostManaAttribute(), FName("Data.Skill.MaxCostMana") }, { UMJCharacterSkillAttributeSet::GetCostFocusAttribute(), FName("Data.Skill.CostFocus") }, - { UMJCharacterSkillAttributeSet::GetMaxCostFocusAttribute(), FName("Data.Skill.MaxCostFocus") }, // Damage / Scaling { UMJCharacterSkillAttributeSet::GetBaseDamageAttribute(), FName("Data.Skill.BaseDamage") }, - { UMJCharacterSkillAttributeSet::GetMaxBaseDamageAttribute(), FName("Data.Skill.MaxBaseDamage") }, { UMJCharacterSkillAttributeSet::GetHealingAttribute(), FName("Data.Skill.Healing") }, - { UMJCharacterSkillAttributeSet::GetMaxHealingAttribute(), FName("Data.Skill.MaxHealing") }, { UMJCharacterSkillAttributeSet::GetLifeStealAttribute(), FName("Data.Skill.LifeSteal") }, - { UMJCharacterSkillAttributeSet::GetMaxLifeStealAttribute(), FName("Data.Skill.MaxLifeSteal") }, { UMJCharacterSkillAttributeSet::GetAttackDamageScalingAttribute(), FName("Data.Skill.AttackDamageScaling") }, - { UMJCharacterSkillAttributeSet::GetMaxAttackDamageScalingAttribute(), FName("Data.Skill.MaxAttackDamageScaling") }, { UMJCharacterSkillAttributeSet::GetAbilityPowerScalingAttribute(), FName("Data.Skill.AbilityPowerScaling") }, - { UMJCharacterSkillAttributeSet::GetMaxAbilityPowerScalingAttribute(), FName("Data.Skill.MaxAbilityPowerScaling") }, // Range { UMJCharacterSkillAttributeSet::GetSkillRadiusAttribute(), FName("Data.Skill.SkillRadius") }, - { UMJCharacterSkillAttributeSet::GetMaxSkillRadiusAttribute(), FName("Data.Skill.MaxSkillRadius") }, { UMJCharacterSkillAttributeSet::GetSkillRangeAttribute(), FName("Data.Skill.SkillRange") }, - { UMJCharacterSkillAttributeSet::GetMaxSkillRangeAttribute(), FName("Data.Skill.MaxSkillRange") }, + { UMJCharacterSkillAttributeSet::GetSkillAttackLocationOffsetAttribute(), FName("Data.Skill.SkillAttackLocationOffset") }, // Time - { UMJCharacterSkillAttributeSet::GetCooldownAttribute(), FName("Data.Skill.Cooldown") }, + // DOTO: Max가 먼저 와야함 { UMJCharacterSkillAttributeSet::GetMaxCooldownAttribute(), FName("Data.Skill.MaxCooldown") }, + { UMJCharacterSkillAttributeSet::GetCooldownAttribute(), FName("Data.Skill.Cooldown") }, + { UMJCharacterSkillAttributeSet::GetMaxSkillAttackRateAttribute(), FName("Data.Skill.MaxSkillAttackRate") }, { UMJCharacterSkillAttributeSet::GetSkillAttackRateAttribute(), FName("Data.Skill.SkillAttackRate") }, - { UMJCharacterSkillAttributeSet::GetMaxSkillAttackRateAttribute(), FName("Data.Skill.MaxSkillAttackRate") }, { UMJCharacterSkillAttributeSet::GetCastTimeAttribute(), FName("Data.Skill.CastTime") }, - { UMJCharacterSkillAttributeSet::GetMaxCastTimeAttribute(), FName("Data.Skill.MaxCastTime") }, { UMJCharacterSkillAttributeSet::GetPreDelayAttribute(), FName("Data.Skill.PreDelay") }, - { UMJCharacterSkillAttributeSet::GetMaxPreDelayAttribute(), FName("Data.Skill.MaxPreDelay") }, { UMJCharacterSkillAttributeSet::GetPostDelayAttribute(), FName("Data.Skill.PostDelay") }, - { UMJCharacterSkillAttributeSet::GetMaxPostDelayAttribute(), FName("Data.Skill.MaxPostDelay") }, { UMJCharacterSkillAttributeSet::GetEffectDurationAttribute(), FName("Data.Skill.EffectDuration") }, - { UMJCharacterSkillAttributeSet::GetMaxEffectDurationAttribute(), FName("Data.Skill.MaxEffectDuration") }, // Status effect { UMJCharacterSkillAttributeSet::GetStatusEffectChanceAttribute(), FName("Data.Skill.StatusEffectChance") }, - { UMJCharacterSkillAttributeSet::GetMaxStatusEffectChanceAttribute(), FName("Data.Skill.MaxStatusEffectChance") }, { UMJCharacterSkillAttributeSet::GetStatusEffectDurationAttribute(), FName("Data.Skill.StatusEffectDuration") }, - { UMJCharacterSkillAttributeSet::GetMaxStatusEffectDurationAttribute(), FName("Data.Skill.MaxStatusEffectDuration") }, + { UMJCharacterSkillAttributeSet::GetStatusBaseDamageAttribute(), FName("Data.Skill.StatusBaseDamage") }, + { UMJCharacterSkillAttributeSet::GetStatusEffectADScalingAttribute(),FName("Data.Skill.StatusEffectADScaling") }, + { UMJCharacterSkillAttributeSet::GetStatusEffectAPScalingAttribute(), FName("Data.Skill.StatusEffectAPScaling") }, + { UMJCharacterSkillAttributeSet::GetMaxStatusEffectMaxStackAttribute(), FName("Data.Skill.MaxStatusEffectMaxStack") }, + + { UMJCharacterSkillAttributeSet::GetStatusEffectMaxStackAttribute(), FName("Data.Skill.StatusEffectMaxStack") }, + { UMJCharacterSkillAttributeSet::GetStatusEffectPeriodAttribute(), FName("Data.Skill.StatusEffectPeriod") }, + { UMJCharacterSkillAttributeSet::GetStatusEffectSlowPercentAttribute(),FName("Data.Skill.StatusEffectSlowPercent") }, // Projectile { UMJCharacterSkillAttributeSet::GetProjectileSpeedAttribute(), FName("Data.Skill.ProjectileSpeed") }, - { UMJCharacterSkillAttributeSet::GetMaxProjectileSpeedAttribute(), FName("Data.Skill.MaxProjectileSpeed") }, + { UMJCharacterSkillAttributeSet::GetMaxProjectileCountAttribute(), FName("Data.Skill.MaxProjectileCount") }, { UMJCharacterSkillAttributeSet::GetProjectileCountAttribute(), FName("Data.Skill.ProjectileCount") }, - { UMJCharacterSkillAttributeSet::GetMaxProjectileCountAttribute(), FName("Data.Skill.MaxProjectileCount") } + { UMJCharacterSkillAttributeSet::GetProjectileLifeSpanAttribute(), FName("Data.Skill.ProjectileLifeSpan") }, + { UMJCharacterSkillAttributeSet::GetMaxProjectilePierceCountAttribute(), FName("Data.Skill.MaxProjectilePierceCount") }, + { UMJCharacterSkillAttributeSet::GetProjectilePierceCountAttribute(), FName("Data.Skill.ProjectilePierceCount") }, + + // Explosion + { UMJCharacterSkillAttributeSet::GetExplosionRadiusAttribute(), FName("Data.Skill.ExplosionRadius") }, + { UMJCharacterSkillAttributeSet::GetExplosionBaseDamageAttribute(), FName("Data.Skill.ExplosionBaseDamage") }, + { UMJCharacterSkillAttributeSet::GetExplosionADScalingAttribute(), FName("Data.Skill.ExplosionADScaling") }, + { UMJCharacterSkillAttributeSet::GetExplosionAPScalingAttribute(), FName("Data.Skill.ExplosionAPScaling") }, + }; for (const auto& Iter : Attributes) diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.cpp new file mode 100644 index 00000000..5cdcd1bc --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.cpp @@ -0,0 +1,13 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJGE_SkillCooldown.h" + +UMJGE_SkillCooldown::UMJGE_SkillCooldown() +{ + DurationPolicy = EGameplayEffectDurationType::HasDuration; + + FSetByCallerFloat SetByCallerCooldown; + SetByCallerCooldown.DataTag = FGameplayTag::RequestGameplayTag(FName("Data.Skill.Cooldown")); + DurationMagnitude = SetByCallerCooldown; +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.h b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.h new file mode 100644 index 00000000..571871b4 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCooldown.h @@ -0,0 +1,26 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffect.h" +#include "MJGE_SkillCooldown.generated.h" + +/** + * Class Description: 커스텀 쿨타임 이펙트 + * Author: 신동민 + * Created Date: 2025.07.23 + * Description of Change: + * Modified By: 신동민 + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGE_SkillCooldown : public UGameplayEffect +{ + GENERATED_BODY() + +public: + UMJGE_SkillCooldown(); + + +}; diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.cpp b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.cpp new file mode 100644 index 00000000..416cb6d7 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.cpp @@ -0,0 +1,38 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/Effect/MJGE_SkillCost.h" + +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + +UMJGE_SkillCost::UMJGE_SkillCost() +{ + DurationPolicy = EGameplayEffectDurationType::Instant; + + // TODO: 결국 스킬 유형마다로 변경해야할 듯 + // 지금은 그냥 안 쓰는 자원을 0 으로 받는 중 + + FGameplayModifierInfo StaminaModifier; + StaminaModifier.Attribute = UMJCharacterAttributeSet::GetStaminaAttribute(); + StaminaModifier.ModifierOp = EGameplayModOp::Additive; + FSetByCallerFloat SetByCallerStamina; + SetByCallerStamina.DataTag = FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostStamina")); + StaminaModifier.ModifierMagnitude = SetByCallerStamina; + Modifiers.Add(StaminaModifier); + + FGameplayModifierInfo ManaModifier; + ManaModifier.Attribute = UMJCharacterAttributeSet::GetManaAttribute(); + ManaModifier.ModifierOp = EGameplayModOp::Additive; + FSetByCallerFloat SetByCallerMana; + SetByCallerMana.DataTag = FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostMana")); + ManaModifier.ModifierMagnitude = SetByCallerMana; + Modifiers.Add(ManaModifier); + + FGameplayModifierInfo FocusModifier; + FocusModifier.Attribute = UMJCharacterAttributeSet::GetFocusAttribute(); + FocusModifier.ModifierOp = EGameplayModOp::Additive; + FSetByCallerFloat SetByCallerFocus; + SetByCallerFocus.DataTag = FGameplayTag::RequestGameplayTag(FName("Data.Skill.CostFocus")); + FocusModifier.ModifierMagnitude = SetByCallerFocus; + Modifiers.Add(FocusModifier); +} diff --git a/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.h b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.h new file mode 100644 index 00000000..f50b6688 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Effect/MJGE_SkillCost.h @@ -0,0 +1,25 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayEffect.h" +#include "MJGE_SkillCost.generated.h" + +/** + * Class Description: 스킬의 자원 소비 + * Author: 신동민 + * Created Date: 2025.07.18 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJGE_SkillCost : public UGameplayEffect +{ + GENERATED_BODY() + +public: + UMJGE_SkillCost(); + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.cpp b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.cpp new file mode 100644 index 00000000..64d7c951 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.cpp @@ -0,0 +1,82 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/GameplayCue/MJGC_DurationEffect.h" + +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "Character/MJCharacterBase.h" + +AMJGC_DurationEffect::AMJGC_DurationEffect() +{ + PrimaryActorTick.bCanEverTick = false; +} + +void AMJGC_DurationEffect::BeginPlay() +{ + Super::BeginPlay(); +} + +bool AMJGC_DurationEffect::OnActive_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) +{ + if (!MyTarget) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist Target")); + return false; + } + + if (!ActiveNiagaraSystem) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ActiveNiagaraSystem")); + return false; + } + + USceneComponent* AttachComponent = MyTarget->GetRootComponent(); + AMJCharacterBase* Character = Cast(MyTarget); + + if (Character && Character->GetMesh()) + { + AttachComponent = Character->GetMesh(); + } + + if (!AttachComponent) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist AttachComponent")); + return false; + } + + ActiveNiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAttached( + ActiveNiagaraSystem, + AttachComponent, + ActiveNiagaraSystemSocketName, + FVector::ZeroVector, + FRotator::ZeroRotator, + EAttachLocation::Type::SnapToTarget, + true, + true, + ENCPoolMethod::None, + true + ); + + return true; +} + +bool AMJGC_DurationEffect::OnRemove_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) +{ + if (!ActiveNiagaraComponent) + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist ActiveNiagaraComponent")); + return false; + } + ActiveNiagaraComponent->Deactivate(); + ActiveNiagaraComponent->DestroyComponent(); + + return true; +} + +bool AMJGC_DurationEffect::WhileActive_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) +{ + + return true; +} diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.h b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.h new file mode 100644 index 00000000..eac5ba2c --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_DurationEffect.h @@ -0,0 +1,50 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayCueNotify_Actor.h" +#include "MJGC_DurationEffect.generated.h" + +/** + * Class Description: 지속적인 효과를 주는 Effect에 부여하는 GameplayCue + * Author: 신동민 + * Created Date: 2025.07.28 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UNiagaraComponent; +class UNiagaraSystem; + +UCLASS() +class PROJECTMJ_API AMJGC_DurationEffect : public AGameplayCueNotify_Actor +{ + GENERATED_BODY() + +public: + AMJGC_DurationEffect(); + +protected: + virtual void BeginPlay() override; + + virtual bool OnActive_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) override; + virtual bool OnRemove_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) override; + virtual bool WhileActive_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + TObjectPtr ActiveNiagaraSystem; + + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + FName ActiveNiagaraSystemSocketName; + + UPROPERTY() + TObjectPtr ActiveNiagaraComponent; + + // TODO: SFX + + +}; diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.cpp b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.cpp new file mode 100644 index 00000000..7f02347c --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.cpp @@ -0,0 +1,36 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/GameplayCue/MJGC_Hit.h" + +#include "NiagaraSystem.h" +#include "NiagaraFunctionLibrary.h" + +UMJGC_Hit::UMJGC_Hit() +{ +} + +bool UMJGC_Hit::OnExecute_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) const +{ + if (!MyTarget) + { + return false; + } + const FHitResult* HitResult = Parameters.EffectContext.GetHitResult(); + if (HitResult) + { + UNiagaraFunctionLibrary::SpawnSystemAtLocation(MyTarget, NiagaraSystem, HitResult->ImpactPoint , FRotator::ZeroRotator, FVector(Scale), true, true, ENCPoolMethod::AutoRelease); + } + else + { + for (const auto& TargetActor : Parameters.EffectContext.Get()->GetActors()) + { + if (TargetActor.Get()) + { + UNiagaraFunctionLibrary::SpawnSystemAtLocation(MyTarget, NiagaraSystem, TargetActor.Get()->GetActorLocation(), FRotator::ZeroRotator, FVector(Scale), true, true, ENCPoolMethod::AutoRelease); + } + } + } + + return false; +} diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.h b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.h new file mode 100644 index 00000000..490f6c2a --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_Hit.h @@ -0,0 +1,33 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayCueNotify_Static.h" +#include "MJGC_Hit.generated.h" + +class UNiagaraSystem; +/** + * Class Description: Has Hit VFX Gameplay Cue + * Author: 신동민 + * Created Date: 2025_06_18 + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ + +UCLASS() +class PROJECTMJ_API UMJGC_Hit : public UGameplayCueNotify_Static +{ + GENERATED_BODY() +public: + UMJGC_Hit(); + + virtual bool OnExecute_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) const override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + TObjectPtr NiagaraSystem; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + float Scale = 1.0f; +}; diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.cpp b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.cpp new file mode 100644 index 00000000..d97b644e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.cpp @@ -0,0 +1,54 @@ +// ThenOneDayStudio + + +#include "AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.h" + +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" + +UMJGC_ProjectileExplosion::UMJGC_ProjectileExplosion() +{ +} + +bool UMJGC_ProjectileExplosion::OnExecute_Implementation(AActor* MyTarget, + const FGameplayCueParameters& Parameters) const +{ + FVector SpawnLocation; + float ExplosionRadius = Parameters.RawMagnitude; + + if (!Parameters.Location.IsNearlyZero()) + { + SpawnLocation = Parameters.Location; + } + else if (MyTarget) + { + SpawnLocation = MyTarget->GetActorLocation(); + } + else + { + MJ_LOG(LogMJ, Warning, TEXT("Not Exist MyTarget")); + } + + if (ExplosionVFX) + { + if (MyTarget) + { + UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation( + MyTarget->GetWorld(), + ExplosionVFX, + SpawnLocation, + FRotator::ZeroRotator, + FVector(1.0f)); + + { + float NiagaraScale = ExplosionRadius / EffectRatio; + NiagaraComponent->SetFloatParameter(TEXT("Scale_All"), NiagaraScale); + } + } + } + + // TODO:SFX + return true; +} diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.h b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.h new file mode 100644 index 00000000..4560241e --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileExplosion.h @@ -0,0 +1,38 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayCueNotify_Static.h" +#include "MJGC_ProjectileExplosion.generated.h" + +/** + * Class Description: 투사체 직선 이동 + * Author: 신동민 + * Created Date: 2025.07.31 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UNiagaraSystem; + +UCLASS() +class PROJECTMJ_API UMJGC_ProjectileExplosion : public UGameplayCueNotify_Static +{ + GENERATED_BODY() + +public: + UMJGC_ProjectileExplosion(); + + virtual bool OnExecute_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) const override; +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + TObjectPtr ExplosionVFX; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + float EffectRatio = 1.0f; + + //TODO: Sound + +}; diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.cpp b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.cpp new file mode 100644 index 00000000..e4c8b2ea --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.cpp @@ -0,0 +1,42 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/GameplayCue/MJGC_ProjectileHit.h" + +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" + +UMJGC_ProjectileHit::UMJGC_ProjectileHit() +{ +} + +bool UMJGC_ProjectileHit::OnExecute_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) const +{ + if (!MyTarget) + { + return false; + } + if (!NiagaraSystem) + { + return false; + } + + const FHitResult* HitResult = Parameters.EffectContext.GetHitResult(); + + if (HitResult) + { + UNiagaraFunctionLibrary::SpawnSystemAtLocation(MyTarget, NiagaraSystem, HitResult->GetActor()->GetActorLocation(), FRotator::ZeroRotator, FVector(Scale), true, true, ENCPoolMethod::AutoRelease); + } + else + { + for (const auto& TargetActor : Parameters.EffectContext.Get()->GetActors()) + { + if (TargetActor.Get()) + { + UNiagaraFunctionLibrary::SpawnSystemAtLocation(MyTarget, NiagaraSystem, TargetActor.Get()->GetActorLocation(), FRotator::ZeroRotator, FVector(Scale), true, true, ENCPoolMethod::AutoRelease); + } + } + } + + return false; +} diff --git a/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.h b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.h new file mode 100644 index 00000000..cbb9e483 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/GameplayCue/MJGC_ProjectileHit.h @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayCueNotify_Static.h" +#include "MJGC_ProjectileHit.generated.h" + +/** + * Class Description: Has Projectile Hit VFX Gameplay Cue + * Author: 신동민 + * Created Date: 2025.07.09 + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ + +class UNiagaraSystem; + +UCLASS() +class PROJECTMJ_API UMJGC_ProjectileHit : public UGameplayCueNotify_Static +{ + GENERATED_BODY() + +public: + UMJGC_ProjectileHit(); + + virtual bool OnExecute_Implementation(AActor* MyTarget, const FGameplayCueParameters& Parameters) const override; + +protected: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + TObjectPtr NiagaraSystem; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayCue") + float Scale = 1.0f; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/MJAbilitySystemComponent.cpp b/Source/ProjectMJ/AbilitySystem/MJAbilitySystemComponent.cpp index 689b7b65..f6790c70 100644 --- a/Source/ProjectMJ/AbilitySystem/MJAbilitySystemComponent.cpp +++ b/Source/ProjectMJ/AbilitySystem/MJAbilitySystemComponent.cpp @@ -4,7 +4,7 @@ #include "AbilitySystem/MJAbilitySystemComponent.h" #include "AbilitySystem/Abilities/MJGA_GameplayAbility.h" #include "Character/MJPlayerCharacter.h" -#include "Character/Component/MJSkillComponent.h" +// #include "Character/Component/MJSkillComponentBase.h" //This is a function that detects key presses in GAS. @@ -38,48 +38,6 @@ void UMJAbilitySystemComponent::OnAbilityInputPressed(const FGameplayTag& InInpu return; } - - //for ( FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) - //{ - // if (!AbilitySpec.DynamicAbilityTags.HasTagExact(InInputTag)) - // { - // UE_LOG(LogTemp, Warning, TEXT("Found match via AbilityTags: %s"), *InInputTag.ToString()); - // TryActivateAbility(AbilitySpec.Handle); - // break; - // } - // AbilitySpecInputPressed(AbilitySpec); - // TryActivateAbility(AbilitySpec.Handle); - //} - // - //for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) - //{ - // if (!AbilitySpec.DynamicAbilityTags.HasTagExact(InInputTag)) - // { - // continue; - // } - // TryActivateAbility(AbilitySpec.Handle); - //} - - - //for (const FGameplayAbilitySpec& Spec : GetActivatableAbilities()) - //{ - // const FString SpecName = Spec.Ability ? Spec.Ability->GetName() : TEXT("None"); - // UE_LOG(LogTemp, Warning, TEXT("Checking Spec: %s"), *SpecName); - - // if (Spec.DynamicAbilityTags.HasTagExact(InInputTag)) - // { - // UE_LOG(LogTemp, Warning, TEXT("Found match via DynamicTag: %s"), *InInputTag.ToString()); - // TryActivateAbility(Spec.Handle); - // break; - // } - // else if (Spec.Ability && Spec.Ability->AbilityTags.HasTagExact(InInputTag)) - // { - // UE_LOG(LogTemp, Warning, TEXT("Found match via AbilityTags: %s"), *InInputTag.ToString()); - // TryActivateAbility(Spec.Handle); - // break; - // } - //} - } void UMJAbilitySystemComponent::OnAbilityInputReleased(const FGameplayTag& InInputTag) diff --git a/Source/ProjectMJ/AbilitySystem/Struct/MJSkillProjectileParams.h b/Source/ProjectMJ/AbilitySystem/Struct/MJSkillProjectileParams.h new file mode 100644 index 00000000..dc38d4ae --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/Struct/MJSkillProjectileParams.h @@ -0,0 +1,115 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MJSkillProjectileParams.generated.h" + +/** +* Class Description : Projectile Param +* Author : 신동민 +* Created Date : 2025.07.31 +* Last Modified By : +* Last Modified Date : +*/ + +class UAbilitySystemComponent; +class UGameplayEffect; + +USTRUCT(BlueprintType) +struct FMJSkillProjectileParams +{ + GENERATED_BODY() + + FMJSkillProjectileParams() {}; + // Target + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FVector TargetLocation = FVector::ZeroVector; + + // Damage/Scaling + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BaseDamage = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float AttackDamageScaling = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float AbilityPowerScaling = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float LifeSteal = 0.0f; + + // Range + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float SkillRadius = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float SkillRange = 0.0f; + + // Status Effect + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectChance = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectDuration = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectBaseDamage = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectADScaling = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectAPScaling = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectMaxStack = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectPeriod = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float StatusEffectSlowPercent = 0.f; + + // Explosion + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ExplosionRadius = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ExplosionBaseDamage = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ExplosionADScaling = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ExplosionAPScaling = 0.f; + + // Projectile + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ProjectileSpeed = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float ProjectileCount = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float LifeSpan = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 PierceCount = 0; + + // ASC + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr SourceASC = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr TargetASC = nullptr; + + // Effects + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf DamageGameplayEffectClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf StatusGameplayEffectClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ExplosionDamageGameplayEffectClass; + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.cpp b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.cpp new file mode 100644 index 00000000..5093e2fd --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.cpp @@ -0,0 +1,86 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/TargetActor/MJTA_CapsuleTrace.h" + +#include "AbilitySystemBlueprintLibrary.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "Components/CapsuleComponent.h" +#include "GameFramework/Character.h" +#include "Physics/MJCollision.h" +#include "Engine/OverlapResult.h" + + +AMJTA_CapsuleTrace::AMJTA_CapsuleTrace() +{ +} + +FGameplayAbilityTargetDataHandle AMJTA_CapsuleTrace::MakeTargetData() const +{ + ACharacter* Character = CastChecked(SourceActor); + + UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(SourceActor); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("ASC not found!")); + return FGameplayAbilityTargetDataHandle(); + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Error, TEXT("SkillAttributeSet not found!")); + return FGameplayAbilityTargetDataHandle(); + } + + const float AttackRadius = SkillAttributeSet->GetSkillRadius(); + const float AttackRange = FMath::Max(SkillAttributeSet->GetSkillRange(), AttackRadius * 2.0f); + const float Offset = SkillAttributeSet->GetSkillAttackLocationOffset(); + + const FVector OriginLocation = Character->GetActorLocation(); + const FVector Forward = Character->GetActorForwardVector(); + + FCollisionQueryParams Params(SCENE_QUERY_STAT(UMJTA_Trace), false, Character); + + const FVector Start = OriginLocation + Forward * Character->GetCapsuleComponent()->GetScaledCapsuleRadius() * ((Offset != 0) ? Offset : 1); + const FVector End = Start + Forward * AttackRange; + const FVector CapsuleOrigin = Start + (End - Start) * 0.5f; + + float CapsuleHalfHeight = AttackRange * 0.5f; + + TArray OutHitResults; + GetWorld()->OverlapMultiByChannel(OutHitResults, CapsuleOrigin, FRotationMatrix::MakeFromZ(Forward).ToQuat(), CCHANNEL_MJAbilityTargetTrace, FCollisionShape::MakeCapsule(AttackRadius, CapsuleHalfHeight), Params); + + /* + * Minjin + * HowTo: 적끼리 공격하지 않는다. + * FGameplayTargetDataFilter 생성, RequiredActorClass로 SourceActor의 C++ 클래스를 설정한다. + * -> 이러면 FilterPassesForActor 중 !ActorToBeFiltered(HitActor를 뜻함)->IsA(RequiredActorClass)에 걸려 false를 리턴: Target 대상에서 제외됨 + * => bReverseFilter를 true로 한다... + */ + // Minjin: 다른 방법이 있다면 알려주세요.... + + TSubclassOf ActorClass = GetParentNativeClass(SourceActor.GetClass()); + + FGameplayTargetDataFilter FilteringData; + FilteringData.RequiredActorClass = ActorClass; + FilteringData.bReverseFilter = true; + FilteringData.InitializeFilterContext(SourceActor); + + TArray> HitActors; + for (const FOverlapResult& OutHitResult : OutHitResults) + { + AActor* HitActor = OutHitResult.GetActor(); + + // Minjin: Filter를 통과하면(공격대상) HitActor에 추가 + if (HitActor && !HitActors.Contains(HitActor) && (FilteringData.FilterPassesForActor(HitActor))) + { + HitActors.Add(HitActor); + } + } + FGameplayAbilityTargetData_ActorArray* ActorData = new FGameplayAbilityTargetData_ActorArray(); + ActorData->SetActors(HitActors); + + return FGameplayAbilityTargetDataHandle(ActorData); +} \ No newline at end of file diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.h b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.h new file mode 100644 index 00000000..715a7863 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_CapsuleTrace.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/TargetActor/MJTA_Trace.h" +#include "MJTA_CapsuleTrace.generated.h" + +/** + * Class Description: 캡슐 모양 Trace + * Author: 신동민 + * Created Date: 2025.07.18 + * Description of Change: 같은 클래스끼리 공격 못하도록 수정 + * Modified By: 김민진 + * Modified Date: 2025.07.27. + * Description of Change: Trace 위치 오프셋 추가 + * Modified By: 김민진 + * Modified Date: 2025.08.05. + */ +UCLASS() +class PROJECTMJ_API AMJTA_CapsuleTrace : public AMJTA_Trace +{ + GENERATED_BODY() + +public: + AMJTA_CapsuleTrace(); +protected: + virtual FGameplayAbilityTargetDataHandle MakeTargetData() const override; +}; diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.cpp b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.cpp new file mode 100644 index 00000000..d257ca19 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.cpp @@ -0,0 +1,77 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "AbilitySystem/TargetActor/MJTA_SphereTrace.h" + +#include "AbilitySystemBlueprintLibrary.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "Engine/OverlapResult.h" +#include "GameFramework/Character.h" +#include "Physics/MJCollision.h" + +AMJTA_SphereTrace::AMJTA_SphereTrace() +{ +} + +FGameplayAbilityTargetDataHandle AMJTA_SphereTrace::MakeTargetData() const +{ + ACharacter* Character = CastChecked(SourceActor); + + UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(SourceActor); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("ASC not found!")); + return FGameplayAbilityTargetDataHandle(); + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Error, TEXT("SkillAttributeSet not found!")); + return FGameplayAbilityTargetDataHandle(); + } + + TArray Overlaps; + + const float AttackRadius = SkillAttributeSet->GetSkillRadius(); + + FVector OriginLocation = Character->GetActorLocation(); + const FVector Forward = Character->GetActorForwardVector(); + const float Offset = SkillAttributeSet->GetSkillAttackLocationOffset(); + + FVector Center = OriginLocation + Forward * ((Offset!=0)?Offset:1); + FCollisionQueryParams Params(SCENE_QUERY_STAT(UMJTA_Trace), false, Character); + GetWorld()->OverlapMultiByChannel(Overlaps, Center, FQuat::Identity, CCHANNEL_MJAbilityTargetTrace, FCollisionShape::MakeSphere(AttackRadius), Params); + + /* + * Minjin + * HowTo: 적끼리 공격하지 않는다. + * FGameplayTargetDataFilter 생성, RequiredActorClass로 SourceActor의 C++ 클래스를 설정한다. + * -> 이러면 FilterPassesForActor 중 !ActorToBeFiltered(HitActor를 뜻함)->IsA(RequiredActorClass)에 걸려 false를 리턴: Target 대상에서 제외됨 + * => bReverseFilter를 true로 한다... + */ + // Minjin: 다른 방법이 있다면 알려주세요.... + + TSubclassOf ActorClass = GetParentNativeClass(SourceActor.GetClass()); + + FGameplayTargetDataFilter FilteringData; + FilteringData.RequiredActorClass = ActorClass; + FilteringData.bReverseFilter = true; + FilteringData.InitializeFilterContext(SourceActor); + + TArray> HitActors; + for (const FOverlapResult& Overlap : Overlaps) + { + AActor* HitActor = Overlap.OverlapObjectHandle.FetchActor(); + if (HitActor && !HitActors.Contains(HitActor)&& (FilteringData.FilterPassesForActor(HitActor))) + { + HitActors.Add(HitActor); + } + } + FGameplayAbilityTargetData_ActorArray* ActorData = new FGameplayAbilityTargetData_ActorArray(); + ActorData->SetActors(HitActors); + + return FGameplayAbilityTargetDataHandle(ActorData); +} + diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.h b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.h new file mode 100644 index 00000000..293ac8d5 --- /dev/null +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_SphereTrace.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystem/TargetActor/MJTA_Trace.h" +#include "MJTA_SphereTrace.generated.h" + +/** + * Class Description: 구 모양 Trace + * Author: 신동민 + * Created Date: 2025.07.18 + * Description of Change: 같은 클래스끼리 공격 못하도록 수정 + * Modified By: 김민진 + * Modified Date: 2025.07.27. + * Description of Change: Trace 위치 오프셋 추가 + * Modified By: 김민진 + * Modified Date: 2025.08.05. + */ +UCLASS() +class PROJECTMJ_API AMJTA_SphereTrace : public AMJTA_Trace +{ + GENERATED_BODY() +public: + AMJTA_SphereTrace(); +protected: + virtual FGameplayAbilityTargetDataHandle MakeTargetData() const override; + +}; diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.cpp b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.cpp index 6cbb38f8..2feaa0b8 100644 --- a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.cpp +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.cpp @@ -4,10 +4,6 @@ #include "AbilitySystem/TargetActor/MJTA_Trace.h" #include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" #include "AbilitySystemBlueprintLibrary.h" -#include "ProjectMJ.h" -#include "Components/CapsuleComponent.h" -#include "GameFramework/Character.h" -#include "Physics/MJCollision.h" AMJTA_Trace::AMJTA_Trace() { @@ -31,52 +27,8 @@ void AMJTA_Trace::ConfirmTargetingAndContinue() FGameplayAbilityTargetDataHandle AMJTA_Trace::MakeTargetData() const { - ACharacter* Character = CastChecked(SourceActor); - - UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(SourceActor); - if (!ASC) - { - MJ_LOG(LogMJ, Error, TEXT("ASC not found!")); - return FGameplayAbilityTargetDataHandle(); - } - - const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); - if (!SkillAttributeSet) - { - MJ_LOG(LogMJ, Error, TEXT("SkillAttributeSet not found!")); - return FGameplayAbilityTargetDataHandle(); - } - - FHitResult OutHitResult; - const float AttackRange = SkillAttributeSet->GetSkillRange(); - const float AttackRadius = SkillAttributeSet->GetSkillRadius(); - - FCollisionQueryParams Params(SCENE_QUERY_STAT(UMJTA_Trace), false, Character); - const FVector Forward = Character->GetActorForwardVector(); - const FVector Start = Character->GetActorLocation() + Forward * Character->GetCapsuleComponent()->GetScaledCapsuleRadius(); - const FVector End = Start + Forward * AttackRange; - - bool HitDetected = GetWorld()->SweepSingleByChannel(OutHitResult, Start, End, FQuat::Identity, CCHANNEL_MJAbilityTargetTrace, FCollisionShape::MakeSphere(AttackRadius), Params); - - FGameplayAbilityTargetDataHandle DataHandle; - if (HitDetected) - { - FGameplayAbilityTargetData_SingleTargetHit* TargetData = new FGameplayAbilityTargetData_SingleTargetHit(OutHitResult); - DataHandle.Add(TargetData); - } - -#if ENABLE_DRAW_DEBUG - - if (bShowDebug) - { - FVector CapsuleOrigin = Start + (End - Start) * 0.5f; - float CapsuleHalfHeight = AttackRange * 0.5f; - FColor DrawColor = HitDetected ? FColor::Green : FColor::Red; - DrawDebugCapsule(GetWorld(), CapsuleOrigin, CapsuleHalfHeight, AttackRadius, FRotationMatrix::MakeFromZ(Forward).ToQuat(), DrawColor, false, 5.0f); - } - -#endif - - return DataHandle; + // 언리얼의 UCLASS는 순수 가상함수가 되지 않아서 원형으로 못 쓰게했음 + // 오버라이드 해라 + checkNoEntry(); + return FGameplayAbilityTargetDataHandle(); } - diff --git a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.h b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.h index 0c8d1e3b..77529a48 100644 --- a/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.h +++ b/Source/ProjectMJ/AbilitySystem/TargetActor/MJTA_Trace.h @@ -9,9 +9,10 @@ /** * Class Description: 충돌을 확인하는 Target Actor * Author: 신동민 - * Created Date: 2025_06_18 - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) + * Created Date: 2025.06.18 + * Description of Change: 추상화 후 자식에서 구체화 하도록 구조 변경 + * Modified By: 신동민 + * Modified Date: 2025.07.18 */ UCLASS() class PROJECTMJ_API AMJTA_Trace : public AGameplayAbilityTargetActor diff --git a/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.cpp b/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.cpp index 1f188907..9f714282 100644 --- a/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.cpp +++ b/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.cpp @@ -1,21 +1,22 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #include "AnimInstance/MJAnimInstanceBase.h" #include "MJAnimInstanceBase.h" -#include "Character/MJPlayerCharacter.h" +#include "Character/MJCharacterBase.h" #include "GameFramework/CharacterMovementComponent.h" void UMJAnimInstanceBase::NativeInitializeAnimation() { - OwningCharacter = Cast(TryGetPawnOwner()); + OwningCharacter = Cast(TryGetPawnOwner()); if (OwningCharacter) { OwningCharacterMovementComponent = OwningCharacter->GetCharacterMovement(); } + bIsOpen = false; } void UMJAnimInstanceBase::NativeThreadSafeUpdateAnimation(float DeltaSeconds) @@ -30,4 +31,17 @@ void UMJAnimInstanceBase::NativeThreadSafeUpdateAnimation(float DeltaSeconds) { bHasAcceleration = true; } + else + { + bHasAcceleration = false; + } + + const float BaseRunSpeed = 600.f; // 기본 속도 + + MoveAnimPlayRate = FMath::Max(GroundSpeed / BaseRunSpeed, 0.2f); +} + +void UMJAnimInstanceBase::SetOpen(bool Value) +{ + SetbIsOpen(Value); } diff --git a/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.h b/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.h index d652aed4..89b0f8d8 100644 --- a/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.h +++ b/Source/ProjectMJ/AnimInstance/MJAnimInstanceBase.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #pragma once @@ -6,14 +6,14 @@ #include "Animation/AnimInstance.h" #include "MJAnimInstanceBase.generated.h" -class AMJPlayerCharacter; +class AMJCharacterBase; class UCharacterMovementComponent; /** * Class Description: * Author: Lee JuHyeon * Created Date: 2025_06_12 - * Last Modified By: Lee JuHyeon - * Last Modified Date: Add Class + * Last Modified By: Kim Minjin + * Last Modified Date: (25.07.10.)Change Cast */ UCLASS() class PROJECTMJ_API UMJAnimInstanceBase : public UAnimInstance @@ -25,10 +25,13 @@ class PROJECTMJ_API UMJAnimInstanceBase : public UAnimInstance virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds)override; + FORCEINLINE void SetbIsOpen(bool Value) { bIsOpen = Value; } + UFUNCTION(BlueprintCallable) + void SetOpen(bool Value); public: UPROPERTY(VisibleDefaultsOnly,BlueprintReadOnly,Category="AnimData|LocomotionData") - TObjectPtr OwningCharacter; + TObjectPtr OwningCharacter; UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "AnimData|LocomotionData") TObjectPtr OwningCharacterMovementComponent; @@ -39,4 +42,10 @@ class PROJECTMJ_API UMJAnimInstanceBase : public UAnimInstance UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Animation|LocomotionData") bool bHasAcceleration; + + UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Animation|LocomotionData") + bool bIsOpen; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AnimData|LocomotionData") + float MoveAnimPlayRate = 1.0f; }; diff --git a/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.cpp b/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.cpp new file mode 100644 index 00000000..afc7f5e5 --- /dev/null +++ b/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.cpp @@ -0,0 +1,51 @@ +// ThenOneDayStudio + + +#include "AnimInstance/MJEnemyAnimInstance.h" + +#include "GameplayTagContainer.h" +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "Character/MJCharacterBase.h" + +UMJEnemyAnimInstance::UMJEnemyAnimInstance() +{ +} + +void UMJEnemyAnimInstance::NativeInitializeAnimation() +{ + Super::NativeInitializeAnimation(); + + AActor* OwningActor = GetOwningActor(); + AMJCharacterBase* Character = Cast(OwningActor); + if (Character == nullptr) + { + MJ_LOG(LogMJ, Warning, TEXT("Character nullptr.")); + return; + } + + // Minjin: IsInActivated 태그를 처음부터 부여 - 비활성화로 만듦 + FGameplayTag InActivatedTag = FGameplayTag::RequestGameplayTag(TEXT("Character.State.IsInactivated")); + + Character->ASC->AddLooseGameplayTag(InActivatedTag); + bIsInActivated = true; + + Character->ASC->RegisterGameplayTagEvent(InActivatedTag, EGameplayTagEventType::NewOrRemoved).AddUObject(this, &UMJEnemyAnimInstance::TagConditionChanged); +} + +void UMJEnemyAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds) +{ + Super::NativeThreadSafeUpdateAnimation(DeltaSeconds); +} + +void UMJEnemyAnimInstance::TagConditionChanged(const FGameplayTag InTag, int32 NewCount) +{ + if (NewCount > 0) + { + bIsInActivated = true; + } + else + { + bIsInActivated = false; + } +} diff --git a/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.h b/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.h new file mode 100644 index 00000000..6e39d424 --- /dev/null +++ b/Source/ProjectMJ/AnimInstance/MJEnemyAnimInstance.h @@ -0,0 +1,31 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "AnimInstance/MJAnimInstanceBase.h" +#include "MJEnemyAnimInstance.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJEnemyAnimInstance : public UMJAnimInstanceBase +{ + GENERATED_BODY() + +public: + UMJEnemyAnimInstance(); + + virtual void NativeInitializeAnimation() override; + + virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds)override; + +protected: + UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Animation|LocomotionData") + bool bIsInActivated; + + UFUNCTION() + void TagConditionChanged(const FGameplayTag InTag, int32 NewCount); +}; diff --git a/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.cpp b/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.cpp new file mode 100644 index 00000000..7268d10a --- /dev/null +++ b/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.cpp @@ -0,0 +1,20 @@ +// ThenOneDayStudio + + +#include "AnimInstance/Notify/MJAnimNotify_AnimSpeed.h" +#include "Components/SkeletalMeshComponent.h" + +void UMJAnimNotify_AnimSpeed::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) +{ + Super::Notify(MeshComp, Animation); + + UAnimInstance* AnimInstance = MeshComp->GetAnimInstance(); + if (!AnimInstance) + { + return; + } + AnimInstance->Montage_SetPlayRate( + AnimInstance->GetCurrentActiveMontage(), + NewPlayRate + ); +} diff --git a/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.h b/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.h new file mode 100644 index 00000000..d71ba624 --- /dev/null +++ b/Source/ProjectMJ/AnimInstance/Notify/MJAnimNotify_AnimSpeed.h @@ -0,0 +1,26 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimNotifies/AnimNotify.h" +#include "MJAnimNotify_AnimSpeed.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJAnimNotify_AnimSpeed : public UAnimNotify +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, Category="Rate") + float NewPlayRate; + + UFUNCTION() + virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override; + + +}; diff --git a/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.cpp b/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.cpp new file mode 100644 index 00000000..6cec13a0 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.cpp @@ -0,0 +1,9 @@ +// ThenOneDayStudio + + +#include "Character/Component/MJAbilityContextComponent.h" + +UMJAbilityContextComponent::UMJAbilityContextComponent() +{ + PrimaryComponentTick.bCanEverTick = false; +} \ No newline at end of file diff --git a/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.h b/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.h new file mode 100644 index 00000000..dbbda908 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJAbilityContextComponent.h @@ -0,0 +1,34 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "MJAbilityContextComponent.generated.h" + +/** + * Class Description: 어빌리이 발동에 필요한 데이터를 캐싱(이라 하고 계속 가지고 있는)하는 컴포넌트 + * Author: 신동민 + * Created Date: 2025.08.01 + * Description of Change: + * Modified By: + * Modified Date: + */ + +UCLASS( ClassGroup=(AbilityContext), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJAbilityContextComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UMJAbilityContextComponent(); + +protected: + +private: + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilityContext") + FVector LastTargetedMouseLocation; + +}; diff --git a/Source/ProjectMJ/Character/Component/MJEffectComponentBase.cpp b/Source/ProjectMJ/Character/Component/MJEffectComponentBase.cpp new file mode 100644 index 00000000..372892e6 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEffectComponentBase.cpp @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJEffectComponentBase.h" + +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Character/MJCharacterBase.h" + +// TODO: SFX 재생 + +UMJEffectComponentBase::UMJEffectComponentBase() +{ + PrimaryComponentTick.bCanEverTick = false; +} + +void UMJEffectComponentBase::BeginPlay() +{ + Super::BeginPlay(); + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + +} \ No newline at end of file diff --git a/Source/ProjectMJ/Character/Component/MJEffectComponentBase.h b/Source/ProjectMJ/Character/Component/MJEffectComponentBase.h new file mode 100644 index 00000000..41283bee --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEffectComponentBase.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Components/ActorComponent.h" +#include "MJEffectComponentBase.generated.h" + +/** + * Class Description: 캐릭터로 부터 나오는 VFX/SFX 를 재생하는 컴포넌트 + * Author: 신동민 + * Created Date: 2025.07.21 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UNiagaraComponent; +class UNiagaraSystem; + +UCLASS( ClassGroup=("VFX/SFX"), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJEffectComponentBase : public UActorComponent +{ + GENERATED_BODY() + +public: + UMJEffectComponentBase(); + +protected: + virtual void BeginPlay() override; + +protected: + +}; diff --git a/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.cpp b/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.cpp new file mode 100644 index 00000000..077d42d5 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.cpp @@ -0,0 +1,110 @@ +// ThenOneDayStudio + + +#include "Character/Component/MJEnemySkillComponent.h" + +#include "ProjectMJ.h" +#include "DataTable/MJEnemyDataRow.h" +#include "Kismet/KismetMathLibrary.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "TG/MJGameInstance.h" + +UMJEnemySkillComponent::UMJEnemySkillComponent() +{ + +} + +void UMJEnemySkillComponent::InitializeComponent() +{ + Super::InitializeComponent(); + + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->EnemyDataTable) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist GI or EnemyDataTable")); + return; + } + + AMJMonsterCharacter* Enemy = Cast(GetOwner()); + if (Enemy == nullptr) + { + MJ_LOG(LogMJ, Error, TEXT("Owner is Not Exist")); + return; + } + + const FMJEnemyDataRow* DataRow = GI->EnemyDataTable->FindRow(Enemy->GetDefaultEnemyTag().GetTagName(), TEXT("Find EnemyData")); + if (!DataRow) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist DataRow")); + return; + } + + /* + * Minjin + * TODO + * 몬스터의 레벨에 따라 스킬 레벨도 다르게 적용하기?-색깔 다르게 할까ㅎㅎ + */ + // Minjin: 기본 공격 + LearnSkill(DataRow->NormalAttackTag); + EquipSkill(DataRow->NormalAttackTag); + + // Minjin: 특수 공격 -> 드랍하는 스킬 + LearnSkill(DataRow->IdentitySkillTag); + EquipSkill(DataRow->IdentitySkillTag); + + // Minjin: Activate 때 사용할 수 있도록 스킬 태그를 저장 + NormalSkillTag = DataRow->NormalAttackTag; + IdentitySkillTag = DataRow->IdentitySkillTag; + + // Minjin: 죽었을 때 스킬을 전달할 확률 + GiveChance = DataRow->GiveChance; +} + +void UMJEnemySkillComponent::BeginPlay() +{ + Super::BeginPlay(); +} + +UDataTable* UMJEnemySkillComponent::GetSkillDataTable() const +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist GI")); + return nullptr; + } + UDataTable* SkillDataTable = GI->EnemySkillDataTable; + if (!SkillDataTable) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillDataTable")); + return nullptr; + } + return SkillDataTable; +} + +void UMJEnemySkillComponent::ActivateNormalSkill() +{ + ActivateSkill(NormalSkillTag); +} + +void UMJEnemySkillComponent::ActivateIdentitySkill() +{ + ActivateSkill(IdentitySkillTag); +} + +FGameplayTag UMJEnemySkillComponent::TryGiveMemory() +{ + if(!IdentitySkillTag.IsValid()) + { + return FGameplayTag::EmptyTag; + } + + bool IsCanGive = UKismetMathLibrary::RandomBoolWithWeight(GiveChance); + + if (IsCanGive) + { + return IdentitySkillTag; + } + + return FGameplayTag::EmptyTag; +} diff --git a/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.h b/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.h new file mode 100644 index 00000000..b23cba93 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEnemySkillComponent.h @@ -0,0 +1,45 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "MJEnemySkillComponent.generated.h" + +/** + * Class Description: 몬스터의 스킬 컴포넌트 + * Author: 신동민 + * Created Date: 2025.07.30 + * Description of Change: Activate를 Normal, Identity로 나눔, 스킬 관련 초기화 + * Modified By: 김민진 + * Modified Date: 2025.08.01. + */ + +class UDataTable; + +UCLASS() +class PROJECTMJ_API UMJEnemySkillComponent : public UMJSkillComponentBase +{ + GENERATED_BODY() + +private: + FGameplayTag NormalSkillTag; + FGameplayTag IdentitySkillTag; + float GiveChance; + +public: + UMJEnemySkillComponent(); + + //virtual void PostInitProperties() override; + virtual void InitializeComponent() override; + + virtual void BeginPlay() override; + + virtual UDataTable* GetSkillDataTable() const override; + + void ActivateNormalSkill(); + void ActivateIdentitySkill(); + + // Minjin TODO: 확률에 따른 기억(스킬) 전달 + FGameplayTag TryGiveMemory(); +}; diff --git a/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.cpp b/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.cpp new file mode 100644 index 00000000..9c893dc0 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.cpp @@ -0,0 +1,68 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJEnemyStatComponent.h" + +#include "ProjectMJ.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h" +#include "DataTable/MJEnemyDataRow.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "TG/MJGameInstance.h" + + +UMJEnemyStatComponent::UMJEnemyStatComponent() +{ + /* + * Minjin + * TODO + * 우선은 1로 하는데, 캐릭터마다 다르게 설정해야 됨. 생각해보기 + */ + EnemyLevel = 1; +} + +void UMJEnemyStatComponent::InitializeStat() +{ + bIsInitializingStats = true; + + AMJMonsterCharacter* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Warning, TEXT("Not exist OwnerCharacter")); + return; + } + + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not exist ASC")); + return; + } + + TSubclassOf EffectClass = UMJGE_SetCharacterAttributeSet::StaticClass(); + FGameplayEffectContextHandle EffectContext = ASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(EffectClass, 1.f, EffectContext); + if (!SpecHandle.IsValid()) + { + return; + } + + for (const auto& CurveTableRow : EnemyStatTable->GetRowMap()) + { + const FName& RowName = CurveTableRow.Key; + FRealCurve* Curve = EnemyStatTable->FindCurve(RowName, TEXT("FindCurve")); + if (Curve) + { + float Value = Curve->Eval(EnemyLevel); + FString TagString = FString::Printf(TEXT("Data.Character.%s"), *RowName.ToString()); + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName(*TagString)), Value); + } + } + ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get()); + // OwnerCharacter->GetCharacterMovement()->MaxWalkSpeed = ASC->GetSet()->GetMaxMovementSpeed(); + + bIsInitializingStats = false; +} diff --git a/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.h b/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.h new file mode 100644 index 00000000..15e01625 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJEnemyStatComponent.h @@ -0,0 +1,34 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Character/Component/MJStatComponentBase.h" +#include "MJEnemyStatComponent.generated.h" + +/** + * Class Description: 몬스터는 GameInstance에 있는 DataTable에서 CurveTable을 바로 접근 + * Author: 신동민 + * Created Date: 2025.07.17 + * Modified By: Kim Minjin + * Modified Date: (2025.07.17.) InitializeStat 구현 + */ +UCLASS() +class PROJECTMJ_API UMJEnemyStatComponent : public UMJStatComponentBase +{ + GENERATED_BODY() + +public: + UMJEnemyStatComponent(); + + virtual void InitializeStat() override; + void SetStatTable(UCurveTable *AttributeCurve){EnemyStatTable = AttributeCurve;} + +protected: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat") + TObjectPtr EnemyStatTable; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat") + int32 EnemyLevel = 1; +}; diff --git a/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.cpp b/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.cpp new file mode 100644 index 00000000..f589a4f1 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.cpp @@ -0,0 +1,58 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJPlayerEffectComponent.h" + +#include "MJPlayerStatComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "Character/MJCharacterBase.h" + +UMJPlayerEffectComponent::UMJPlayerEffectComponent() +{ + +} + +void UMJPlayerEffectComponent::PlayLevelUpEffect() +{ + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + + if (LevelUpVFX) + { + UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), LevelUpVFX, OwnerCharacter->GetActorLocation()); + } + + if (LevelUpSFX) + { + // TODO: SFX 재생 + } +} + +void UMJPlayerEffectComponent::BeginPlay() +{ + Super::BeginPlay(); + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + + UMJPlayerStatComponent* StatComponent = OwnerCharacter->FindComponentByClass(); + if (StatComponent) + { + StatComponent->OnLevelUp.AddDynamic(this, &UMJPlayerEffectComponent::OnOwnerLevelUp); + } + +} + +void UMJPlayerEffectComponent::OnOwnerLevelUp(int32 NewLevel) +{ + PlayLevelUpEffect(); +} diff --git a/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.h b/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.h new file mode 100644 index 00000000..69bab77a --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerEffectComponent.h @@ -0,0 +1,43 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Character/Component/MJEffectComponentBase.h" +#include "MJPlayerEffectComponent.generated.h" + +/** + * Class Description: 캐릭터 레벨업 할 때, 이펙트 재생 + * Author: 신동민 + * Created Date: 2025.07.21 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UNiagaraSystem; + +UCLASS() +class PROJECTMJ_API UMJPlayerEffectComponent : public UMJEffectComponentBase +{ + GENERATED_BODY() +public: + UMJPlayerEffectComponent(); + + UFUNCTION(BlueprintCallable) + void PlayLevelUpEffect(); + +protected: + virtual void BeginPlay() override; + + UFUNCTION() + void OnOwnerLevelUp(int32 NewLevel); + +protected: + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "VFX|LevelUp") + TObjectPtr LevelUpVFX; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SFX|LevelUp") + TObjectPtr LevelUpSFX; +}; diff --git a/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.cpp b/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.cpp new file mode 100644 index 00000000..db49b613 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.cpp @@ -0,0 +1,82 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJPlayerSkillComponent.h" + +#include "ProjectMJ.h" +#include "DataTable/MJSkillDataRow.h" +#include "TG/MJGameInstance.h" + +UMJPlayerSkillComponent::UMJPlayerSkillComponent() +{ +} + +void UMJPlayerSkillComponent::BeginPlay() +{ + Super::BeginPlay(); + + // Beginplay 시마다 스킬들을 저장된 정보에 따라 다시 배우고 장착 + // 맨 처음 기본 스킬 -> MJNewGamePopUpWidget.cpp Line:78 참고 + OwnedSkillMap.Empty(); + for (auto Iter : LoadedOwnedSkillMap) + { + LearnSkill(Iter.Key); + } + + EquippedSkillMap.Empty(); + for (auto Iter : LoadedEquippedSkillMap) + { + EquipSkill(Iter.Value); + OnLearnSkillEvents.Broadcast(Iter.Value); + } + + + + // For Debug + if (EquippedSkillMap.IsEmpty()) {} + if (OwnedSkillMap.IsEmpty()){} +} + +UDataTable* UMJPlayerSkillComponent::GetSkillDataTable() const +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist GI")); + return nullptr; + } + UDataTable* SkillDataTable = GI->PlayerSkillDataTable; + if (!SkillDataTable) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillDataTable")); + return nullptr; + } + return SkillDataTable; +} + +void UMJPlayerSkillComponent::ActivateSkillByInputTag(const FGameplayTag InputTag) +{ + if (EquippedSkillMap.Contains(InputTag)) + { + FGameplayTag EquippedSkill = EquippedSkillMap[InputTag]; + ActivateSkill(EquippedSkill); + } + else + { + MJ_LOG(LogMJ, Warning, TEXT("Not Equipped Skill Tag")) + } +} + +void UMJPlayerSkillComponent::ReleaseSkillByInputTag(const FGameplayTag InputTag) +{ + if (EquippedSkillMap.Contains(InputTag)) + { + FGameplayTag EquippedSkill = EquippedSkillMap[InputTag]; + ReleaseSkill(EquippedSkill); + } + else + { + MJ_LOG(LogMJ, Warning, TEXT("Not Equipped Skill Tag")) + + } +} diff --git a/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.h b/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.h new file mode 100644 index 00000000..665050a1 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerSkillComponent.h @@ -0,0 +1,42 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "MJPlayerSkillComponent.generated.h" + +/** + * Class Description: 스킬 컴포넌트 + * - 플레이어가 사용할 컴포넌트 + * Author: 신동민 + * Created Date: 2025.07.08 + * Modified Description: InputTag 를 그냥 유형으로 받아오기 + * Modified By: 신동민 + * Modified Date: 2025.07.15 + * Modified Description: 스킬 데이터 테이블 분리 + * Modified By: 신동민 + * Modified Date: 2025.07.30 + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLearnSkillEvents, FGameplayTag, Tag); +class UDataTable; + +UCLASS() +class PROJECTMJ_API UMJPlayerSkillComponent : public UMJSkillComponentBase +{ + GENERATED_BODY() + +public: + UMJPlayerSkillComponent(); + + virtual void BeginPlay() override; + + virtual UDataTable* GetSkillDataTable() const override; + + void ActivateSkillByInputTag(const FGameplayTag InputTag); + + void ReleaseSkillByInputTag(const FGameplayTag InputTag); + + FOnLearnSkillEvents OnLearnSkillEvents; +}; diff --git a/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.cpp b/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.cpp new file mode 100644 index 00000000..21172ef5 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.cpp @@ -0,0 +1,155 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJPlayerStatComponent.h" + +#include "ProjectMJ.h" +#include "Character/MJCharacterBase.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Effect/MJGE_SetCharacterAttributeSet.h" +#include "GameFramework/CharacterMovementComponent.h" + +UMJPlayerStatComponent::UMJPlayerStatComponent() +{ + PlayerLevel = 1; + +} + +void UMJPlayerStatComponent::InitializeStat() +{ + // 다음 필요 경험치 계산 + bIsInitializingStats = true; + + ExperienceForNextLevel = GetTotalExperienceForLevel(PlayerLevel + 1); + + if (!PlayerStatTable) + { + MJ_LOG(LogMJ, Warning, TEXT("Not exist CurveTable")); + return; + } + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Warning, TEXT("Not exist OwnerCharacter")); + return; + } + + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Warning, TEXT("Not exist ASC")); + return; + } + + TSubclassOf EffectClass = UMJGE_SetCharacterAttributeSet::StaticClass(); + FGameplayEffectContextHandle EffectContext = ASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(EffectClass, 1.f, EffectContext); + if (!SpecHandle.IsValid()) + { + return; + } + + for (const auto& CurveTableRow : PlayerStatTable->GetRowMap()) + { + const FName& RowName = CurveTableRow.Key; + FRealCurve* Curve = PlayerStatTable->FindCurve(RowName, TEXT("FindCurve")); + if (Curve) + { + float Value = Curve->Eval(PlayerLevel); + FString TagString = FString::Printf(TEXT("Data.Character.%s"), *RowName.ToString()); + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName(*TagString)), Value); + } + } + ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get()); + // OwnerCharacter->GetCharacterMovement()->MaxWalkSpeed = ASC->GetSet()->GetMaxMovementSpeed(); + + bIsInitializingStats = false; +} + +void UMJPlayerStatComponent::GainExperience(int32 GainedExp) +{ + if (GainedExp <= 0) + { + MJ_LOG(LogMJ, Warning, TEXT("GainedExp is zero under")) + return; + } + + TotalCumulativeExperience += GainedExp; + CheckForLevelUp(); + OnExperienceChanged.Broadcast(GetNumerator() ,GetDenominator()); +} + +void UMJPlayerStatComponent::GainGold(int32 GainedGold) +{ + Gold += GainedGold; + OnGoldChange.Broadcast(Gold); +} + +void UMJPlayerStatComponent::SpendGold(int32 UsedGold) +{ + Gold -= UsedGold; + OnGoldChange.Broadcast(Gold); +} + +void UMJPlayerStatComponent::CheckForLevelUp() +{ + if (ExperienceForNextLevel <= 0) + { + MJ_LOG(LogMJ, Warning, TEXT("ExperienceForNextLevel is zero under")) + return; + } + + while(TotalCumulativeExperience >= ExperienceForNextLevel) + { + MJ_LOG(LogMJ, Warning, TEXT("Level Up")) + ++PlayerLevel; + + InitializeStat(); + + OnLevelUp.Broadcast(PlayerLevel); + + } + +} + +float UMJPlayerStatComponent::GetTotalExperienceForLevel(int32 Level) const +{ + // 데미지 공식 부분 + // 현재는 n^3 + float TotalExp = FMath::Pow(Level, 3.0f); + + return TotalExp; +} + +float UMJPlayerStatComponent::GetNumerator() +{ + + if (PlayerLevel == 1) + { + Numerator = TotalCumulativeExperience; + } + else + { + Numerator = TotalCumulativeExperience - GetTotalExperienceForLevel(PlayerLevel); + } + + return Numerator; +} + +float UMJPlayerStatComponent::GetDenominator() +{ + Denominator = ExperienceForNextLevel - GetTotalExperienceForLevel(PlayerLevel); + if (PlayerLevel == 1) + { + Denominator = GetTotalExperienceForLevel(PlayerLevel + 1); + } + else + { + Denominator = ExperienceForNextLevel - GetTotalExperienceForLevel(PlayerLevel); + } + return Denominator; +} diff --git a/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.h b/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.h new file mode 100644 index 00000000..e5606af4 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJPlayerStatComponent.h @@ -0,0 +1,86 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Character/Component/MJStatComponentBase.h" +#include "MJPlayerStatComponent.generated.h" + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLevelUpDelegate, int32, NewLevel); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnExperienceChanged, float, TotalExperience, float, ExpForNextLevel); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGoldChangeDelegate, int32, NewGold); + +/** + * Class Description: 플레이어는 GameInstance에 있는 CurveTable을 바로 접근 + * Author: 신동민 + * Created Date: 2025.07.17 + * Description of Change: 레벨 업 추가 + * Modified By: 신동민 + * Modified Date: 2025.07.21 + */ + +class UCurveTable; + +UCLASS() +class PROJECTMJ_API UMJPlayerStatComponent : public UMJStatComponentBase +{ + GENERATED_BODY() + +public: + UMJPlayerStatComponent(); + + virtual void InitializeStat() override; + + UFUNCTION(BlueprintCallable, Category = "Stat|Experience") + void GainExperience(int32 GainedExp); + + UFUNCTION(Category = "Stat|Gold") + void GainGold(int32 GainedGold); + + UFUNCTION(Category = "Stat|Gold") + void SpendGold(int32 UsedGold); + + UPROPERTY(BlueprintAssignable, Category = "Stat|Experience") + FOnLevelUpDelegate OnLevelUp; + + UPROPERTY(BlueprintAssignable, Category = "Stat|Experience") + FOnExperienceChanged OnExperienceChanged; + + UPROPERTY(BlueprintAssignable, Category = "Stat|Gold") + FOnGoldChangeDelegate OnGoldChange; + + FORCEINLINE int32 GetPlayerLevel() const { return PlayerLevel; } + FORCEINLINE float GetTotalCumulativeExperience() const { return TotalCumulativeExperience; } + FORCEINLINE float GetExperienceForNextLevel() const { return ExperienceForNextLevel; } + FORCEINLINE int32 GetGold() const { return Gold; } + FORCEINLINE void SetPlayerLevel(int32 NewPlayerLevel) {PlayerLevel = NewPlayerLevel;} + FORCEINLINE void SetTotalCumulativeExperience(float NewExp){ TotalCumulativeExperience = NewExp;} + + + float GetNumerator(); + float GetDenominator(); + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat") + TObjectPtr PlayerStatTable; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat|Level") + int32 PlayerLevel = 1; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat|Experience") + float TotalCumulativeExperience = 0.0f; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat|Experience") + float ExperienceForNextLevel = 0.0f; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat|Gold") + int32 Gold = 100; // 테스트용으로 100 + + float Numerator; + float Denominator; + + +private: + void CheckForLevelUp(); + float GetTotalExperienceForLevel(int32 Level) const; +}; diff --git a/Source/ProjectMJ/Character/Component/MJSkillComponent.cpp b/Source/ProjectMJ/Character/Component/MJSkillComponent.cpp deleted file mode 100644 index 8dcaf9d0..00000000 --- a/Source/ProjectMJ/Character/Component/MJSkillComponent.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "Character/Component/MJSkillComponent.h" - -#include "ProjectMJ.h" -#include "Character/MJCharacterBase.h" -#include "AbilitySystemComponent.h" -#include "AbilitySystem/MJAbilitySystemComponent.h" -#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" -#include "AbilitySystem/Effect/MJGE_SetSkillAttributeSet.h" -//#include "DM/MJGameInstanceDM.h" -#include "TG/MJGameInstanceTG.h" - -// Sets default values for this component's properties -UMJSkillComponent::UMJSkillComponent() -{ - PrimaryComponentTick.bCanEverTick = false; - - // TODO: 현재 테스트 용 Input 태그를 사용해서 매핑했으므로 나중에 태그 정리와 함께 할 것 - // 하드 코딩 개선 하고 싶음 - InputTagToSkillTagMap.Add(FGameplayTag::RequestGameplayTag(TEXT("Input.Test.Q")),FGameplayTag::RequestGameplayTag(TEXT("Skill.Instant"))); - InputTagToSkillTagMap.Add(FGameplayTag::RequestGameplayTag(TEXT("Input.Test.W")),FGameplayTag::RequestGameplayTag(TEXT("Skill.Charge"))); -} - -void UMJSkillComponent::BeginPlay() -{ - Super::BeginPlay(); - - - // Dongmin: 테스트용 시작하자 마자 스킬 획득 - LearnSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack"))); - EquipSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack"))); - //UnequipSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack"))); - LearnSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Charge.Catastrophe"))); - EquipSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Charge.Catastrophe"))); - - // TODO:일반 공격 슬롯 만들어야 함 - // 일반 공격 태그를 따로 만들어야 하나? - -} - - -void UMJSkillComponent::LearnSkill(const FGameplayTag& NewSkill) -{ - MJ_LOG(LogMJ, Log, TEXT("LearnSkill Start")); - // DongMin: - SkillTag == DefaultSkillTag == RowName(이건 자료형이 FName이긴 한데) - // - SkillTag는 Skill.{Type}.{SkillName} - if (OwnedSkillMap.Contains(NewSkill)) - { - if (OwnedSkillMap[NewSkill].Level < 9) - { - ++OwnedSkillMap[NewSkill].Level; - // 장착중인 스킬이 레벨업 하면 GiveAbility 새로 해줘야함 - if (EquippedSkillMap.Contains(NewSkill)) - { - GiveAbilityToASC(NewSkill); - } - } - MJ_LOG(LogMJ, Log, TEXT("Level up")); - - } - else - { - UMJGameInstanceTG* GI = GetWorld()->GetGameInstance(); - if (!GI || !GI->SkillDataTable) - { - return; - } - FName RowName = NewSkill.GetTagName(); - const FMJSkillDataRow* DataRow = GI->SkillDataTable->FindRow(RowName, TEXT("")); - if (DataRow) - { - FSkillData NewSkillData; - NewSkillData.SkillDefaultTag = NewSkill; - NewSkillData.SkillTypeTag = NewSkill.GetGameplayTagParents().GetByIndex(1); - OwnedSkillMap.Add(NewSkill, NewSkillData); - MJ_LOG(LogMJ, Log, TEXT("LearnSkill Add")); - - } - } - - -} - -void UMJSkillComponent::EquipSkill(const FGameplayTag& EquippingSkill) -{ - - MJ_LOG(LogMJ, Log, TEXT("EquipSkill Start")); - // DongMin: 절대 일어나지 않을거 같지만 예외처리 - if (!OwnedSkillMap.Contains(EquippingSkill)) - { - return; - } - - FGameplayTag SkillTypeTag = OwnedSkillMap[EquippingSkill].SkillTypeTag; - if (EquippedSkillMap.Contains(SkillTypeTag)) - { - FGameplayTag EquippedSkill = EquippedSkillMap[SkillTypeTag]; - UnequipSkill(EquippedSkill); - } - - EquippedSkillMap.Add(SkillTypeTag, EquippingSkill); - GiveAbilityToASC(EquippingSkill); - MJ_LOG(LogMJ, Log, TEXT("EquipSkill End")); - -} - -void UMJSkillComponent::UnequipSkill (const FGameplayTag& UnequippingSkill) -{ - // DongMin: 절대 일어나지 않을거 같지만 예외처리 - if (!OwnedSkillMap.Contains(UnequippingSkill)) - { - return; - } - - FGameplayTag SkillTypeTag = OwnedSkillMap[UnequippingSkill].SkillTypeTag; - if (EquippedSkillMap.Contains(SkillTypeTag) && EquippedSkillMap[SkillTypeTag] == UnequippingSkill) - { - EquippedSkillMap.Remove(SkillTypeTag); - RemoveAbility(UnequippingSkill); - } -} - -void UMJSkillComponent::ActivateSkill(const FGameplayTag& EquippedSlotSkill) -{ - // DongMin: 절대 일어나지 않을거 같지만 예외처리 - if (!OwnedSkillMap.Contains(EquippedSlotSkill)) - { - return; - } - - if (!GivenAbilityHandles.Contains(EquippedSlotSkill)) - { - MJ_LOG(LogMJ, Log, TEXT("not exist GivenAbilityHandles(EquippedSlotSkill)")); - return; - } - - AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); - if (!OwnerCharacter) - { - MJ_LOG(LogMJ, Log, TEXT("not exist OwnerCharacter")); - return; - } - - UMJAbilitySystemComponent* MJASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); - if (!MJASC) - { - MJ_LOG(LogMJ, Log, TEXT("not exist ASC")); - return; - } - - FGameplayTag SkillTypeTag = OwnedSkillMap[EquippedSlotSkill].SkillTypeTag; - if (!EquippedSkillMap.Contains(SkillTypeTag)) - { - return; - } - - UMJGameInstanceTG* GI = GetWorld()->GetGameInstance(); - if (!GI || !GI->SkillDataTable) - { - MJ_LOG(LogMJ, Log, TEXT("not exist GI or SkillDataTable")); - return; - } - - const FMJSkillDataRow* DataRow = GI->SkillDataTable->FindRow(EquippedSlotSkill.GetTagName(), TEXT("ActivateSkill")); - if (!DataRow) - { - MJ_LOG(LogMJ, Log, TEXT("not exist DataRow")); - return; - } - - UCurveTable* CurveTable = DataRow->SkillLevelDataTable.LoadSynchronous(); - if (!CurveTable) - { - MJ_LOG(LogMJ, Log, TEXT("not exist SkillLevelDataTable")); - return; - } - - const UMJCharacterSkillAttributeSet* SkillAttributeSet = MJASC->GetSet(); - if (!SkillAttributeSet) - { - MJ_LOG(LogMJ, Log, TEXT("not exist SkillAttributeSet")); - return; - } - - TSubclassOf EffectClass = UMJGE_SetSkillAttributeSet::StaticClass(); - - FGameplayEffectContextHandle EffectContext = MJASC->MakeEffectContext(); - EffectContext.AddSourceObject(this); - - FGameplayEffectSpecHandle SpecHandle = MJASC->MakeOutgoingSpec(EffectClass, 1.f, EffectContext); - // Dongmin: 여기서 Attribute 덮어줄거다 - if (SpecHandle.IsValid()) - { - // Skill은 SkillComponent의 SkillData에서 가져와서 쓰는걸로 그 레벨로 다른 데이터를 가져오는 걸로 - float SkillLevel = OwnedSkillMap[EquippedSlotSkill].Level; - SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.SkillLevel")), SkillLevel); - SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.MaxSkillLevel")), 9); - - for (auto& CurveTableRow : CurveTable->GetRowMap()) - { - const FName& RowName = CurveTableRow.Key; - FRealCurve* Curve = CurveTable->FindCurve(RowName, TEXT("FindCurve")); - - if (Curve) - { - float Value = Curve->Eval(SkillLevel); - FString TagString = FString::Printf(TEXT("Data.Skill.%s"), *RowName.ToString()); - SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName(*TagString)), Value); - - } - } - MJASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get()); - } - - FGameplayAbilitySpecHandle Handle = GivenAbilityHandles[EquippedSlotSkill]; - MJASC->TryActivateAbility(Handle); -} - -// TODO: -void UMJSkillComponent::GiveAbilityToASC(const FGameplayTag& UpdateSkill) -{ - AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); - if (!OwnerCharacter) - { - MJ_LOG(LogMJ, Log, TEXT("not exist OwnerCharacter")); - return; - } - UMJAbilitySystemComponent* MJASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); - if (!MJASC) - { - MJ_LOG(LogMJ,Log,TEXT("not exist ASC")); - return; - } - - UMJGameInstanceTG* GI = GetWorld()->GetGameInstance(); - if (!GI || !GI->SkillDataTable) - { - MJ_LOG(LogMJ, Log, TEXT("not exist GI or SkillDataTable")); - return; - } - - const FMJSkillDataRow* DataRow = GI->SkillDataTable->FindRow(UpdateSkill.GetTagName(), TEXT("GiveAbility")); - if (DataRow && DataRow->SkillAbilityClass) - { - RemoveAbility(UpdateSkill); - - // DongmMin: 예외처리 안 한 이유는 이거가 들어가는 로직에 이미 OwnedSkillMap을 검증해서 인데 - // 그래도 예외처리 해주는게 좋은가? - //if (!OwnedSkillMap.Contains(UpdateSkill)) - //{ - // return; - //} - int32 SkillLevel = OwnedSkillMap[UpdateSkill].Level; - - FGameplayAbilitySpec AbilitySpec(DataRow->SkillAbilityClass, SkillLevel, INDEX_NONE, OwnerCharacter); - - FGameplayAbilitySpecHandle Handle = MJASC->GiveAbility(AbilitySpec); - GivenAbilityHandles.Add(UpdateSkill, Handle); - } -} - -void UMJSkillComponent::RemoveAbility(const FGameplayTag& RemoveSkill) -{ - AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); - if (!OwnerCharacter) - { - MJ_LOG(LogMJ, Log, TEXT("not exist OwnerCharacter")); - return; - } - UMJAbilitySystemComponent* MJASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); - if (!MJASC) - { - MJ_LOG(LogMJ, Log, TEXT("not exist ASC")); - return; - } - - if (GivenAbilityHandles.Contains(RemoveSkill)) - { - MJASC->ClearAbility(GivenAbilityHandles[RemoveSkill]); - GivenAbilityHandles.Remove(RemoveSkill); - } - -} - -void UMJSkillComponent::ActivateSkillByInputTag(const FGameplayTag& InputTag) -{ - - if (!InputTagToSkillTagMap.Contains(InputTag)) - { - MJ_LOG(LogMJ, Warning, TEXT("not exist InputTag in InputTagToSkillTagMap")); - return; - } - FGameplayTag SkillTypeTag = InputTagToSkillTagMap[InputTag]; - - if (!EquippedSkillMap.Contains(SkillTypeTag)) - { - MJ_LOG(LogMJ, Warning, TEXT("not exist SkillTag in EquippedSkillMap")); - return; - } - - FGameplayTag EquippedSkill = EquippedSkillMap[SkillTypeTag]; - ActivateSkill(EquippedSkill); -} - -FGameplayTag UMJSkillComponent::ConvertInputTagToTypeTag(const FGameplayTag& InputTag) -{ - if (InputTagToSkillTagMap.Contains(InputTag)) - { - return InputTagToSkillTagMap[InputTag]; - } - - return FGameplayTag(); -} \ No newline at end of file diff --git a/Source/ProjectMJ/Character/Component/MJSkillComponent.h b/Source/ProjectMJ/Character/Component/MJSkillComponent.h deleted file mode 100644 index 0a5c1a52..00000000 --- a/Source/ProjectMJ/Character/Component/MJSkillComponent.h +++ /dev/null @@ -1,81 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Components/ActorComponent.h" -#include "GameplayTagContainer.h" -#include "DataTable/MJSkillDataRow.h" -#include "GameplayAbilitySpec.h" -#include "MJSkillComponent.generated.h" - -/** - * Class Description: 스킬 컴포넌트 - * 역할 - * - 보유 스킬을 가질 수 있고, 장착 할 수 있음 - * - - * - - * Author: 신동민 - * Created Date: 2025_06_27 - * Last Modified By: - * Last Modified Date: - */ - - -USTRUCT(BlueprintType) -struct FSkillData -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite) - int32 Level = 1; - - UPROPERTY(EditAnywhere, BlueprintReadWrite) - FGameplayTag SkillDefaultTag; - - UPROPERTY(EditAnywhere, BlueprintReadWrite) - FGameplayTag SkillTypeTag; - -}; - -UCLASS( ClassGroup=(Skill), meta=(BlueprintSpawnableComponent) ) -class PROJECTMJ_API UMJSkillComponent : public UActorComponent -{ - GENERATED_BODY() - -public: - // Sets default values for this component's properties - UMJSkillComponent(); - - virtual void BeginPlay() override; - -public: - void LearnSkill(const FGameplayTag& NewSkill); - - void EquipSkill(const FGameplayTag& EquippingSkill); - - void UnequipSkill(const FGameplayTag& UnequippingSkill); - - void ActivateSkill(const FGameplayTag& EquippedSlotSkill); - - void GiveAbilityToASC(const FGameplayTag& AddSkill); - - void RemoveAbility(const FGameplayTag& RemoveSkill); - - void ActivateSkillByInputTag(const FGameplayTag& InputTag); - - FGameplayTag ConvertInputTagToTypeTag(const FGameplayTag& InputTag); - -protected: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") - TMap OwnedSkillMap; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") - TMap EquippedSkillMap; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") - TMap InputTagToSkillTagMap; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") - TMap GivenAbilityHandles; -}; diff --git a/Source/ProjectMJ/Character/Component/MJSkillComponentBase.cpp b/Source/ProjectMJ/Character/Component/MJSkillComponentBase.cpp new file mode 100644 index 00000000..111f539f --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJSkillComponentBase.cpp @@ -0,0 +1,366 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJSkillComponentBase.h" + +#include "ProjectMJ.h" +#include "AbilitySystemComponent.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "AbilitySystem/Effect/MJGE_SetSkillAttributeSet.h" +#include "Character/MJCharacterBase.h" +#include "DataTable/MJSkillDataRow.h" +#include "DataTable/MJSkillLevelAbilityRow.h" + +// Sets default values for this component's properties +UMJSkillComponentBase::UMJSkillComponentBase() +{ + PrimaryComponentTick.bCanEverTick = false; +} + +void UMJSkillComponentBase::LearnSkill(const FGameplayTag& SkillTag) +{ + // DongMin: - SkillTag == DefaultSkillTag == RowName(이건 자료형이 FName이긴 한데) + // - SkillTag는 Skill.{Type}.{SkillName} + if (OwnedSkillMap.Contains(SkillTag)) + { + if (OwnedSkillMap[SkillTag].Level < 9) + { + ++OwnedSkillMap[SkillTag].Level; + + FGameplayTag SkillTypeTag = OwnedSkillMap[SkillTag].SkillTypeTag; + + if (EquippedSkillMap.Contains(SkillTypeTag)) + { + GiveAbilityToASC(SkillTag); + } + } + } + else + { + FName RowName = SkillTag.GetTagName(); + UDataTable* SkillDataTable = GetSkillDataTable(); + const FMJSkillDataRow* DataRow = SkillDataTable->FindRow(RowName, TEXT("")); + if (DataRow) + { + FSkillData NewSkillData; + + // TG : struct 에서 매번 1로 초기화하기 때문에 따로 레벨 복구 로직 추가 + if (LoadedOwnedSkillMap.Contains(SkillTag)) + { + NewSkillData.Level = LoadedOwnedSkillMap[SkillTag].Level; + } + NewSkillData.SkillDefaultTag = SkillTag; + int32 TagLength = SkillTag.GetGameplayTagParents().Num(); + if (TagLength < 2) + { + MJ_LOG(LogMJ, Warning, TEXT("Tag is too short")); + return; + } + NewSkillData.SkillTypeTag = SkillTag.GetGameplayTagParents().GetByIndex(TagLength - 2); + OwnedSkillMap.Add(SkillTag, NewSkillData); + } + } + if (OwnedSkillMap.Contains(SkillTag)) + { + OnLearnSkillEvent.Broadcast(SkillTag, OwnedSkillMap[SkillTag].Level); + } +} + +void UMJSkillComponentBase::EquipSkill(const FGameplayTag& SkillTag) +{ + if (!OwnedSkillMap.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnedSkillMap[SkillTag]")); + return; + } + + FGameplayTag SkillTypeTag = OwnedSkillMap[SkillTag].SkillTypeTag; + if (EquippedSkillMap.Contains(SkillTypeTag)) + { + FGameplayTag EquippedSkill = EquippedSkillMap[SkillTypeTag]; + UnequipSkill(EquippedSkill); + } + + EquippedSkillMap.Add(SkillTypeTag, SkillTag); + GiveAbilityToASC(SkillTag); +} + +void UMJSkillComponentBase::UnequipSkill(const FGameplayTag& SkillTag) +{ + if (!OwnedSkillMap.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnedSkillMap[SkillTag]")); + return; + } + + FGameplayTag SkillTypeTag = OwnedSkillMap[SkillTag].SkillTypeTag; + if (EquippedSkillMap.Contains(SkillTypeTag) && EquippedSkillMap[SkillTypeTag] == SkillTag) + { + EquippedSkillMap.Remove(SkillTypeTag); + RemoveAbility(SkillTag); + } +} + +void UMJSkillComponentBase::ActivateSkill(const FGameplayTag& SkillTag) +{ + if (!OwnedSkillMap.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnedSkillMap[SkillTag]")); + return; + } + + if (!GivenActionAbilityHandles.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist GivenActionAbilityHandles(EquippedSlotSkill)")); + return; + } + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + FGameplayTag SkillTypeTag = OwnedSkillMap[SkillTag].SkillTypeTag; + if (!EquippedSkillMap.Contains(SkillTypeTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + UDataTable* SkillDataTable = GetSkillDataTable(); + const FMJSkillDataRow* DataRow = SkillDataTable->FindRow(SkillTag.GetTagName(), TEXT("ActivateSkill")); + if (!DataRow) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist DataRow")); + return; + } + + UCurveTable* CurveTable = DataRow->SkillLevelDataTable.LoadSynchronous(); + if (!CurveTable) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillLevelDataTable")); + return; + } + + const UMJCharacterSkillAttributeSet* SkillAttributeSet = ASC->GetSet(); + if (!SkillAttributeSet) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillAttributeSet")); + return; + } + + TSubclassOf EffectClass = UMJGE_SetSkillAttributeSet::StaticClass(); + + FGameplayEffectContextHandle EffectContext = ASC->MakeEffectContext(); + EffectContext.AddSourceObject(this); + + FGameplayEffectSpecHandle SpecHandle = ASC->MakeOutgoingSpec(EffectClass, 1.f, EffectContext); + // Dongmin: 여기서 Attribute 덮어줌 + if (SpecHandle.IsValid()) + { + // Dongmin: Skill은 SkillComponent의 SkillData에서 가져와서 쓰는걸로 그 레벨로 다른 데이터를 가져오는 방식 + float SkillLevel = OwnedSkillMap[SkillTag].Level; + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.SkillLevel")), SkillLevel); + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Skill.MaxSkillLevel")), 9); + + for (auto& CurveTableRow : CurveTable->GetRowMap()) + { + const FName& RowName = CurveTableRow.Key; + FRealCurve* Curve = CurveTable->FindCurve(RowName, TEXT("FindCurve")); + + if (Curve) + { + float Value = Curve->Eval(SkillLevel); + FString TagString = FString::Printf(TEXT("Data.Skill.%s"), *RowName.ToString()); + SpecHandle.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName(*TagString)), Value); + + } + } + ASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get()); + } + + FGameplayAbilitySpecHandle Handle = GivenActionAbilityHandles[SkillTag]; + ASC->TryActivateAbility(Handle); + +} + +void UMJSkillComponentBase::ReleaseSkill(const FGameplayTag& SkillTag) +{ + if (!OwnedSkillMap.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnedSkillMap[SkillTag]")); + return; + } + + if (!GivenActionAbilityHandles.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist GivenActionAbilityHandles(EquippedSlotSkill)")); + return; + } + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + FGameplayAbilitySpecHandle Handle = GivenActionAbilityHandles[SkillTag]; + FGameplayAbilitySpec* SpecPtr = ASC->FindAbilitySpecFromHandle(Handle); + if (SpecPtr) + { + ASC->AbilitySpecInputReleased(*SpecPtr); + } +} + +void UMJSkillComponentBase::GiveAbilityToASC(const FGameplayTag& SkillTag) +{ + if (!OwnedSkillMap.Contains(SkillTag)) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnedSkillMap[SkillTag]")); + return; + } + + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + UDataTable* SkillDataTable = GetSkillDataTable(); + const FMJSkillDataRow* DataRow = SkillDataTable->FindRow(SkillTag.GetTagName(), TEXT("GiveAbility")); + if (!DataRow) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist DataRow")); + return; + } + + RemoveAbility(SkillTag); + + int32 SkillLevel = OwnedSkillMap[SkillTag].Level; + FName RowName = FName(*FString::FromInt(SkillLevel)); + + UDataTable* SkillLevelAbilityTable = DataRow->SkillLevelAbilityTable.LoadSynchronous(); + if (!SkillLevelAbilityTable) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillLevelAbilityTable")); + return; + } + + const FMJSkillLevelAbilityRow* LevelAbilityRow = SkillLevelAbilityTable->FindRow(RowName, TEXT("SkillLevelAbility")); + + if (LevelAbilityRow->ActionSkillAbilityClass) + { + FGameplayAbilitySpec ActionAbilitySpec(LevelAbilityRow->ActionSkillAbilityClass, SkillLevel, INDEX_NONE, OwnerCharacter); + FGameplayAbilitySpecHandle ActionHandle = ASC->GiveAbility(ActionAbilitySpec); + GivenActionAbilityHandles.Add(SkillTag, ActionHandle); + } + + if (LevelAbilityRow->SkillAbilityClass) + { + FGameplayAbilitySpec SkillAbilitySpec(LevelAbilityRow->SkillAbilityClass, SkillLevel, INDEX_NONE, OwnerCharacter); + + FGameplayAbilitySpecHandle SkillHandle = ASC->GiveAbility(SkillAbilitySpec); + GivenSkillAbilityHandles.Add(SkillTag, SkillHandle); + } + + if (LevelAbilityRow->PassiveSkillAbilityClass) + { + FGameplayAbilitySpec PassiveSkillAbilitySpec(LevelAbilityRow->PassiveSkillAbilityClass, SkillLevel, INDEX_NONE, OwnerCharacter); + FGameplayAbilitySpecHandle PassiveSkillHandle = ASC->GiveAbility(PassiveSkillAbilitySpec); + GivenPassiveAbilityHandles.Add(SkillTag, PassiveSkillHandle); + } + + if (LevelAbilityRow->DrawMarkerAbilityClass) + { + FGameplayAbilitySpec DrawMarkerAbilitySpec(LevelAbilityRow->DrawMarkerAbilityClass, SkillLevel, INDEX_NONE, OwnerCharacter); + FGameplayAbilitySpecHandle PassiveSkillHandle = ASC->GiveAbility(DrawMarkerAbilitySpec); + GivenDrawMarkerAbilityHandles.Add(SkillTag, PassiveSkillHandle); + } +} + +void UMJSkillComponentBase::RemoveAbility(const FGameplayTag& SkillTag) +{ + AMJCharacterBase* OwnerCharacter = Cast(GetOwner()); + if (!OwnerCharacter) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist OwnerCharacter")); + return; + } + UMJAbilitySystemComponent* ASC = Cast(OwnerCharacter->GetAbilitySystemComponent()); + if (!ASC) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist ASC")); + return; + } + + if (GivenActionAbilityHandles.Contains(SkillTag)) + { + ASC->ClearAbility(GivenActionAbilityHandles[SkillTag]); + GivenActionAbilityHandles.Remove(SkillTag); + } + + if (GivenSkillAbilityHandles.Contains(SkillTag)) + { + ASC->ClearAbility(GivenSkillAbilityHandles[SkillTag]); + GivenSkillAbilityHandles.Remove(SkillTag); + } + + if (GivenPassiveAbilityHandles.Contains(SkillTag)) + { + ASC->ClearAbility(GivenPassiveAbilityHandles[SkillTag]); + GivenPassiveAbilityHandles.Remove(SkillTag); + } + + if (GivenDrawMarkerAbilityHandles.Contains(SkillTag)) + { + ASC->ClearAbility(GivenDrawMarkerAbilityHandles[SkillTag]); + GivenDrawMarkerAbilityHandles.Remove(SkillTag); + } +} + +void UMJSkillComponentBase::SetOwnedSkillMap(TMap InputOwnedSkillMap) +{ + LoadedOwnedSkillMap.Empty(); + + for (auto Iter : InputOwnedSkillMap) + { + LoadedOwnedSkillMap.Add(Iter); + } + +} + +void UMJSkillComponentBase::SetEquippedSkillMap(TMap InputEquippedSkillMap) +{ + LoadedEquippedSkillMap.Empty(); + + for (auto Iter : InputEquippedSkillMap) + { + LoadedEquippedSkillMap.Add(Iter); + } + +} diff --git a/Source/ProjectMJ/Character/Component/MJSkillComponentBase.h b/Source/ProjectMJ/Character/Component/MJSkillComponentBase.h new file mode 100644 index 00000000..50f2d7b2 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJSkillComponentBase.h @@ -0,0 +1,105 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "GameplayTagContainer.h" +#include "GameplayAbilitySpec.h" + +#include "MJSkillComponentBase.generated.h" + +/** + * Class Description: 스킬 컴포넌트 베이스 + * - 플레이어와 AI가 사용하는 공통 스킬 컴포넌트 + * - *더 구체적으로 분류한다면, AI가 스킬을 여러개 사용 + 장착을 안 하는 개념으로 더 추상화 가능* + * Author: 신동민 + * Created Date: 2025.07.08 + * Last Modified By: + * Last Modified Date: + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnLearnSkillEvent, FGameplayTag, SkillTag, int32, Level); +class UMJUIManagerSubsystem; +class UMJSkillWidget; + +USTRUCT(BlueprintType) +struct FSkillData +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 Level = 1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FGameplayTag SkillDefaultTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FGameplayTag SkillTypeTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FGameplayTag ProjectileTag; +}; + +UCLASS( ClassGroup=(Skill), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJSkillComponentBase : public UActorComponent +{ + GENERATED_BODY() + +public: + UMJSkillComponentBase(); + + virtual void LearnSkill(const FGameplayTag& SkillTag); + + virtual void EquipSkill(const FGameplayTag& SkillTag); + + virtual void UnequipSkill(const FGameplayTag& SkillTag); + + virtual void ActivateSkill(const FGameplayTag& SkillTag); + + virtual void ReleaseSkill(const FGameplayTag& SkillTag); + + void GiveAbilityToASC(const FGameplayTag& SkillTag); + + void RemoveAbility(const FGameplayTag& SkillTag); + + // Getter + TMap GetOwnedSkillMap() const { return OwnedSkillMap; } + TMap GetEquippedSkillMap() const {return EquippedSkillMap;} + + void SetOwnedSkillMap(TMap InputOwnedSkillMap); + void SetEquippedSkillMap(TMap InputEquippedSkillMap); + + virtual UDataTable* GetSkillDataTable() const PURE_VIRTUAL(UMJSkillComponentBase::GetSkillDataTable, return nullptr;); + + FOnLearnSkillEvent OnLearnSkillEvent; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap OwnedSkillMap; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap EquippedSkillMap; + + // PlayerSessionData 에서 로딩된 데이터는 여기에 담겨 다시 Learn 함 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap LoadedOwnedSkillMap; + + // PlayerSessionData 에서 로딩된 데이터는 여기에 담겨 다시 Equip 함 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap LoadedEquippedSkillMap; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap GivenActionAbilityHandles; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap GivenSkillAbilityHandles; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap GivenPassiveAbilityHandles; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap GivenDrawMarkerAbilityHandles; + + + // 시간관계상 구조를 생각하지 않고.. 임시로 갖고 있겠다... +}; diff --git a/Source/ProjectMJ/Character/Component/MJStatComponentBase.cpp b/Source/ProjectMJ/Character/Component/MJStatComponentBase.cpp new file mode 100644 index 00000000..818f46e5 --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJStatComponentBase.cpp @@ -0,0 +1,26 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Character/Component/MJStatComponentBase.h" + +UMJStatComponentBase::UMJStatComponentBase() +{ + PrimaryComponentTick.bCanEverTick = false; +} + +void UMJStatComponentBase::OnDead(AActor* InEffectCauser) +{ + // wanna any implemenation, override and call super. + + bIsDead = true; + + OnDeath.Broadcast(InEffectCauser); +} + +void UMJStatComponentBase::OnDamaged(float Magnitude, bool bIsCritical) +{ + OnDamage.Broadcast(Magnitude, bIsCritical); +} + + + diff --git a/Source/ProjectMJ/Character/Component/MJStatComponentBase.h b/Source/ProjectMJ/Character/Component/MJStatComponentBase.h new file mode 100644 index 00000000..26a531eb --- /dev/null +++ b/Source/ProjectMJ/Character/Component/MJStatComponentBase.h @@ -0,0 +1,52 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "MJStatComponentBase.generated.h" + +/** + * Class Description: Character Stat Component + * Author: 신동민 + * Created Date: 2025.07.17 + * Modified By: + * Modified Date: + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDeathDelegate, AActor*, InEffectCauser); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnDamageDelegate, float, Data, bool, IsCritical); + + +class UGameplayEffect; + +UCLASS( ClassGroup=(Stat), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJStatComponentBase : public UActorComponent +{ + GENERATED_BODY() + +public: + UMJStatComponentBase(); + + virtual void InitializeStat() {}; + + FORCEINLINE void SetbIsDead(bool NewbIsDead) {bIsDead = NewbIsDead;} + FORCEINLINE bool GetbIsDead() const { return bIsDead; } + + FORCEINLINE void SetbbIsInitializingStats(bool NewbIsInitializingStats) {bIsInitializingStats = NewbIsInitializingStats;} + FORCEINLINE bool GetbIsInitializingStats() const { return bIsInitializingStats; } + + virtual void OnDead(AActor* InEffectCauser); + virtual void OnDamaged(float Magnitude, bool bIsCritical); + + FOnDeathDelegate OnDeath; + mutable FOnDamageDelegate OnDamage; + +protected: + + UPROPERTY() + bool bIsDead = false; + + bool bIsInitializingStats = false; + +}; diff --git a/Source/ProjectMJ/Character/MJCharacterBase.cpp b/Source/ProjectMJ/Character/MJCharacterBase.cpp index 1bb31cca..fd321886 100644 --- a/Source/ProjectMJ/Character/MJCharacterBase.cpp +++ b/Source/ProjectMJ/Character/MJCharacterBase.cpp @@ -5,10 +5,17 @@ #include "ProjectMJ.h" #include "AbilitySystem/MJAbilitySystemComponent.h" -#include "Component/MJSkillComponent.h" #include "Components/CapsuleComponent.h" #include "Physics/MJCollision.h" #include "Player/MJPlayerState.h" +#include "MotionWarpingComponent.h" +#include "Component/MJAbilityContextComponent.h" +#include "DataAsset/MJInnateGameplayEffectDataAsset.h" +#include "DataAsset/MJStateAbilityDataAsset.h" +#include "TG/Component/MJMiniMapIconMeshComponent.h" +#include "UI/World/MJDamageComponent.h" +#include "UI/World/MJDamageWidget.h" + // Sets default values AMJCharacterBase::AMJCharacterBase() @@ -19,11 +26,20 @@ AMJCharacterBase::AMJCharacterBase() PrimaryActorTick.bStartWithTickEnabled = false; GetMesh()->bReceivesDecals = false; ASC = nullptr; - - SkillComponent = CreateDefaultSubobject(TEXT("SkillComponent")); // Capsule Component Collision Profile GetCapsuleComponent()->SetCollisionProfileName(CPROFILE_MJCAPSULE); + + // MotionWarpingComponent Section + MotionWarpingComponent = CreateDefaultSubobject(TEXT("MotionWarpingComponent")); + + // AbilityContextComponent Section + AbilityContextComponent = CreateDefaultSubobject(TEXT("AbilityContextComponent")); + + // TG : MiniMapIconMeshComponent + MiniMapIconMeshComponent = CreateDefaultSubobject(TEXT("MiniMapIconMeshComponent")); + MiniMapIconMeshComponent->SetupAttachment(GetMesh()); + } UAbilitySystemComponent* AMJCharacterBase::GetAbilitySystemComponent() const @@ -38,6 +54,44 @@ void AMJCharacterBase::BeginPlay() // TeamId 설정 - 적/중립/아군 구별용 TeamId = FGenericTeamId(static_cast(ID)); UE_LOG(LogTemp, Log, TEXT("Selected Team Enum: %d"), TeamId.GetId()); + + /* + * Minjin + * StateAbilityDataAsset 설정 - Player는 캐릭터 BP에서, Enemy는 DataTable을 통해서 + */ + if(StateAbilityDataAsset) + { + // Minjin: State Ability 부여 + FGameplayAbilitySpec AppearAbilitySpec(StateAbilityDataAsset->ActionAppearanceAbilityClass); + ASC->GiveAbility(AppearAbilitySpec); + + FGameplayAbilitySpec DamageAbilitySpec(StateAbilityDataAsset->ActionDamageAbilityClass); + ASC->GiveAbility(DamageAbilitySpec); + + FGameplayAbilitySpec DeathAbilitySpec(StateAbilityDataAsset->ActionDeathAbilityClass); + ASC->GiveAbility(DeathAbilitySpec); + } + + // TODO: 함수로 만들기 - 동민 - + if(InnateGameplayEffectData) + { + for (const TSubclassOf& GameplayEffect : InnateGameplayEffectData->Effects) + { + if (!*GameplayEffect) + { + continue; + } + FGameplayEffectContextHandle GameplayEffectContextHandle = ASC->MakeEffectContext(); + GameplayEffectContextHandle.AddSourceObject(this); + + FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(GameplayEffect, 1.f, GameplayEffectContextHandle); + if (Spec.IsValid()) + { + FActiveGameplayEffectHandle ActiveGameplayEffectHandle = ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get()); + InnateGEHandles.Add(ActiveGameplayEffectHandle); + } + } + } } void AMJCharacterBase::PossessedBy(AController* NewController) @@ -62,6 +116,27 @@ void AMJCharacterBase::PossessedBy(AController* NewController) } +void AMJCharacterBase::FloatDamage(float Magnitude, bool bIsCritical, EOwnerType type) +{ + UMJDamageComponent* NewComp = NewObject(this); + NewComp->RegisterComponent(); + NewComp->SetDamageWidget(this->GetActorLocation(), OffSet); + NewComp->SetVisibility(true); + DamageComponents.Add(NewComp); + + if (UMJDamageWidget* Widget =Cast(NewComp->GetUserWidgetObject()) ) + { + Widget->SetDamage(-Magnitude, bIsCritical, type); + Widget->PlayAnim(); + } + + OffSet ++; + if (OffSet > 5) + { + OffSet = 0; + } +} + diff --git a/Source/ProjectMJ/Character/MJCharacterBase.h b/Source/ProjectMJ/Character/MJCharacterBase.h index 7166f509..fca3b0dc 100644 --- a/Source/ProjectMJ/Character/MJCharacterBase.h +++ b/Source/ProjectMJ/Character/MJCharacterBase.h @@ -9,17 +9,43 @@ #include "MJ/AI/AIPerceptionInfo.h" #include "MJCharacterBase.generated.h" +struct FActiveGameplayEffectHandle; +class UMJInnateGameplayEffectDataAsset; +class UMJDamageComponent; +class UMJStateAbilityDataAsset; +class UMJAbilityContextComponent; +class UMJMiniMapIconMeshComponent; +class AMJMiniMapIconActor; class UMJAbilitySystemComponent; -class UMJSkillComponent; class UMJAttributeSet; class UDataAsset_StartDataBase; +class UMotionWarpingComponent; /** - * Class Description: CharacterBase + * Class Description: CharacterBased * Author: Lee JuHyeon * Created Date: 2025_06_11 - * Last Modified By: Lee JuHyeon / Kim Minjin - * Last Modified Date: Add DA_StartData / (2025.06.20.)Inheritance from IGenericTeamAgentInterface + * Modified By: Lee JuHyeon / Kim Minjin + * Modified Date: Add DA_StartData / (2025.06.20.)Inheritance from IGenericTeamAgentInterface + * + * Description of Change: 모션 워핑 컴포넌트 추가 + * Modified By: 신동민 + * Modified Date: 2025.07.19 + * + * Description of Change: add MiniMapIconMeshComponent + * Modified By: CTG + * Modified Date: 2025.07.31 + * + * Description of Change: Add StateAbilityDataAsset And Setting + * Modified By: Kim Minjin + * Modified Date: 2025.08.09. */ +UENUM(BlueprintType) +enum class EOwnerType : uint8 +{ + Player, + Monster +}; + UCLASS() class PROJECTMJ_API AMJCharacterBase : public ACharacter , public IAbilitySystemInterface, public IGenericTeamAgentInterface { @@ -31,26 +57,54 @@ class PROJECTMJ_API AMJCharacterBase : public ACharacter , public IAbilitySystem virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; + UMJMiniMapIconMeshComponent* GetMiniMapIconMeshComponent() {return MiniMapIconMeshComponent;} + // GenericTeamAgentInterface 구현 virtual FGenericTeamId GetGenericTeamId() const override {return TeamId;} // 에디터에서 ID 설정 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ID") ETeam_ID ID = ETeam_ID::NONE; -protected: - - virtual void BeginPlay() override; - virtual void PossessedBy(AController* NewController)override; +public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GAS") TObjectPtr ASC; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Gas") - TObjectPtr SkillComponent; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GAS") TSoftObjectPtrCharacterStartData; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GAS") + TObjectPtr InnateGameplayEffectData; + + UPROPERTY() + TArray InnateGEHandles; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GAS") + TObjectPtr StateAbilityDataAsset; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") + TObjectPtr MotionWarpingComponent; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GAS") + TObjectPtr AbilityContextComponent; + +protected: + + UPROPERTY(EditDefaultsOnly, Category = "UI") + TObjectPtr MiniMapIconMeshComponent; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Widget, Meta = (AllowPrivateAccess = "true")) + TArray> DamageComponents; + virtual void BeginPlay() override; + virtual void PossessedBy(AController* NewController)override; + FGenericTeamId TeamId; + +public: + // jisoo + UFUNCTION() + virtual void FloatDamage(float Magnitude, bool bIsCritical, EOwnerType type); + float OffSet = 0; }; diff --git a/Source/ProjectMJ/Character/MJForestCreatureCharacter.cpp b/Source/ProjectMJ/Character/MJForestCreatureCharacter.cpp new file mode 100644 index 00000000..b15fcc65 --- /dev/null +++ b/Source/ProjectMJ/Character/MJForestCreatureCharacter.cpp @@ -0,0 +1,29 @@ +// ThenOneDayStudio + + +#include "Character/MJForestCreatureCharacter.h" +#include "MJForestCreatureCharacter.h" +#include "ProjectMJ.h" +#include "Character/Component/MJEnemySkillComponent.h" +#include "DataTable/MJSkillDataRow.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + +AMJForestCreatureCharacter::AMJForestCreatureCharacter() +{ + +} + +void AMJForestCreatureCharacter::BeginPlay() +{ + Super::BeginPlay(); + + SetActorHiddenInGame(false); + SetActorEnableCollision(true); + + +} + +void AMJForestCreatureCharacter::PossessedBy(AController* NewController) +{ + Super::PossessedBy(NewController); +} diff --git a/Source/ProjectMJ/Character/MJForestCreatureCharacter.h b/Source/ProjectMJ/Character/MJForestCreatureCharacter.h new file mode 100644 index 00000000..35d134aa --- /dev/null +++ b/Source/ProjectMJ/Character/MJForestCreatureCharacter.h @@ -0,0 +1,29 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "MJForestCreatureCharacter.generated.h" + +/** + * + */ +class UMJCharacterAttributeSet; +UCLASS() +class PROJECTMJ_API AMJForestCreatureCharacter : public AMJMonsterCharacter +{ + GENERATED_BODY() + +public: + AMJForestCreatureCharacter(); + + virtual void BeginPlay() override; + + virtual void PossessedBy(AController* NewController) override; + FORCEINLINE UMJCharacterAttributeSet* GetAttributeSet() { return CharacterAttributeSet; } + +private: + + +}; diff --git a/Source/ProjectMJ/Character/MJPlayerCharacter.cpp b/Source/ProjectMJ/Character/MJPlayerCharacter.cpp index 1cb98952..64c6352b 100644 --- a/Source/ProjectMJ/Character/MJPlayerCharacter.cpp +++ b/Source/ProjectMJ/Character/MJPlayerCharacter.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #include "Character/MJPlayerCharacter.h" @@ -10,11 +10,17 @@ #include "AbilitySystem/MJAbilitySystemComponent.h" #include "DataAsset/DataAsset_StartDataBase.h" #include "Component/MJPlayerCombatComponent.h" +#include "Component/MJPlayerSkillComponent.h" #include "Perception/AIPerceptionStimuliSourceComponent.h" #include "Perception/AISense_Sight.h" +#include "UI/Inventory/MJInventoryComponent.h" +#include "Component/MJFadeObjectComponent.h" +#include "Component/MJPlayerEffectComponent.h" +#include "Component/MJPlayerStatComponent.h" #include "Perception/AISense_Damage.h" #include "Perception/AISense_Hearing.h" + class UMJSaveGameSubsystem; AMJPlayerCharacter::AMJPlayerCharacter() @@ -40,12 +46,16 @@ AMJPlayerCharacter::AMJPlayerCharacter() FollowCamera->bUsePawnControlRotation = false; GetCharacterMovement()->bOrientRotationToMovement = true; - GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); - GetCharacterMovement()->MaxWalkSpeed = 400.0; - GetCharacterMovement()->BrakingDecelerationWalking = 2000.0; + GetCharacterMovement()->RotationRate = FRotator(0.0f, 720.0f, 0.0f); + // GetCharacterMovement()->BrakingDecelerationWalking = 2000.0; + GetCharacterMovement()->MaxAcceleration = 99999.f; + GetCharacterMovement()->BrakingFrictionFactor = 999.f; + GetCharacterMovement()->BrakingDecelerationWalking = 99999.f; PlayerCombatComponent = CreateDefaultSubobject(TEXT("PlayerCombatComponent")); + //Add FadeComponent + //FadeComponent = CreateDefaultSubobject(TEXT("FadeComponent")); // Minjin: AI Perception-캐릭터를 StimuliSource로 등록(AI가 감지) PerceptionStimuliSourceComponent = CreateDefaultSubobject(TEXT("AIPerceptionStimuliSourceComponent")); if (nullptr!= PerceptionStimuliSourceComponent) @@ -59,20 +69,52 @@ AMJPlayerCharacter::AMJPlayerCharacter() PerceptionStimuliSourceComponent->RegisterWithPerceptionSystem(); } - DialogueTarget = nullptr; + UITarget = nullptr; + + UITrigger = CreateDefaultSubobject(TEXT("UITrigger")); + UITrigger->SetupAttachment(RootComponent); + UITrigger->InitSphereRadius(180.f); + UITrigger->SetCollisionProfileName(TEXT("Trigger")); + UITrigger->SetGenerateOverlapEvents(true); + UITrigger->SetHiddenInGame(true); + + InventoryComponent = CreateDefaultSubobject(TEXT("InventoryComponent")); + // Skill Component + SkillComponent = CreateDefaultSubobject(TEXT("SkillComponent")); + // Stat Component + StatComponent = CreateDefaultSubobject(TEXT("StatComponent")); + // Effect Component + EffectComponent = CreateDefaultSubobject(TEXT("EffectComponent")); - DialogueTrigger = CreateDefaultSubobject(TEXT("DialogueTrigger")); - DialogueTrigger->SetupAttachment(RootComponent); - DialogueTrigger->InitSphereRadius(120.f); - DialogueTrigger->SetCollisionProfileName(TEXT("Trigger")); - DialogueTrigger->SetGenerateOverlapEvents(true); - DialogueTrigger->SetHiddenInGame(false); + // Minjin: ID 설정 + ID = ETeam_ID::PLAYER; + + static ConstructorHelpers::FClassFinderWEAPONCLASS(TEXT("/Game/Characters/Item/BP_Sword.BP_Sword_C")); + if (WEAPONCLASS.Succeeded()) + { + WeaponClass = WEAPONCLASS.Class; + } } void AMJPlayerCharacter::BeginPlay() { Super::BeginPlay(); + + APawn* WeaponInstigator = Cast(GetMesh()->GetOwner()); + FTransform Trans; + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = GetMesh()->GetOwner(); + SpawnParams.Instigator = WeaponInstigator; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding; + + AActor* Weapon = GetWorld()->SpawnActor(WeaponClass, Trans, SpawnParams); + if (!Weapon) + { + return; + } + Weapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, TEXT("Weapon")); + } void AMJPlayerCharacter::PossessedBy(AController* NewController) @@ -88,34 +130,21 @@ void AMJPlayerCharacter::PossessedBy(AController* NewController) // 로딩 데이터 있을 시 받아와서 AttributeSet에 적용 // 없을 시엔 무시하고 기본 AttributeSet 으로 진행됩니다. - - - // UMJGameInstanceTG* MJGI = Cast(GetWorld()->GetGameInstance()); - // - // if (MJGI) + // 태관 : MJPlayerState 에서 LoadPlayersessionData 함수가 InitializeStat 을 호출합니다. + // if (StatComponent) // { - // UMJSaveGameSubsystem* MJSaveGameSubsystem = MJGI->GetSubsystem(); - // if (MJSaveGameSubsystem) - // { - // MJSaveGameSubsystem->LoadSaveGame(this); - // } - // MJ_LOG(LogTG, Log, TEXT("player loaded health : %f"), GetAbilitySystemComponent()->GetNumericAttribute(UMJCharacterAttributeSet::GetHealthAttribute())); + // StatComponent->InitializeStat(); // } - + StatComponent->OnDamage.AddDynamic(this,&ThisClass::OnDamage); } void AMJPlayerCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); - - // if (UMJGameInstanceTG* MJGI = Cast(GetGameInstance())) - // { - // UMJSaveGameSubsystem* MJSaveGameSubsystem = MJGI->GetSubsystem(); - // if (MJSaveGameSubsystem) - // { - // MJSaveGameSubsystem->SaveGameToSlot(this); - // } - // MJ_LOG(LogTG,Log, TEXT("Character Attribute Saved")); - // } -} \ No newline at end of file +} + +void AMJPlayerCharacter::OnDamage(float Magnitude, bool bIsCritical) +{ + FloatDamage(Magnitude, bIsCritical,EOwnerType::Player); +} diff --git a/Source/ProjectMJ/Character/MJPlayerCharacter.h b/Source/ProjectMJ/Character/MJPlayerCharacter.h index 4e936f15..e7f993cd 100644 --- a/Source/ProjectMJ/Character/MJPlayerCharacter.h +++ b/Source/ProjectMJ/Character/MJPlayerCharacter.h @@ -1,38 +1,49 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" +#include "GameplayTagContainer.h" #include "Character/MJCharacterBase.h" +#include "UI/Inventory/MJInventoryComponent.h" +#include "UI/Inventory/MJInventoryInterface.h" #include "MJPlayerCharacter.generated.h" +class UMJPlayerEffectComponent; +class UMJPlayerStatComponent; +class UMJPlayerSkillComponent; class USpringArmComponent; class UCameraComponent; class UMJPlayerCombatComponent; class UAIPerceptionStimuliSourceComponent; class USphereComponent; +class UMJInventoryComponent; +class UMJFadeObjectComponent; + /** * Class Description: * Author: Lee JuHyeon * Created Date: 2025_06_11 - * Last Modified By: Add Combat Component - * Last Modified Date: 2025_06_18 + * Modified By: 신동민 + * Modified Date: 2025.07.08 + * Modified Description: Add SkillComponent + * Modified By: 김민진 + * Modified Date: 2025.07.29. + * Modified Description: Add ID + * Modified By: Lee JUHYEON + * Modified Date: 2025.08.04 + * Modified Description: Add Weapon */ UCLASS() -class PROJECTMJ_API AMJPlayerCharacter : public AMJCharacterBase +class PROJECTMJ_API AMJPlayerCharacter : public AMJCharacterBase, public IMJInventoryInterface { GENERATED_BODY() public: AMJPlayerCharacter(); - + protected: virtual void BeginPlay() override; - - - - - virtual void PossessedBy(AController* NewController)override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; @@ -50,16 +61,27 @@ class PROJECTMJ_API AMJPlayerCharacter : public AMJCharacterBase #pragma region DialoguePart protected: UPROPERTY(VisibleAnywhere, BlueprintReadOnly ,Category = "Trigger") - USphereComponent* DialogueTrigger; + TObjectPtr UITrigger; UPROPERTY() - AActor* DialogueTarget; + TObjectPtr UITarget; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventory") + TObjectPtr InventoryComponent; + + UPROPERTY() + TSubclassOfWeaponClass; public: - void SetDialogueTarget(AActor* NewTarget) { DialogueTarget = NewTarget; } + void SetUITarget(AActor* NewTarget) { UITarget = NewTarget; } + + AActor* GetUITarget() {return UITarget;} + USphereComponent* GetUITrigger() {return UITrigger;} + UMJInventoryComponent* GetInventoryComponent() {return InventoryComponent;} + + + //태관 25.07.23 add protected: +protected: - AActor* GetDialogueTarget() {return DialogueTarget;} - USphereComponent* GetDialogueTrigger() {return DialogueTrigger;} #pragma endregion UPROPERTY(VisibleAnywhere, BlueprintReadOnly,Category="Camera",meta=(AllowPrivateAccess=true)) TObjectPtr CameraBoom; @@ -69,5 +91,23 @@ class PROJECTMJ_API AMJPlayerCharacter : public AMJCharacterBase UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat", meta = (AllowPrivateAccess = true)) TObjectPtrPlayerCombatComponent; + + //FadeActor System Part + /*UPROPERTY(EditDefaultsOnly,BlueprintReadWrite,Category="Fade", meta = (AllowPrivateAccess = true)) + TObjectPtrFadeComponent;*/ #pragma endregion + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Skill", meta = (AllowPrivateAccess = true)) + TObjectPtr SkillComponent; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stat", meta = (AllowPrivateAccess = true)) + TObjectPtr StatComponent; + + UPROPERTY(VisibleAnywhere,BlueprintReadOnly, Category = "Effect", meta = (AllowPrivateAccess = true)) + TObjectPtr EffectComponent; + + UFUNCTION() + void OnDamage(float Magnitude, bool bIsCritical); + }; diff --git a/Source/ProjectMJ/Character/Weapon/MJWeaponBase.cpp b/Source/ProjectMJ/Character/Weapon/MJWeaponBase.cpp index eea76f8f..602d15ac 100644 --- a/Source/ProjectMJ/Character/Weapon/MJWeaponBase.cpp +++ b/Source/ProjectMJ/Character/Weapon/MJWeaponBase.cpp @@ -10,7 +10,9 @@ AMJWeaponBase::AMJWeaponBase() PrimaryActorTick.bCanEverTick = false; WeaponMesh = CreateDefaultSubobject(TEXT("WeaponMesh")); - SetRootComponent(WeaponMesh); + Body = CreateDefaultSubobject(TEXT("Body")); + SetRootComponent(Body); + WeaponMesh->SetupAttachment(GetRootComponent()); } diff --git a/Source/ProjectMJ/Character/Weapon/MJWeaponBase.h b/Source/ProjectMJ/Character/Weapon/MJWeaponBase.h index 3d0abc80..db2e66c3 100644 --- a/Source/ProjectMJ/Character/Weapon/MJWeaponBase.h +++ b/Source/ProjectMJ/Character/Weapon/MJWeaponBase.h @@ -12,7 +12,7 @@ class UBoxComponent; * Author: Lee JuHyeon * Created Date: 2025_06_18 * Last Modified By:Lee Ju Hyeon - * Last Modified Date: Remove Collision + * Last Modified Date: 2025_07_26 */ UCLASS() class PROJECTMJ_API AMJWeaponBase : public AActor @@ -27,6 +27,7 @@ class PROJECTMJ_API AMJWeaponBase : public AActor UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Mesh") TObjectPtrWeaponMesh; - - + // Sword RootComponent Change + UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Mesh") + TObjectPtrBody; }; diff --git a/Source/ProjectMJ/Component/Input/MJInputComponent.h b/Source/ProjectMJ/Component/Input/MJInputComponent.h index 17353de4..7f7dc97e 100644 --- a/Source/ProjectMJ/Component/Input/MJInputComponent.h +++ b/Source/ProjectMJ/Component/Input/MJInputComponent.h @@ -41,7 +41,7 @@ inline void UMJInputComponent::BindAbilityInputAction(const UDataAsset_InputConf { checkf(InInputConfig, TEXT("Input config data aseet is Null, can not Proceed with binding")); - for (const FMJInputActionConfig& AbilityInputActionConfig : InInputConfig->AbilityInputAction) + for (const FMJInputActionConfig& AbilityInputActionConfig : InInputConfig->GetAbilityInputActions()) { if (!AbilityInputActionConfig.IsVaild()) { diff --git a/Source/ProjectMJ/Component/MJFadeObjectComponent.cpp b/Source/ProjectMJ/Component/MJFadeObjectComponent.cpp new file mode 100644 index 00000000..00f49840 --- /dev/null +++ b/Source/ProjectMJ/Component/MJFadeObjectComponent.cpp @@ -0,0 +1,248 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Component/MJFadeObjectComponent.h" +#include "Materials/MaterialInterface.h" +#include "Engine.h" + +#include "Kismet/GameplayStatics.h" +void FFadeSystemStuc::NewElement(TObjectPtr _Primitive, const TArray& _MaterialInt, const TArray& _MID, float _FadeCount, bool _bToHide) +{ + PrimitiveComp = _Primitive; + BaseMatInterface = _MaterialInt; + FadeMID = _MID; + FadeCount = _FadeCount; + bToHide = _bToHide; +} +void FFadeSystemStuc::SetToHide(bool _ToHide) +{ + bToHide = _ToHide; +} +void FFadeSystemStuc::SetHideAndFade(bool _ToHide, float _FadeCount) +{ + bToHide = _ToHide; + FadeCount = _FadeCount; + +} +void FFadeSystemStuc::Destroy() +{ + PrimitiveComp = nullptr; +} +// Sets default values for this component's properties +UMJFadeObjectComponent::UMJFadeObjectComponent() +{ + + PrimaryComponentTick.bCanEverTick = false; + + CurrentFade = 0.0; + FadeNowID = 0; + bIsEnabled = true; + bIsActivate = true; + bIsTraceComplex = false; + + AddObjectInterval = 0.1f; + CalcFadeInterval = 0.05f; + + WorkDistance = 5000.0f; + FadeRate = 10.0f; + + CapsuleRadius = 34.0f; + + NearObjectFade = 0.3f; + FarObjectFade = 0.1f; + + ImmediatelyFade = 0.5; + + ObjectTypes.Add(ECC_WorldStatic); +} + + +// Called when the game starts +void UMJFadeObjectComponent::BeginPlay() +{ + Super::BeginPlay(); + + APlayerController* Controller = UGameplayStatics::GetPlayerController(GetWorld(), 0); + if (Controller == nullptr) + { + return; + } + if (Controller->IsLocalController()) + { + GetOwner()->GetWorld()->GetTimerManager().SetTimer(TimerHandle_AddObjectsTimer, this, &ThisClass::AddObjectHideTimer, AddObjectInterval,true); + GetOwner()->GetWorld()->GetTimerManager().SetTimer(TimerHandle_ObjectComputeTimer, this, &ThisClass::FadeWorkTimer, CalcFadeInterval, true); + + GetOwner()->GetWorld()->GetTimerManager().PauseTimer(TimerHandle_ObjectComputeTimer); + GetOwner()->GetWorld()->GetTimerManager().PauseTimer(TimerHandle_AddObjectsTimer); + + SetActivate(bIsActivate); + } + + +} + +void UMJFadeObjectComponent::AddObjectHideTimer() +{ + UGameplayStatics::GetAllActorsOfClass(this, PlayerClass, CharacterArray); + + for (AActor* CurrentActor : CharacterArray) + { + const FVector TraceStart=GEngine->GetFirstLocalPlayerController(GetOwner()->GetWorld())->PlayerCameraManager->GetCameraLocation(); + const FVector TraceEnd = CurrentActor->GetActorLocation(); + FVector TraceLength = TraceStart - TraceEnd; + const FQuat AcQuat = CurrentActor->GetActorQuat(); + + if (TraceLength.Size() < WorkDistance) + { + FCollisionQueryParams TraceParams(TEXT("FadeObjectsTrace"), bIsTraceComplex, GetOwner()); + + TraceParams.AddIgnoredActors(ActorIgnore); + TraceParams.bReturnPhysicalMaterial = false; + TraceParams.bTraceComplex = bIsTraceComplex; + + TArrayHitArray; + TArray> traceObjectTypes; + + for (int i = 0; i < ObjectTypes.Num(); i++) + { + traceObjectTypes.Add(UEngineTypes::ConvertToObjectType(ObjectTypes[i].GetValue())); + } + GetOwner()->GetWorld()->SweepMultiByObjectType(HitArray, TraceStart, TraceEnd, AcQuat, traceObjectTypes, + FCollisionShape::MakeCapsule(CapsuleRadius, CapsuleHalfHeight), TraceParams); + + for (int IHit = 0; IHit < HitArray.Num(); IHit++) + { + if (HitArray[IHit].bBlockingHit && IsValid(HitArray[IHit].GetComponent()) + && !FadeObjectsHit.Contains(HitArray[IHit].GetComponent())) + { + FadeObjectsHit.AddUnique(HitArray[IHit].GetComponent()); + } + } + } + } + + for (int IObject = 0; IObject < FadeObjectsHit.Num(); IObject++) + { + if (!FadeObjectTemp.Contains(FadeObjectsHit[IObject])) + { + + FadeObjectTemp.AddUnique(FadeObjectsHit[IObject]); + + TArray IBaseMaterials; + TArray IMID; + + IBaseMaterials.Empty(); + IMID.Empty(); + + for (int Nm = 0; Nm < FadeObjectsHit[IObject]->GetNumMaterials(); Nm++) + { + IMID.Add(UMaterialInstanceDynamic::Create(FadeMaterial, FadeObjectsHit[IObject])); + IBaseMaterials.Add(FadeObjectsHit[IObject]->GetMaterial(Nm)); + + FadeObjectsHit[IObject]->SetMaterial(Nm, IMID.Last()); + + } + FFadeSystemStuc NewObject; + NewObject.NewElement(FadeObjectsHit[IObject], IBaseMaterials, IMID, ImmediatelyFade, true); + NewObject.CameraCollsion = FadeObjectsHit[IObject]->GetCollisionResponseToChannel(ECC_Camera); + + FadeObject.Add(NewObject); + + FadeObjectsHit[IObject]->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); + } + } + for (int Fot = 0; Fot < FadeObjectTemp.Num(); ++Fot) + { + if (!FadeObjectsHit.Contains(FadeObjectTemp[Fot])) + { + FadeObject[Fot].SetToHide(false); + } + } + FadeObjectsHit.Empty(); + +} + +void UMJFadeObjectComponent::FadeWorkTimer() +{ + if (FadeObject.Num() > 0) + { + for (int IObject = 0; IObject < FadeObject.Num(); ++IObject) + { + float AdaptiveFade; + int FniD = IObject; + + if (FniD == FadeObject.Num()) + { + AdaptiveFade = NearObjectFade; + } + else + { + AdaptiveFade = FarObjectFade; + } + + for (int i = 0; i < FadeObject[IObject].FadeMID.Num(); i++) + { + float TargetF; + const float CurrentF = FadeObject[IObject].FadeCount; + if (FadeObject[IObject].bToHide) + { + TargetF = AdaptiveFade; + } + else + { + TargetF = 1.0f; + } + const float NewFade = FMath::FInterpConstantTo(CurrentF, TargetF, GetOwner()->GetWorld()->GetDeltaSeconds(), FadeRate); + + FadeObject[IObject].FadeMID[i]->SetScalarParameterValue("Fade", NewFade); + CurrentFade = NewFade; + + FadeObject[IObject].SetHideAndFade(FadeObject[IObject].bToHide, NewFade); + } + if (CurrentFade == 1.0f) + { + for (int IBaseMat = 0; IBaseMat < FadeObject[FniD].BaseMatInterface.Num(); ++IBaseMat) + { + FadeObject[FniD].PrimitiveComp->SetMaterial(IBaseMat, FadeObject[FniD].BaseMatInterface[IBaseMat]); + } + + FadeObject[FniD].PrimitiveComp->SetCollisionResponseToChannel(ECC_Camera, FadeObject[FniD].CameraCollsion); + FadeObject.RemoveAt(FniD); + FadeObjectTemp.RemoveAt(FniD); + } + } + + } + +} + +void UMJFadeObjectComponent::SetEnable(bool _bIsEnable) +{ + bIsEnabled = _bIsEnable; +} + +void UMJFadeObjectComponent::SetActivate(bool _bIsActive) +{ + bIsActivate = _bIsActive; + + if (!bIsActivate) + { + GetOwner()->GetWorld()->GetTimerManager().PauseTimer(TimerHandle_ObjectComputeTimer); + GetOwner()->GetWorld()->GetTimerManager().PauseTimer(TimerHandle_AddObjectsTimer); + } + else + { + GetOwner()->GetWorld()->GetTimerManager().UnPauseTimer(TimerHandle_ObjectComputeTimer); + GetOwner()->GetWorld()->GetTimerManager().UnPauseTimer(TimerHandle_AddObjectsTimer); + } +} + + +// Called every frame +void UMJFadeObjectComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + // ... +} + diff --git a/Source/ProjectMJ/Component/MJFadeObjectComponent.h b/Source/ProjectMJ/Component/MJFadeObjectComponent.h new file mode 100644 index 00000000..2cac682d --- /dev/null +++ b/Source/ProjectMJ/Component/MJFadeObjectComponent.h @@ -0,0 +1,149 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "MJFadeObjectComponent.generated.h" +/* +* Class Description : Fade Object System +* Author : Lee JuHyeon +* Created Date : 2025_07_02 +* Last Modified By : +* Last Modified Date : +*/ + +class UMaterialInterface; + +USTRUCT(BlueprintType) +struct FFadeSystemStuc +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + TObjectPtr PrimitiveComp; + + UPROPERTY() + TArray BaseMatInterface; + + UPROPERTY() + TArray FadeMID; + + UPROPERTY() + float FadeCount; + + UPROPERTY() + bool bToHide; + + UPROPERTY() + TEnumAsByte CameraCollsion; + + void NewElement(TObjectPtr _Primitive, const TArray& _MaterialInt, const TArray& _MID, float _FadeCount, bool _bToHide); + + void SetToHide(bool _ToHide); + + void SetHideAndFade(bool _ToHide, float _FadeCount); + + void Destroy(); + FFadeSystemStuc() :PrimitiveComp(nullptr), FadeCount(0.0f), bToHide(true) {}; + +}; + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJFadeObjectComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + // Sets default values for this component's properties + UMJFadeObjectComponent(); + + // Called every frame + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + +protected: + // Called when the game starts + virtual void BeginPlay() override; + + void AddObjectHideTimer(); + + void FadeWorkTimer(); + + UFUNCTION(BlueprintCallable, Category = "Fade Object") + void SetEnable(bool _bIsEnable); + + UFUNCTION(BlueprintCallable, Category = "Fade Object") + void SetActivate(bool _bIsActive); + + TEnumAsByte MJTraceType = TraceTypeQuery1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Fade Object") + TArray ActorIgnore; + +private: + + TArrayFadeObject; + + FTimerHandle TimerHandle_ObjectComputeTimer; + FTimerHandle TimerHandle_AddObjectsTimer; + FTimerHandle TimerHandle_AddPlayersTimer; + + float CurrentFade; + + int32 FadeNowID; + + UPROPERTY() + TArray FadeObjectTemp; + + UPROPERTY() + TArray FadeObjectsHit; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + UMaterialInstance* FadeMaterial; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + bool bIsEnabled; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + bool bIsActivate; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + bool bIsTraceComplex; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float AddObjectInterval; + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float CalcFadeInterval; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float WorkDistance; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + TSubclassOf PlayerClass; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + TArray> ObjectTypes; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float FadeRate; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float CapsuleHalfHeight; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float CapsuleRadius; + + UPROPERTY() + TArray CharacterArray; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float NearObjectFade ; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float FarObjectFade ; + + UPROPERTY(EditAnywhere, Category = "Fade Objects") + float ImmediatelyFade; + +}; + diff --git a/Source/ProjectMJ/Controller/MJPlayerController.cpp b/Source/ProjectMJ/Controller/MJPlayerController.cpp index 53c1526e..4751ee42 100644 --- a/Source/ProjectMJ/Controller/MJPlayerController.cpp +++ b/Source/ProjectMJ/Controller/MJPlayerController.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #include "Controller/MJPlayerController.h" @@ -10,26 +10,54 @@ #include "Component/Input/MJInputComponent.h" #include "Character/MJPlayerCharacter.h" #include "Blueprint/AIBlueprintHelperLibrary.h" -#include "AbilitySystem/MJAbilitySystemComponent.h" -#include "MJGamePlayTags.h" -#include "Dialogue/MJDialogueComponent.h" #include "Components/SphereComponent.h" #include "UI/MJUIManagerSubsystem.h" +#include "Player/MJPlayerState.h" #include "ProjectMJ.h" -#include "Navigation/PathFollowingComponent.h" -#include "GameFramework/CharacterMovementComponent.h" -#include "Character/MJPlayerCharacter.h" -#include "Character/Component/MJSkillComponent.h" -#include "Compression/lz4.h" - +#include "Character/Component/MJPlayerSkillComponent.h" +#include "Dialogue/MJDialogueWidget.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/Button.h" +#include "DataTable/MJSkillDataRow.h" +#include "UI/Inventory/MJInventoryComponent.h" +#include "Item/MJItemBase.h" +#include "TG/UI/MJGameFlowHUDWidget.h" +#include "UI/MJHUDWidget.h" +#include "UI/Component/MJInteractComponent.h" +#include "UI/Skill/MJEquipedSkillWidget.h" +#include "UI/Skill/MJSkillWidget.h" +#include "UI/Store/MJMerchandiseSlot.h" +#include "UI/Store/MJPopupWidget.h" +#include "UI/Store/MJSalesSlot.h" +#include "UI/Store/MJStoreComponent.h" +#include "UI/Store/MJStoreWidget.h" +#include "UI/Skill/MJSkillSlotWidget.h" + + +// TODO: Input 관련한 로직들 Component로 따로 빼기 - 동민 - AMJPlayerController::AMJPlayerController() { bShowMouseCursor = true; DefaultMouseCursor = EMouseCursor::Default; - CachedDestination = FVector::ZeroVector; - FollowTime = 0.f; - bIsTouch=false; + + bIsLMBPressed = false; + bIsLMBHolding = false; + LMBHoldTime = 0.0f; + + bIsRMBPressed = false; + bIsRMBHolding = false; + RMBHoldTime = 0.0f; + + bIsCharge = false; + bShiftKeyDown = false; + + ChargeThreshold = 0.3f; +} + +void AMJPlayerController::PostInitializeComponents() +{ + Super::PostInitializeComponents(); } void AMJPlayerController::BeginPlay() @@ -38,15 +66,60 @@ void AMJPlayerController::BeginPlay() UIManager = GetGameInstance()->GetSubsystem(); ensure(UIManager); - // 언리얼 엔진의 초기화 순서 : GameInstance > GameMode > Actor - // 그러므로 GetSubsystem 시 nullptr 을 반환할 일은 없지만, ! - // 혹시 모를 상황(모듈 누락, 이상한 호출 타이밍, 비동기 로직 중 접근 등)에 대비하여 ensure() 또는 UE_LOG 찍기 AMJPlayerCharacter* MJChar = Cast(GetPawn()); if (MJChar) { - MJChar->GetDialogueTrigger()->OnComponentBeginOverlap.AddDynamic(this,&AMJPlayerController::OnTriggeredDialogueIn); - MJChar->GetDialogueTrigger()->OnComponentEndOverlap.AddDynamic(this,&AMJPlayerController::OnTriggeredDialogueOut); + MJChar->GetUITrigger()->OnComponentBeginOverlap.AddDynamic(this,&AMJPlayerController::OnTriggeredIn); + MJChar->GetUITrigger()->OnComponentEndOverlap.AddDynamic(this,&AMJPlayerController::OnTriggeredOut); + MJChar->GetUITrigger()->OnComponentBeginOverlap.AddDynamic(this,&ThisClass::OnTriggeredItemIn); + } + + GameFlowHUD = CastChecked(CreateWidget(this, GameFlowHUDWidgetClass)); + if (GameFlowHUD) + { + // TG : Do not change order of PC setter. + GameFlowHUD->SetPlayerController(this); + GameFlowHUD->AddToViewport(1); + } + UMJPlayerStatComponent* MJPlayerStatComp = GetPawn()->FindComponentByClass(); + if (MJPlayerStatComp) + { + MJPlayerStatComp->OnDeath.AddDynamic(this,&AMJPlayerController::OnDead); + } + + AMJPlayerState* State = GetPlayerState(); + if (State && MJPlayerStatComp) + { + UIManager->ShowHUD(State, this, MJPlayerStatComp); + UIManager->GetHUDWidget()->GetStoreWidget()->SetStatComponent(MJPlayerStatComp); + } + + TArray MerSlot = UIManager->GetHUDWidget()->GetStoreWidget()->GetMerchandiseSlots(); + for (int i = 0; i < MerSlot.Num(); i++) + { + if (MerSlot[i]) + { + MerSlot[i]->OnMerchandiseSlotEvent.AddDynamic(this, &AMJPlayerController::OnTryPurchase); + } + } + + if (UIManager->GetHUDWidget()->GetStoreWidget()) + { + UIManager->GetHUDWidget()->GetStoreWidget()->OnClickedPurchaseYes.AddDynamic(this,&AMJPlayerController::OnPurchase); + UIManager->GetHUDWidget()->GetStoreWidget()->OnClickedSellYes.AddDynamic(this,&AMJPlayerController::OnSell); + } + + if (UMJPlayerSkillComponent* SkillComponent = MJChar->FindComponentByClass()) + { + SkillComponent->OnLearnSkillEvent.AddDynamic(this,&AMJPlayerController::UpdateSkillWidget); + SkillComponent->OnLearnSkillEvents.AddDynamic(this,&AMJPlayerController::UpdateSkillSlot); + } + + for (int i = 0; i < 10; i++) + { + UIManager->GetHUDWidget()->GetSkillWidget()->GetSkillSlots()[i]->OnClickedEquipButton.AddDynamic(this,&AMJPlayerController::UpdateEquipedSkillWidget); + UIManager->GetHUDWidget()->GetSkillWidget()->GetSkillSlots()[i]->GetEquipButton()->OnClicked.AddDynamic(this,&ThisClass::GetOwnedSkill); } } @@ -56,253 +129,596 @@ void AMJPlayerController::SetupInputComponent() if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer())) { - Subsystem->AddMappingContext(InputConfigDataAsset->DefaultMappingContext, 0); + Subsystem->AddMappingContext(InputConfigDataAsset->GetDefaultMappingContext(), 0); } - UMJInputComponent* ProjectMJInputComponent = CastChecked< UMJInputComponent>(InputComponent); + UMJInputComponent* MJInputComponent = CastChecked< UMJInputComponent>(InputComponent); - ProjectMJInputComponent->BindNativeInputAction(InputConfigDataAsset, MJGameplayTags::Input_SetDestination_Click, ETriggerEvent::Started, this, &ThisClass::OnTouchStart); - ProjectMJInputComponent->BindNativeInputAction(InputConfigDataAsset, MJGameplayTags::Input_SetDestination_Click, ETriggerEvent::Completed, this, &ThisClass::OnTouchReleased); + MJInputComponent->BindAbilityInputAction(InputConfigDataAsset, this, &AMJPlayerController::AbilityInputPressed, &AMJPlayerController::AbilityInputReleased); - - ProjectMJInputComponent->BindAbilityInputAction(InputConfigDataAsset, this, &AMJPlayerController::Input_AbilityInputPressed, &AMJPlayerController::Input_AbilityInputReleased); + MJInputComponent->BindAction(ShiftAction, ETriggerEvent::Started, this, &AMJPlayerController::ShiftPressed); + MJInputComponent->BindAction(ShiftAction, ETriggerEvent::Completed, this, &AMJPlayerController::ShiftReleased); //Dialogue Input - ProjectMJInputComponent->BindAction(ChangeIMCAction, ETriggerEvent::Triggered, this, &ThisClass::ChangeToIMCDialogue); - ProjectMJInputComponent->BindAction(NextDialogueAction, ETriggerEvent::Triggered, this, &ThisClass::ProceedDialogue); - ProjectMJInputComponent->BindAction(ShowBacklogAction, ETriggerEvent::Triggered, this, &ThisClass::ShowBacklog); - + MJInputComponent->BindAction(ChangeIMCAction, ETriggerEvent::Triggered, this, &ThisClass::StartDialogue); + MJInputComponent->BindAction(NextDialogueAction, ETriggerEvent::Triggered, this, &ThisClass::ProceedDialogue); + MJInputComponent->BindAction(ShowBacklogAction, ETriggerEvent::Triggered, this, &ThisClass::ShowBacklog); + + // UI Input + MJInputComponent->BindAction(ShowInventoryAction, ETriggerEvent::Triggered, this, &ThisClass::ShowInventory); + MJInputComponent->BindAction(ShowStatPanelAction, ETriggerEvent::Triggered, this, &ThisClass::ShowStatPanel); + MJInputComponent->BindAction(ShowSkillWidgetAction, ETriggerEvent::Triggered, this, &ThisClass::ShowSkillWidget); + MJInputComponent->BindAction(PauseAction, ETriggerEvent::Triggered, this, &ThisClass::PauseGame); } + void AMJPlayerController::PlayerTick(float DeltaTime) { Super::PlayerTick(DeltaTime); - if (bIspressed && !bIsHolding) + + if (bIsLMBPressed && !bIsLMBHolding) { - PressTimed += DeltaTime; - if (PressTimed >= HoldThresHold) - { - bIsHolding = true; - StopMove(); - } + bIsLMBHolding = true; } - if (bIsHolding) + + if (bIsLMBHolding) { - HoldingMove(); + LMBHoldTime += DeltaTime; + HandleLeftMouseHold(); + } + + if (bIsRMBPressed && !bIsRMBHolding) + { + bIsRMBHolding = true; + } + + if (bIsRMBHolding) + { + RMBHoldTime += DeltaTime; + HandleRightMouseHold(); } } -void AMJPlayerController::StopMove() +void AMJPlayerController::OnLeftMousePressed() { - StopMovement(); -} + bIsLMBPressed = true; + LMBHoldTime = 0.0f; + bIsLMBHolding = false; + if (bShiftKeyDown) + { + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) + { + return; + } + + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + + FGameplayTag BasicAttackTag = FGameplayTag::RequestGameplayTag(FName("Skill.Normal")); + SkillComponent->ActivateSkillByInputTag(BasicAttackTag); + } +} -void AMJPlayerController::HoldingMove() +void AMJPlayerController::OnLeftMouseReleased() { - //FollowTime = 0.f; - FHitResult Hit; + if (!bIsLMBHolding) + { + FHitResult HitResult; + if (GetHitResultUnderCursor(ECC_Visibility, false, HitResult)) + { + AttackOrMove(HitResult); + } + } + + bIsLMBPressed = false; + bIsLMBHolding = false; +} - if (GetHitResultUnderCursor(ECC_Visibility, false, Hit)) +void AMJPlayerController::HandleLeftMouseHold() +{ + // 꾹 눌렀을 때는 몹 위에 마우스 있어도 이동이 맞음 - 동민 - + FHitResult HitResult; + if (GetHitResultUnderCursor(ECC_Visibility, false, HitResult)) { - FVector MoveDir = Hit.ImpactPoint - GetPawn()->GetActorLocation(); - MoveDir.Z = 0; - MoveDir.Normalize(); + if (bShiftKeyDown) + { + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) + { + return; + } + + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + + FGameplayTag BasicAttackTag = FGameplayTag::RequestGameplayTag(FName("Skill.Normal")); + SkillComponent->ActivateSkillByInputTag(BasicAttackTag); + StopMovement(); - if (!MoveDir.IsNearlyZero()) + } + else { - GetPawn()->AddMovementInput(MoveDir, 1.0f); + UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, HitResult.Location); + } } } -void AMJPlayerController::OnTouchStart() +void AMJPlayerController::OnRightMousePressed() { - //bIsTouch = true; - bIspressed = true; - bIsHolding = false; - PressTimed = 0.0f; + bIsRMBPressed = true; + bIsRMBHolding = false; + bIsCharge = false; + RMBHoldTime = 0.0f; } -void AMJPlayerController::OnTouchReleased() +void AMJPlayerController::OnRightMouseReleased() { - //bIsTouch = false; - const float TraceOffsetZ = 10.0f; - const float TraceDepthZ = 1000.0; + StopMovement(); - if (!bIsHolding) + + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) { - FHitResult Hit; - - FVector WorldOrigin, WorldDirection; + return; + } - if (GetHitResultUnderCursor(ECC_Visibility, false, Hit)) - { - if (DeprojectMousePositionToWorld(WorldOrigin, WorldDirection)) - { - FVector TraceStart = WorldOrigin; - FVector TraceEnd = TraceStart + WorldDirection * 10000.0f; - - TArray HitResults; - FCollisionQueryParams TraceParams; - TraceParams.AddIgnoredActor(GetPawn()); - - bool bHit = GetWorld()->LineTraceMultiByChannel( - HitResults, - TraceStart, - TraceEnd, - ECC_Visibility, - TraceParams - ); - DrawDebugLine(GetWorld(), TraceStart, TraceEnd, FColor::Red, false, 1.5f); - for (const FHitResult& Hits : HitResults) - { - UE_LOG(LogTemp, Warning, TEXT("%d"), HitResults.Num()); - AActor* HitActor = Hits.GetActor(); - if (!HitActor) - { - continue; - } - - if (HitActor->ActorHasTag("BlockClick")) - { - continue; - } - - if (HitActor->ActorHasTag("Ground") || Hits.Component->GetCollisionObjectType() == ECC_WorldStatic) - { - UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, Hits.ImpactPoint); - break; - } - } - } + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + MJ_LOG(LogMJ, Warning, TEXT("%f"), RMBHoldTime); - CachedDestination = Hit.Location; - UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, CachedDestination); - } + if (!bIsCharge) + { + + FGameplayTag InstantSkillTag = FGameplayTag::RequestGameplayTag(FName("Skill.Instant")); + SkillComponent->ActivateSkillByInputTag(InstantSkillTag); } else { - StopMove(); + FGameplayTag ChargeSkillTag = FGameplayTag::RequestGameplayTag(FName("Skill.Charge")); + SkillComponent->ReleaseSkillByInputTag(ChargeSkillTag); } - bIspressed = false; - bIsHolding = false; - PressTimed = 0.0f; + bIsRMBPressed = false; + bIsRMBHolding = false; + bIsCharge = false; + RMBHoldTime = 0.f; } +void AMJPlayerController::HandleRightMouseHold() +{ + if (!bIsCharge && RMBHoldTime >= ChargeThreshold) + { + bIsCharge = true; + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) + { + return; + } -void AMJPlayerController::ChangeToIMCDialogue() + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + + FGameplayTag ChargeSkillTag = FGameplayTag::RequestGameplayTag(FName("Skill.Charge")); + SkillComponent->ActivateSkillByInputTag(ChargeSkillTag); + } +} + +void AMJPlayerController::AttackOrMove(const FHitResult& HitResult) { - if (IsTriggered) + if (bShiftKeyDown) { - AMJPlayerCharacter* MyChar = Cast(GetPawn()); - if (!MyChar) return; - AActor* DialogueTarget = MyChar->GetDialogueTarget(); - if (!DialogueTarget) return; - UMJDialogueComponent* DialogueComp = DialogueTarget->GetComponentByClass(); - if (!DialogueComp) return; - - UIManager->ShowDialogue(DialogueComp); - - if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer())) - { - Subsystem->AddMappingContext(InputConfigDataAsset->DialogueMappingContext, 0); - Subsystem->RemoveMappingContext(InputConfigDataAsset->DefaultMappingContext); - } + // Shift + Left Click은 OnLeftMousePressed에서 처리되므로 여기서는 아무것도 하지 않습니다. + return; + } + + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) + { + return; + } + + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + + AMJCharacterBase* TargetCharacter = Cast(HitResult.GetActor()); + if (!TargetCharacter) + { + UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, HitResult.Location);; + } + else if (TargetCharacter != ControlledCharacter && TargetCharacter->GetGenericTeamId() != ControlledCharacter->GetGenericTeamId()) + { + MJ_LOG(LogMJ, Warning, TEXT("NormalAttack")); + FGameplayTag LeftClickInputTag = FGameplayTag::RequestGameplayTag(FName("Skill.Normal")); + SkillComponent->ActivateSkillByInputTag(LeftClickInputTag); + } + else + { + UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, HitResult.Location); } } -void AMJPlayerController::ChangeToIMCDefault() // showDialogue 마지막에 들어가야 함 +void AMJPlayerController::AbilityInputPressed(FGameplayTag InInputTag) { - if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer())) + AMJPlayerCharacter* ControlledCharacter = Cast(GetPawn()); + if (!ControlledCharacter) + { + return; + } + + UMJPlayerSkillComponent* SkillComponent = ControlledCharacter->FindComponentByClass(); + if (!SkillComponent) + { + return; + } + + if (InInputTag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Input.Mouse.Left.Pressed")))) { - Subsystem->AddMappingContext(InputConfigDataAsset->DefaultMappingContext, 0); - Subsystem->RemoveMappingContext(InputConfigDataAsset->DialogueMappingContext); + OnLeftMousePressed(); + } + else if (InInputTag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Input.Mouse.Right.Pressed")))) + { + OnRightMousePressed(); + } + else + { + } } -void AMJPlayerController::ProceedDialogue() +void AMJPlayerController::AbilityInputReleased(FGameplayTag InInputTag) { - if (IsTriggered) + if (InInputTag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Input.Mouse.Left.Released")))) + { + OnLeftMouseReleased(); + } + else if (InInputTag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Input.Mouse.Right.Released")))) { - AMJPlayerCharacter* MyChar = Cast(GetPawn()); - if (!MyChar) return; - AActor* DialogueTarget = MyChar->GetDialogueTarget(); - if (!DialogueTarget) return; - UMJDialogueComponent* DialogueComp = DialogueTarget->GetComponentByClass(); - if (!DialogueComp) return; + OnRightMouseReleased(); + } + else + { + + } +} - UIManager->NextDialogue(DialogueComp); +void AMJPlayerController::ShiftPressed() +{ + bShiftKeyDown = true; +} + +void AMJPlayerController::ShiftReleased() +{ + bShiftKeyDown = false; +} + +void AMJPlayerController::StartDialogue()// x키를 눌렀을 때 실행되는 함수 +{ + if (!IsInteracted) return; + + AMJPlayerCharacter* MyChar = Cast(GetPawn()); + if (!MyChar) return; + + if (UMJInteractComponent* InteractComp = MyChar->GetUITarget()->FindComponentByClass()) + { - if (DialogueComp->IsDialogueEnd()) // 마지막 대사라면 imc 전환 + InteractComp->StartInteraction(); + switch (InteractComp->CurrentType) { - ChangeToIMCDefault(); + case EMJInteractionType::Dialogue: + ChangeToIMCDialogue(); + UIManager->SetDialogueVisibility(); + break; + + case EMJInteractionType::Store: + ChangeToIMCDialogue(); + UIManager->SetDialogueVisibility(); + break; + + default: + break; } + } +} + +void AMJPlayerController::ChangeToIMCDialogue() +{ + StopMovement(); + if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer())) + { + Subsystem->AddMappingContext(InputConfigDataAsset->GetDialogueMappingContext(), 0); + Subsystem->RemoveMappingContext(InputConfigDataAsset->GetDefaultMappingContext()); + } +} + +void AMJPlayerController::ChangeToIMCDefault() +{ + if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer())) + { + Subsystem->AddMappingContext(InputConfigDataAsset->GetDefaultMappingContext(), 0); + Subsystem->RemoveMappingContext(InputConfigDataAsset->GetDialogueMappingContext()); + } +} + +void AMJPlayerController::ProceedDialogue() +{ + AMJPlayerCharacter* MyChar = Cast(GetPawn()); + if (!MyChar) + { + return; + } + if (!MyChar->GetUITarget()) + { + return; + } + + UMJInteractComponent* InteractComp = MyChar->GetUITarget()->FindComponentByClass(); + if (!InteractComp) + { + return; } + InteractComp->ProceedInteraction(); +} + +void AMJPlayerController::SetDialogueVisibility() +{ + ChangeToIMCDefault(); + UIManager->SetDialogueVisibility(); +} + +void AMJPlayerController::ShowStore() +{ + UIManager->SetDialogueVisibility(); + UIManager->ShowStore(); +} + +void AMJPlayerController::HideStore() +{ + UIManager->ShowStore(); + ChangeToIMCDefault(); + UE_LOG(LogTemp, Display, TEXT("AMJPlayerController::HideStore")); } void AMJPlayerController::ShowBacklog() { - UIManager->ShowBacklog(); + UIManager->GetHUDWidget()->GetDialogueWidget()->ShowBacklog(); } -void AMJPlayerController::OnTriggeredDialogueIn(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, - int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) +void AMJPlayerController::ShowStatPanel() { - UE_LOG(LogTemp, Log, TEXT("OnTriggerBegin")); - AMJPlayerCharacter* MJChar = Cast(GetPawn()); - if ( Other && Other->FindComponentByClass()) + UIManager->ShowStatPanel(); +} + +void AMJPlayerController::ShowInventory() +{ + UIManager->ShowInventory(); +} + +void AMJPlayerController::ShowSkillWidget() +{ + UIManager->SetSkillWidgetVisibility(); +} + +void AMJPlayerController::UpdateSkillWidget(FGameplayTag SkillTag,int32 Level) +{ + UIManager->GetHUDWidget()->GetSkillWidget()->UpdateSkillSlots(SkillTag,Level); +} + +void AMJPlayerController::UpdateEquipedSkillWidget(UTexture2D* Icon, ESkillType SkillType, FGameplayTag Tag) +{ + if (SkillType == ESkillType::Instant) { - MJChar->SetDialogueTarget(Other); - IsTriggered = true; + UIManager->GetHUDWidget()->GetEquipedSkillWidget()->SetInstantImage(Icon); } + if (SkillType == ESkillType::Passive) + { + UIManager->GetHUDWidget()->GetEquipedSkillWidget()->SetPassiveImage(Icon); + } + if (SkillType == ESkillType::Charge) + { + UIManager->GetHUDWidget()->GetEquipedSkillWidget()->SetChargingImage(Icon); + } + + TempTag = Tag; } -void AMJPlayerController::OnTriggeredDialogueOut(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, - int32 OtherBodyIndex) +void AMJPlayerController::GetOwnedSkill() { AMJPlayerCharacter* MJChar = Cast(GetPawn()); - if (MJChar->GetDialogueTarget()== Other) + if (UMJPlayerSkillComponent* SkillComponent = MJChar->FindComponentByClass()) { - MJChar->SetDialogueTarget(nullptr); - IsTriggered = false; + SkillComponent->EquipSkill(TempTag); + UE_LOG(LogTemp,Error,TEXT("AMJPlayerController::GetOwnedSkill, %s"),*TempTag.ToString()); } } +void AMJPlayerController::UpdateSkillSlot(FGameplayTag SkillTag) +{ + UIManager->GetHUDWidget()->GetEquipedSkillWidget()->SetAllImage(SkillTag); +} -void AMJPlayerController::Input_AbilityInputPressed(FGameplayTag InInputTag) +void AMJPlayerController::OnTriggeredIn(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, + int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { - MJ_LOG(LogMJ, Warning, TEXT("Input Pressed: %s"), *InInputTag.ToString()) - AMJPlayerCharacter* ControlledPawn = Cast(GetPawn()); - if (ControlledPawn) + if (Other) { - if (UMJAbilitySystemComponent* MJASC = Cast(ControlledPawn->GetAbilitySystemComponent())) + if (UMJInteractComponent* InteractComp = Other->FindComponentByClass()) { + IsInteracted = true; + InteractComp->OnBeginInteract(); + + InteractComp->OndialogueEnd.RemoveDynamic(this, &AMJPlayerController::SetDialogueVisibility); + InteractComp->OndialogueEnd.AddDynamic(this, &AMJPlayerController::SetDialogueVisibility); + + InteractComp->OnstoreOpen.RemoveDynamic(this, &AMJPlayerController::ShowStore); + InteractComp->OnstoreOpen.AddDynamic(this, &AMJPlayerController::ShowStore); + + InteractComp->OnstoreClose.RemoveDynamic(this, &AMJPlayerController::HideStore); + InteractComp->OnstoreClose.AddDynamic(this, &AMJPlayerController::HideStore); - MJASC->OnAbilityInputPressed(InInputTag); - if (UMJSkillComponent* SkillComponent = ControlledPawn->FindComponentByClass()) + if (AMJPlayerCharacter* MJChar = Cast(GetPawn())) + { + MJChar->SetUITarget(Other); + if (InteractComp->GetStoreComponent()) + { + InteractComp->GetStoreComponent()->SetItemData(MJChar->GetInventoryComponent()->GetItemTags(),MJChar->GetInventoryComponent()->GetItemTags().Num(),MJChar->GetInventoryComponent()); + } + } + } + } +} + +void AMJPlayerController::OnTriggeredOut(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, + int32 OtherBodyIndex) +{ + if (Other) + { + if (UMJInteractComponent* InteractComp = Other->FindComponentByClass()) + { + IsInteracted = false; + InteractComp->OnEndInteract(); + + if (AMJPlayerCharacter* MJChar = Cast(GetPawn())) { - SkillComponent->ActivateSkillByInputTag(InInputTag); + MJChar->SetUITarget(nullptr); } } } +} +void AMJPlayerController::OnTriggeredItemIn(UPrimitiveComponent* Overlapped, AActor* Other, + UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) +{ + AMJItemBase* Item = Cast(Other); + if (!Item) return; + UE_LOG(LogTemp,Error,TEXT("AMJPlayerController::OnTriggeredItemIn")); + AMJPlayerCharacter* MyChar = Cast(GetPawn()); + if (!MyChar) return; + UMJInventoryComponent* InventoryComp = MyChar->GetInventoryComponent(); + if (InventoryComp) + { + InventoryComp->PickUpItem(Item->GetItemTag(), 1, UIManager->GetHUDWidget()->GetStoreWidget()); + + TArray SalesSlot = UIManager->GetHUDWidget()->GetStoreWidget()->GetInventorySlots(); + for (int i = 0; i < SalesSlot.Num(); i++) + { + if (SalesSlot[i]) + { + SalesSlot[i]->OnMerchandiseSlotEvent.RemoveDynamic(this, &AMJPlayerController::OnTrySell); + SalesSlot[i]->OnMerchandiseSlotEvent.AddDynamic(this, &AMJPlayerController::OnTrySell); + UE_LOG(LogTemp,Error,TEXT("%d개의 슬롯에 델리게이트 연결됨"),SalesSlot.Num()); + } + } + + Item->Destroy(); + } +} + +void AMJPlayerController::OnTryPurchase(FGameplayTag& ItemTag, int32 Price, int32 Quantity) // GET Item Data +{ + UE_LOG(LogTemp,Error,TEXT("AMJPlayerController::OnTryPurchase")); + PurchaseItemTag = ItemTag; + ItemPrice = Price; + ItemQuantity = Quantity; } -void AMJPlayerController::Input_AbilityInputReleased(FGameplayTag InInputTag) +void AMJPlayerController::OnTrySell(FGameplayTag& ItemTag, int32 Price, int32 Quantity) { - AMJPlayerCharacter* ControlledPawn = Cast(GetPawn()); - if (ControlledPawn) + UE_LOG(LogTemp,Error,TEXT("AMJPlayerController::OnTrySell")); + SalesItemTag = ItemTag; + SalesPrice = Price; + SalesQuantity = Quantity; +} + +void AMJPlayerController::OnPurchase() +{ + AMJPlayerCharacter* MyChar = Cast(GetPawn()); + if (!MyChar) return; + + UMJPlayerStatComponent* StatComp = GetPawn()->FindComponentByClass(); + UMJInventoryComponent* InventoryComp = MyChar->GetInventoryComponent(); + + if (StatComp && InventoryComp) + { + if (StatComp->GetGold() >= ItemQuantity * ItemPrice) + { + InventoryComp->PickUpItem(PurchaseItemTag, ItemQuantity, UIManager->GetHUDWidget()->GetStoreWidget()); // 인벤토리 내 수량증가 + UMJStoreComponent* StoreComp = MyChar->GetUITarget()->FindComponentByClass(); + StoreComp->SetItemData(MyChar->GetInventoryComponent()->GetItemTags(),MyChar->GetInventoryComponent()->GetItemTags().Num(),InventoryComp); + StatComp->SpendGold(ItemQuantity * ItemPrice); + for (auto* Slot : UIManager->GetHUDWidget()->GetStoreWidget()->GetMerchandiseSlots()) + { + if (Slot) + { + Slot->InitializeQuantity(); // 구매 완료 후 구매 희망수량 초기화 + } + } + } + else + { + // storewidget이 잔액 부족을 띄우게 한다. + return; + } + } +} + +void AMJPlayerController::OnSell() +{ + AMJPlayerCharacter* MyChar = Cast(GetPawn()); + if (!MyChar) return; + + UMJPlayerStatComponent* StatComp = GetPawn()->FindComponentByClass(); + UMJInventoryComponent* InventoryComp = MyChar->GetInventoryComponent(); + UMJStoreComponent* StoreComp = MyChar->GetUITarget()->FindComponentByClass(); + if (StatComp && InventoryComp) { - if (UMJAbilitySystemComponent* MJASC = Cast(ControlledPawn->GetAbilitySystemComponent())) - { + if (InventoryComp->GetItemInInventory()[SalesItemTag].ItemCount > SalesQuantity) + { + StatComp->GainGold(SalesQuantity * SalesPrice); + } + + InventoryComp->DropItem(SalesItemTag,SalesQuantity); + StoreComp->UpdateInventory(InventoryComp); - MJASC->OnAbilityInputReleased(InInputTag); + for (auto* Slot : UIManager->GetHUDWidget()->GetStoreWidget()->GetInventorySlots()) + { + if (Slot) + { + Slot->InitializeQuantity(); // 판매 완료 후 판매 수량 초기화 + } } } } + +void AMJPlayerController::PauseGame() +{ + GameFlowHUD->PauseGame(); +} + +void AMJPlayerController::OnDead(AActor* InEffectCauser) +{ + // TODO 태관 : StatComponent에서 델리게이트 로 호출해서 입력 막고 UI 띄울 예정 + DisableInput(this); + SetPause(true); +} diff --git a/Source/ProjectMJ/Controller/MJPlayerController.h b/Source/ProjectMJ/Controller/MJPlayerController.h index f7a7ff87..ebf149af 100644 --- a/Source/ProjectMJ/Controller/MJPlayerController.h +++ b/Source/ProjectMJ/Controller/MJPlayerController.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #pragma once @@ -7,6 +7,8 @@ #include "GameplayTagContainer.h" #include "MJPlayerController.generated.h" + +class UMJGameFlowHUDWidget; class UDataAsset_InputConfig; class UInputAction; class UMJUIManagerSubsystem; @@ -14,9 +16,13 @@ class UMJUIManagerSubsystem; /** * Class Description: * Author: Lee JuHyeon - * Created Date: Create Controller - * Last Modified By: Lee JuHyeon - * Last Modified Date: Change Input And Move Actor beHind + * Created Date: ? + * Modified By: Lee Jisoo + * Modified Date: 2025.06.25(BeginPlay에 ShowHUD 추가) + * --- + * Modified By: 신동민 + * Modified Date: 2025.07.14 + * Modified Description: 이동과 공격 관련 Input */ UCLASS() @@ -27,74 +33,172 @@ class PROJECTMJ_API AMJPlayerController : public APlayerController AMJPlayerController(); protected: + virtual void PostInitializeComponents() override; virtual void BeginPlay() override; virtual void SetupInputComponent() override; - - virtual void PlayerTick(float DeltaTime)override; -#pragma region move and Input - void StopMove(); - void HoldingMove(); - void OnTouchStart(); - void OnTouchReleased(); - + virtual void PlayerTick(float DeltaTime) override; +public: + void OnLeftMousePressed(); + void OnLeftMouseReleased(); + void HandleLeftMouseHold(); - void Input_AbilityInputPressed(FGameplayTag InInputTag); - void Input_AbilityInputReleased(FGameplayTag InInputTag); - -#pragma endregion + void OnRightMousePressed(); + void OnRightMouseReleased(); + void HandleRightMouseHold(); + void AttackOrMove(const FHitResult& HitResult); + + void AbilityInputPressed(FGameplayTag InInputTag); + void AbilityInputReleased(FGameplayTag InInputTag); + + void ShiftPressed(); + void ShiftReleased(); private: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "CharacterData", meta = (AllowPrivateAccess = "true")) - UDataAsset_InputConfig* InputConfigDataAsset; + bool bIsLMBPressed = false; + bool bIsLMBHolding = false; + float LMBHoldTime = 0.0f; - FVector CachedDestination; + bool bIsRMBPressed = false; + bool bIsRMBHolding = false; + float RMBHoldTime = 0.0f; - bool bIsTouch; // Is it a touch device - float FollowTime; // For how long it has been pressed + bool bIsCharge = false; - bool bIsHolding; - bool bIspressed; + bool bShiftKeyDown = false; - float HoldThresHold = 0.2f; - float PressTimed = 0.0f; - -#pragma region DialoguePart -private: - bool IsTriggered = false; + UPROPERTY(EditDefaultsOnly, Category = "Input") + float ChargeThreshold = 0.4f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inpur", meta = (AllowPrivateAccess = "true")) + UDataAsset_InputConfig* InputConfigDataAsset; + UPROPERTY(EditAnywhere, Category = "Input") + TObjectPtr ShiftAction; +#pragma region UIPart +private: + bool IsInteracted = false; + FGameplayTag PurchaseItemTag; + int32 ItemPrice; + int32 ItemQuantity; + + FGameplayTag SalesItemTag; + int32 SalesPrice; + int32 SalesQuantity; public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) - UInputAction* ChangeIMCAction; + TObjectPtr ChangeIMCAction; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) - UInputAction* NextDialogueAction; + TObjectPtr NextDialogueAction; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) - UInputAction* ShowBacklogAction; + TObjectPtr ShowBacklogAction; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) + TObjectPtr ShowStatPanelAction; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) + TObjectPtr ShowInventoryAction; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) + TObjectPtr ShowStoreAction; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input) + TObjectPtr ShowSkillWidgetAction; + UPROPERTY() - UMJUIManagerSubsystem* UIManager; + TObjectPtr UIManager; + + //Dialogue + UFUNCTION() + void StartDialogue(); - // 이것들이 작동하면 아래 4개 함수는 지워도된다 + UFUNCTION() void ChangeToIMCDialogue(); + + UFUNCTION() void ChangeToIMCDefault(); + + UFUNCTION() void ProceedDialogue(); + UFUNCTION() + void SetDialogueVisibility(); + // Store + UFUNCTION() + void ShowStore(); + UFUNCTION() + void HideStore(); + + // Show Widget + UFUNCTION() void ShowBacklog(); - UFUNCTION() - void OnTriggeredDialogueIn(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, + void ShowStatPanel(); + UFUNCTION() + void ShowInventory(); + UFUNCTION() + void ShowSkillWidget(); + UFUNCTION() + void UpdateSkillWidget(FGameplayTag SkillTag,int32 Level); + UFUNCTION() + void UpdateEquipedSkillWidget(UTexture2D* Icon, ESkillType SkillType, FGameplayTag Tag); + UFUNCTION() + void GetOwnedSkill(); + UFUNCTION() + void UpdateSkillSlot(FGameplayTag SkillTag); + + FGameplayTag TempTag; + + UFUNCTION() + void OnTriggeredIn(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UFUNCTION() - void OnTriggeredDialogueOut(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, + void OnTriggeredOut(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex); - + UFUNCTION() + void OnTriggeredItemIn(UPrimitiveComponent* Overlapped, AActor* Other, UPrimitiveComponent* OtherComp, + int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); + + UFUNCTION() + void OnTryPurchase(FGameplayTag& ItemTag, int32 Price, int32 Quantity); + + UFUNCTION() + void OnTrySell(FGameplayTag& ItemTag, int32 Price, int32 Quantity); + + UFUNCTION() + void OnPurchase(); + + UFUNCTION() + void OnSell(); #pragma endregion - // Active Ability - void Input_InstantSkillPressed(FGameplayTag InInputTag); - void Input_InstantSkillReleased(FGameplayTag InInputTag); + +public: + + UFUNCTION(BlueprintCallable) + UMJGameFlowHUDWidget* GetGameFlowHUD() {return GameFlowHUD; } + +protected: + UPROPERTY() + TObjectPtr GameFlowHUD; + + UPROPERTY(EditDefaultsOnly, Category = UI) + TSubclassOf GameFlowHUDWidgetClass; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = UI) + UInputAction* PauseAction; + // + UFUNCTION() + void PauseGame(); + + + +protected: + UFUNCTION() + void OnDead(AActor* InEffectCauser); + }; diff --git a/Source/ProjectMJ/DM/MJNonPlayerBase.cpp b/Source/ProjectMJ/DM/MJNonPlayerBase.cpp new file mode 100644 index 00000000..ded9d034 --- /dev/null +++ b/Source/ProjectMJ/DM/MJNonPlayerBase.cpp @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DM/MJNonPlayerBase.h" + +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" + +AMJNonPlayerBase::AMJNonPlayerBase() +{ + ASC = CreateDefaultSubobject(TEXT("Ability System Component")); + AttributeSet = CreateDefaultSubobject(TEXT("Character AttributeSet")); + SkillAttributeSet = CreateDefaultSubobject(TEXT("Skill AttributeSet")); +} + +void AMJNonPlayerBase::PossessedBy(AController* NewController) +{ + Super::PossessedBy(NewController); + + if (!ASC) + { + return; + } + + ASC->InitAbilityActorInfo(this, this); + +} diff --git a/Source/ProjectMJ/DM/MJNonPlayerBase.h b/Source/ProjectMJ/DM/MJNonPlayerBase.h new file mode 100644 index 00000000..abb61827 --- /dev/null +++ b/Source/ProjectMJ/DM/MJNonPlayerBase.h @@ -0,0 +1,45 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Character/MJCharacterBase.h" +#include "MJNonPlayerBase.generated.h" + +/** + * Class Description: Test NPC + * Author: 신동민 + * Created Date: 2025.07.07 + * Last Modified By: + * Last Modified Date: + */ + +class UMJCharacterSkillAttributeSet; +class UMJCharacterAttributeSet; +class UAbilitySystemComponent; +class UGameplayEffect; + +UCLASS() +class PROJECTMJ_API AMJNonPlayerBase : public AMJCharacterBase +{ + GENERATED_BODY() + +public: + AMJNonPlayerBase(); + + //virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; + virtual void PossessedBy(AController* NewController) override; + +protected: + + + UPROPERTY(EditAnywhere, Category = GAS) + TObjectPtr AttributeSet; + + UPROPERTY(EditAnywhere, Category = GAS) + TObjectPtr SkillAttributeSet; + + UPROPERTY(EditAnywhere, Category = GAS) + TSubclassOf InitStatEffect; + +}; diff --git a/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.cpp b/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.cpp index 8507154d..db43ba03 100644 --- a/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.cpp +++ b/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.cpp @@ -15,6 +15,18 @@ UInputAction* UDataAsset_InputConfig::FindNativeInputActionByTag(const FGameplay return nullptr; } +UInputAction* UDataAsset_InputConfig::FindAbilityInputActionByTag(const FGameplayTag& InInputTag) const +{ + for (const FMJInputActionConfig& InputActionConfig : AbilityInputActions) + { + if (InputActionConfig.InputTag == InInputTag && InputActionConfig.InputAction) + { + return InputActionConfig.InputAction; + } + } + return nullptr; +} + bool FMJInputActionConfig::IsVaild() const { return InputTag.IsValid() && InputAction; diff --git a/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.h b/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.h index 56baf7eb..355bf4b1 100644 --- a/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.h +++ b/Source/ProjectMJ/DataAsset/DataAsset_InputConfig.h @@ -9,22 +9,20 @@ class UInputAction; class UInputMappingContext; + USTRUCT(BlueprintType) struct FMJInputActionConfig { GENERATED_BODY() -public: - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly,meta=(Categories="InputTag")) + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(Categories="Input")) FGameplayTag InputTag; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TObjectPtr InputAction; - bool IsVaild()const; + bool IsVaild() const; - }; /** @@ -34,24 +32,31 @@ struct FMJInputActionConfig * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ + UCLASS() class PROJECTMJ_API UDataAsset_InputConfig : public UDataAsset { GENERATED_BODY() + + UPROPERTY(EditDefaultsOnly) + TObjectPtr DefaultMappingContext; -public: - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - TObjectPtrDefaultMappingContext; - - UPROPERTY(EditAnywhere, BlueprintReadOnly) + UPROPERTY(EditDefaultsOnly) TObjectPtr DialogueMappingContext; - UPROPERTY(EditDefaultsOnly,BlueprintReadOnly, meta=(TitleProperty=InputAction)) + UPROPERTY(EditDefaultsOnly, meta = (TitleProperty = InputTag)) TArray NativeInputActions; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = InputAction)) - TArrayAbilityInputAction; + UPROPERTY(EditDefaultsOnly, meta = (TitleProperty = InputTag)) + TArray AbilityInputActions; + +public: + UInputMappingContext* GetDefaultMappingContext() const { return DefaultMappingContext.Get(); } + UInputMappingContext* GetDialogueMappingContext() const { return DialogueMappingContext.Get(); } + const TArray& GetNativeInputActions() const { return NativeInputActions; } + const TArray& GetAbilityInputActions() const { return NativeInputActions; } UInputAction* FindNativeInputActionByTag(const FGameplayTag& InInputTag) const; + UInputAction* FindAbilityInputActionByTag(const FGameplayTag& InInputTag) const; }; diff --git a/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.cpp b/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.cpp new file mode 100644 index 00000000..7e3535c4 --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.cpp @@ -0,0 +1,8 @@ +// ThenOneDayStudio + + +#include "DataAsset/MJInnateGameplayEffectDataAsset.h" + +UMJInnateGameplayEffectDataAsset::UMJInnateGameplayEffectDataAsset() +{ +} diff --git a/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.h b/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.h new file mode 100644 index 00000000..e704d9e5 --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJInnateGameplayEffectDataAsset.h @@ -0,0 +1,29 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "MJInnateGameplayEffectDataAsset.generated.h" + +class UGameplayEffect; +/** + * Class Description: 시작 시 부여할 Effect + * Author: 신동민 + * Created Date: 2025.08.12 + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJInnateGameplayEffectDataAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + UMJInnateGameplayEffectDataAsset(); + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray> Effects; + +}; diff --git a/Source/ProjectMJ/DataAsset/MJItemDataAsset.cpp b/Source/ProjectMJ/DataAsset/MJItemDataAsset.cpp new file mode 100644 index 00000000..2d838750 --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJItemDataAsset.cpp @@ -0,0 +1,17 @@ +// ThenOneDayStudio + + +#include "DataAsset/MJItemDataAsset.h" + +UMJItemDataAsset::UMJItemDataAsset() +{ +} + +TSubclassOf UMJItemDataAsset::FindItemClassForTag(const FGameplayTag& Tag) const +{ + if (const TSubclassOf* FoundClass = ItemMap.Find(Tag)) + { + return *FoundClass; + } + return nullptr; +} diff --git a/Source/ProjectMJ/DataAsset/MJItemDataAsset.h b/Source/ProjectMJ/DataAsset/MJItemDataAsset.h new file mode 100644 index 00000000..57ccb12f --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJItemDataAsset.h @@ -0,0 +1,30 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Engine/DataAsset.h" +#include "MJItemDataAsset.generated.h" + +class AMJItemBase; +/** + * Class Description: 아이템 태그로 실제 아이템 클래스 매핑하는 데이터 에셋 + * Author: Kim Minjin + * Created Date: 2025.07.26. + * Last Modified By: + * Last Modified Date: 현재 사용 안 함.(DropItemsDataAsset 사용) + */ +UCLASS() +class PROJECTMJ_API UMJItemDataAsset : public UDataAsset +{ + GENERATED_BODY() +public: + UMJItemDataAsset(); + + TSubclassOf FindItemClassForTag(const FGameplayTag& Tag) const; + +protected: + UPROPERTY(EditDefaultsOnly, Category = "Item", meta = (Categories = "Item")) + TMap> ItemMap; +}; diff --git a/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.cpp b/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.cpp new file mode 100644 index 00000000..0fd348e6 --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.cpp @@ -0,0 +1,5 @@ +// ThenOneDayStudio + + +#include "DataAsset/MJStateAbilityDataAsset.h" + diff --git a/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.h b/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.h new file mode 100644 index 00000000..f038c375 --- /dev/null +++ b/Source/ProjectMJ/DataAsset/MJStateAbilityDataAsset.h @@ -0,0 +1,34 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "MJStateAbilityDataAsset.generated.h" + +class UGameplayAbility; +/** + * Class Description: 시작 시 부여할 State에 관련된 Ability의 데이터에셋. + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJStateAbilityDataAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + UMJStateAbilityDataAsset(){}; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ActionAppearanceAbilityClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ActionDamageAbilityClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ActionDeathAbilityClass; +}; diff --git a/Source/ProjectMJ/DataTable/MJEnemyDataRow.h b/Source/ProjectMJ/DataTable/MJEnemyDataRow.h new file mode 100644 index 00000000..8b1500a9 --- /dev/null +++ b/Source/ProjectMJ/DataTable/MJEnemyDataRow.h @@ -0,0 +1,94 @@ + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "GameplayTagContainer.h" +#include "MJEnemyDataRow.generated.h" + +/** + * Class Description: Enemy DataTable Base + * Author: 신동민 + * Created Date: 2025.07.16 + * Modified By: + * Modified Date: + */ + +class UMJStateAbilityDataAsset; +class UMJDropItemsDataAsset; +class AMJMonsterAIControllerBase; +class UCurveTable; +class USkeletalMesh; +class UGameplayEffect; + +USTRUCT(BlueprintType) +struct FMJEnemyDataRow : public FTableRowBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Enemy.Name")) + FGameplayTag DefaultEnemyTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText DisplayName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText Description; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Enemy.Type")) + FGameplayTag TypeTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Enemy.Species")) + FGameplayTag SpeciesTag; + + // 배우고 있는 스킬 관련 + // Skill Section + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Skill.Normal")) + FGameplayTag NormalAttackTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Skill")) + FGameplayTag IdentitySkillTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 SkillLevel = 1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0", Delta = "0.01", DisplayName = "Give Chance (%)")) + float GiveChance; + + // 기본 스탯 + // Basic Stat Section + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSoftObjectPtr AttributeCurve; + + // TODO: 사운드나 아이콘 등등 + + // Minjin: DropItems + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr DropItems; + + // Minjin: Animation - 안 쓸 예정 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr AppearanceAnimation; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr DeathAnimation; + + // Minjin: State Ability DataAsset + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr StateAbility; + + /* + * TODO + * 비헤이비어트리, AIController + */ + // AIController + + // BehaviorTree + + // 초기 세팅값 + // Initial Section + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray> InitialEffects; + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/DataTable/MJPlayerStatDataRow.h b/Source/ProjectMJ/DataTable/MJPlayerStatDataRow.h new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/Source/ProjectMJ/DataTable/MJPlayerStatDataRow.h @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Source/ProjectMJ/DataTable/MJSkillDataRow.h b/Source/ProjectMJ/DataTable/MJSkillDataRow.h index 4a59d8db..c3ba2a61 100644 --- a/Source/ProjectMJ/DataTable/MJSkillDataRow.h +++ b/Source/ProjectMJ/DataTable/MJSkillDataRow.h @@ -9,12 +9,15 @@ /** * Class Description: DataTable Base * Author: 신동민 - * Created Date: 2025_06_27 - * Last Modified By: - * Last Modified Date: + * Created Date: 2025.06.27 + * Description of Change: Ability 부분 수정 + * Modified By: 신동민 + * Modified Date: 2025.08.06 */ +class AMJProjectileBase; class UGameplayAbility; +class UCurveTable; UENUM(BlueprintType) enum class ERequiredWeapon : uint8 @@ -33,6 +36,7 @@ enum class ESkillType : uint8 Instant UMETA(DisplayName = "Instant", ToolTip = "Instantly Action Skill"), Charge UMETA(DisplayName = "Charge", ToolTip = "Charging Action Skill"), Passive UMETA(DisplayName = "Passive", ToolTip = "Passive Skill"), + Normal UMETA(DisplayName = "Normal", ToolTip = "Normal Action Skill"), }; UENUM(BlueprintType) @@ -43,9 +47,6 @@ enum class ESkillTargetType : uint8 Area UMETA(DisplayName = "AoE", ToolTip = "Area of Effect"), }; -class UAnimMontage; -class UCurveTable; - USTRUCT(BlueprintType) struct FMJSkillDataRow : public FTableRowBase { @@ -53,12 +54,18 @@ struct FMJSkillDataRow : public FTableRowBase public: // Dongmin: RowName을 태그와 같게 할 목적 - UPROPERTY(EditAnywhere, BlueprintReadWrite) + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Skill")) FGameplayTag DefaultSkillTag; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 SkillIndex; + UPROPERTY(EditAnywhere, BlueprintReadWrite) FText SkillName; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UTexture2D* Icon; + UPROPERTY(EditAnywhere, BlueprintReadWrite) FText SkillDescription; @@ -71,13 +78,9 @@ struct FMJSkillDataRow : public FTableRowBase UPROPERTY(EditAnywhere, BlueprintReadWrite) ESkillTargetType SkillTargetType; - /*UPROPERTY(EditAnywhere, BlueprintReadWrite) - TSoftObjectPtr ActionAnimMontage;*/ - - UPROPERTY(EditAnywhere, BlueprintReadWrite) - TSubclassOf SkillAbilityClass; - UPROPERTY(EditAnywhere, BlueprintReadWrite) TSoftObjectPtr SkillLevelDataTable; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSoftObjectPtr SkillLevelAbilityTable; }; \ No newline at end of file diff --git a/Source/ProjectMJ/DataTable/MJSkillLevelAbilityRow.h b/Source/ProjectMJ/DataTable/MJSkillLevelAbilityRow.h new file mode 100644 index 00000000..fe8fe0ed --- /dev/null +++ b/Source/ProjectMJ/DataTable/MJSkillLevelAbilityRow.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "MJSkillLevelAbilityRow.generated.h" + +/** + * Class Description: 스킬 레벨에 따라 다르게 나가는 Ability를 담은 구조체, Row가 스킬의 레벨 + * Author: 신동민 + * Created Date: 2025.07.29 + * Description of Change: + * Modified By: + * Modified Date: + */ + +class UGameplayAbility; + +USTRUCT(BlueprintType) +struct FMJSkillLevelAbilityRow : public FTableRowBase +{ + GENERATED_BODY() + +public: + // 레벨별 Ability 클래스 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ActionSkillAbilityClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf SkillAbilityClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf PassiveSkillAbilityClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf DrawMarkerAbilityClass; + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/Dialogue/MJBacklogWidget.cpp b/Source/ProjectMJ/Dialogue/MJBacklogWidget.cpp index 1893ba0a..3754eb2b 100644 --- a/Source/ProjectMJ/Dialogue/MJBacklogWidget.cpp +++ b/Source/ProjectMJ/Dialogue/MJBacklogWidget.cpp @@ -4,23 +4,38 @@ #include "Dialogue/MJBacklogWidget.h" #include "Components/TextBlock.h" #include "Components/ScrollBox.h" +#include "Components/ScrollBoxSlot.h" #include "Dialogue/MJDialogueRow.h" +#include "Fonts/SlateFontInfo.h" void UMJBacklogWidget::AddLine(const FMJDialogueRow& Row) // 함수명 수정하기 > AddLine으로 { if (!BacklogScrollBox) return; - FString Line = Row.Speaker + TEXT(" : ") + Row.Text; + FString Line = Row.Speaker + TEXT(" : ") + Row.Script; UTextBlock* NewTextBlock = NewObject(BacklogScrollBox); if (NewTextBlock) { + // 폰트 변경 + FSlateFontInfo FontInfo; + FontInfo.FontObject = LoadObject(nullptr, TEXT("/Game/UI/Font/CookieRun_Regular_Font.CookieRun_Regular_Font")); + FontInfo.Size = 15; + NewTextBlock->SetFont(FontInfo); + + NewTextBlock->SetText(FText::FromString(Line)); BacklogScrollBox->AddChild(NewTextBlock); - // 텍스트 색 변경방법 - FSlateColor SetColor = FSlateColor(FLinearColor::Blue); + // 텍스트 색 변경 + FSlateColor SetColor = FSlateColor(FLinearColor::White); NewTextBlock->SetColorAndOpacity(SetColor); + + // 대사 행 간격 + if (UScrollBoxSlot* ScrollSlot = Cast(NewTextBlock->Slot)) + { + ScrollSlot->SetPadding(FMargin(20, 5, 20, 5)); + } } } diff --git a/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.cpp b/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.cpp new file mode 100644 index 00000000..b4307ff6 --- /dev/null +++ b/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.cpp @@ -0,0 +1,23 @@ +// ThenOneDayStudio + + +#include "Dialogue/MJDialogueChoiceWidget.h" + +#include "Components/Button.h" +#include "Components/TextBlock.h" + + +void UMJDialogueChoiceWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + SetVisibility(ESlateVisibility::Hidden); +} + +void UMJDialogueChoiceWidget::SetTextBlock(const FString& quest, const FString& point, const FString& exit) +{ + Quest->SetText(FText::FromString(quest)); + Point->SetText(FText::FromString(point)); + Exit->SetText(FText::FromString(exit)); +} + diff --git a/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.h b/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.h new file mode 100644 index 00000000..586725af --- /dev/null +++ b/Source/ProjectMJ/Dialogue/MJDialogueChoiceWidget.h @@ -0,0 +1,47 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJDialogueChoiceWidget.generated.h" + +class UButton; +class UTextBlock; +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJDialogueChoiceWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Quest; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Point; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Exit; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr QuestButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PointButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ExitButton; + +public: + virtual void NativeConstruct() override; + + void SetTextBlock(const FString& quest, const FString& point, const FString& exit); + + // Getter + UButton* GetQuestButton() {return QuestButton;}; + UButton* GetPointButton(){return PointButton;}; + UButton* GetExitButton() {return ExitButton;}; +}; diff --git a/Source/ProjectMJ/Dialogue/MJDialogueComponent.cpp b/Source/ProjectMJ/Dialogue/MJDialogueComponent.cpp index 9da099b2..b3a801fa 100644 --- a/Source/ProjectMJ/Dialogue/MJDialogueComponent.cpp +++ b/Source/ProjectMJ/Dialogue/MJDialogueComponent.cpp @@ -3,49 +3,117 @@ #include "Dialogue/MJDialogueComponent.h" +#include "MJBacklogWidget.h" +#include "MJDialogueChoiceWidget.h" +#include "MJDialogueWidget.h" +#include "TG/MJGameInstance.h" +#include "UI/MJHUDWidget.h" +#include "UI/MJUIManagerSubsystem.h" + UMJDialogueComponent::UMJDialogueComponent() { - DialogueTable = nullptr; + DialogueTable = nullptr; // 내가 블프에서 직접 넣어줘야 함 CurrentIndex = 0; - RowNames.Empty(); } -void UMJDialogueComponent::StartDialogue() +void UMJDialogueComponent::TurnOver() // x키가 눌리면 이 함수가 실행되어야 함 +{ + // Skip Typing + if (GetDialogueWidget()->GetIsTyping()) + { + GetDialogueWidget()->SkipTyping(); + return; + } + + // Next Script + ++ CurrentIndex; + + if (!IsDialogueEnd()) + { + FloatLine(); + GetDialogueWidget()->SetImageOpacity(GetCurrentRow()->Speaker); + + UpdateBacklog(); + } +} + +void UMJDialogueComponent::UpdateBacklog() { - if (DialogueTable) + if (!GetDialogueWidget()->GetBacklogWidget()) { - RowNames = DialogueTable->GetRowNames(); - CurrentIndex = 0; + return; } + GetDialogueWidget()->GetBacklogWidget()->AddLine(*GetPreviousRow()); } -void UMJDialogueComponent::NextDialogue() +void UMJDialogueComponent::UpdateChoice() { - ++CurrentIndex; + if (!GetDialogueWidget()->GetDialogueChoiceWidget()) + { + return; + } + if (GetCurrentRow()->Choices.IsValidIndex(CurrentIndex)) + { + // GetDialogueWidget()->GetDialogueChoiceWidget()->SetTextBlock(); + } } bool UMJDialogueComponent::IsDialogueEnd() const { - return (CurrentIndex >= RowNames.Num()); + return (CurrentIndex >= DialogueTable->GetRowNames().Num()); +} + +void UMJDialogueComponent::FloatLine() +{ + GetDialogueWidget()->SetTextBlock(GetCurrentRow()->Script, GetCurrentRow()->Speaker); + GetDialogueWidget()->StartTyping(GetCurrentRow()->Script,0.05); + GetDialogueWidget()->SetImageOpacity(GetCurrentRow()->Speaker); } const FMJDialogueRow* UMJDialogueComponent::GetCurrentRow() const { - if (DialogueTable && RowNames.IsValidIndex(CurrentIndex)) + if (DialogueTable && DialogueTable->GetRowNames().IsValidIndex(CurrentIndex)) { - return DialogueTable->FindRow(RowNames[CurrentIndex], TEXT("")); - // ? : TEXT("")는 그냥 디버깅용 메세지라고 한다 + return DialogueTable->FindRow(DialogueTable->GetRowNames()[CurrentIndex], TEXT("")); } - return nullptr; } -const FMJDialogueRow* UMJDialogueComponent::GetPreviousRow() const +const FMJDialogueRow* UMJDialogueComponent::GetPreviousRow() const // 백로그용 { - if (DialogueTable &&RowNames.IsValidIndex(CurrentIndex -1)) + if (DialogueTable && DialogueTable->GetRowNames().IsValidIndex(CurrentIndex -1)) { - return DialogueTable->FindRow(RowNames[CurrentIndex -1], TEXT("")); + return DialogueTable->FindRow(DialogueTable->GetRowNames()[CurrentIndex -1], TEXT("")); } - return nullptr; -} \ No newline at end of file +} + +UMJDialogueWidget* UMJDialogueComponent::GetDialogueWidget() +{ + if (GetWorld() && GetWorld()->GetGameInstance() && + GetWorld()->GetGameInstance()->GetSubsystem() && + GetWorld()->GetGameInstance()->GetSubsystem()->GetHUDWidget()) + { + return GetWorld()->GetGameInstance()->GetSubsystem() + ->GetHUDWidget()->GetDialogueWidget(); + } + return nullptr; +} + +void UMJDialogueComponent::SkipTyping() +{ + if (GetDialogueWidget()->GetIsTyping()) + { + GetDialogueWidget()->SkipTyping(); + } +} + +bool UMJDialogueComponent::bIsFirstIndex() +{ + return CurrentIndex == 0; +} + + + + + diff --git a/Source/ProjectMJ/Dialogue/MJDialogueComponent.h b/Source/ProjectMJ/Dialogue/MJDialogueComponent.h index 52464b88..50836199 100644 --- a/Source/ProjectMJ/Dialogue/MJDialogueComponent.h +++ b/Source/ProjectMJ/Dialogue/MJDialogueComponent.h @@ -15,6 +15,8 @@ * Last Modified Date: 2025.06.12 */ +struct FMJDialogueChoiceRow; +class UMJDialogueWidget; UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class PROJECTMJ_API UMJDialogueComponent : public UActorComponent { @@ -26,24 +28,36 @@ class PROJECTMJ_API UMJDialogueComponent : public UActorComponent protected: // UDataTable 이 들어올 수 있는 방 -> 세입자를 넣어 줘야함 (포인터 == 누군가를 가리킬 수 있음) UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Component") - UDataTable* DialogueTable; - - UPROPERTY(BlueprintReadOnly, Category = "Dialogue") - TArray RowNames; + TObjectPtr DialogueTable; UPROPERTY(BlueprintReadOnly, Category = "Dialogue") int32 CurrentIndex; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Dialogue") + int32 CurrentNodeID; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Dialogue") + int32 SelectedNodeID; + public: - UFUNCTION(BlueprintCallable, Category = "Dialogue") - void StartDialogue(); + virtual bool IsDialogueEnd() const; + + // Refactoring + void FloatLine(); UFUNCTION(BlueprintCallable, Category = "Dialogue") - void NextDialogue(); + void TurnOver(); - bool IsDialogueEnd() const; + void UpdateBacklog(); + void UpdateChoice(); + + + void SetIndex(int index) {CurrentIndex = index;} const FMJDialogueRow* GetCurrentRow() const; - const FMJDialogueRow* GetPreviousRow() const; + UMJDialogueWidget* GetDialogueWidget(); + void SkipTyping(); + bool bIsFirstIndex(); }; diff --git a/Source/ProjectMJ/Dialogue/MJDialogueRow.h b/Source/ProjectMJ/Dialogue/MJDialogueRow.h index f1ab868c..af4d7215 100644 --- a/Source/ProjectMJ/Dialogue/MJDialogueRow.h +++ b/Source/ProjectMJ/Dialogue/MJDialogueRow.h @@ -10,14 +10,39 @@ * Last Modified By: 이지수 * Last Modified Date: 2025.06.12 */ +USTRUCT(BlueprintType) +struct FDialogueChoice +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString ChoiceText; // 버튼으로 업데이트 되어야할 텍스트 + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 NextNodeID; // 다음 노드의 번호, 해당 번호로 Script 변경 +}; + USTRUCT(BlueprintType) struct FMJDialogueRow : public FTableRowBase { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) - FString Speaker; + int32 NodeID; // 현재 노드의 번호 + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString Speaker; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString Script; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString ScriptForStore; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString ScriptForExit; + UPROPERTY(EditAnywhere, BlueprintReadWrite) - FString Text; + TArray Choices; // 선택지는 여러개니까 배열? }; diff --git a/Source/ProjectMJ/Dialogue/MJDialogueWidget.cpp b/Source/ProjectMJ/Dialogue/MJDialogueWidget.cpp index bc7a55d5..2b10bbb0 100644 --- a/Source/ProjectMJ/Dialogue/MJDialogueWidget.cpp +++ b/Source/ProjectMJ/Dialogue/MJDialogueWidget.cpp @@ -13,13 +13,27 @@ void UMJDialogueWidget::NativeConstruct() if (BacklogWidget) { BacklogWidget->SetVisibility(ESlateVisibility::Hidden); - UE_LOG(LogTemp, Warning, TEXT("Dialogue Widget Constructed")); + } +} + +void UMJDialogueWidget::SetTextBlock(const FString& InText,const FString& speaker) +{ + // 여기서 크래시가 난다면 component에 DT를 넣었는지 확인해보세요 + if (Text) + { + Text->SetText(FText::FromString(InText)); + } + + if (Speaker) + { + Speaker->SetText(FText::FromString(speaker)); } } void UMJDialogueWidget::StartTyping(const FString& InText, float TypingSpeed) { FullText = InText; + UE_LOG(LogTemp, Display, TEXT("Text: %s"), *FullText); CurrentCharIndex = 0; bIsTyping = true; Text->SetText(FText::FromString(TEXT(""))); // 빈문자열로 초기화해서 한글자씩 나타날 수 있도록 하기 @@ -57,6 +71,7 @@ void UMJDialogueWidget::SkipTyping() { if (bIsTyping) { + UE_LOG(LogTemp, Display, TEXT("SkipTyping")); GetWorld()->GetTimerManager().ClearTimer(TypingTimerHandle); Text->SetText(FText::FromString(FullText)); bIsTyping = false; @@ -67,17 +82,16 @@ void UMJDialogueWidget::SetImageOpacity(const FString& SpeakerName) { if (!PlayerImage) return; + if (!NPCImage) return; - if (SpeakerName == TEXT("태관")) + if (SpeakerName == TEXT("Player")) { - UE_LOG(LogTemp, Warning, TEXT("Image Opacity")); PlayerImage->SetOpacity(1.0f); NPCImage->SetOpacity(0.3f); } - - if (SpeakerName == TEXT("주현")) + else { NPCImage->SetOpacity(1.0f); PlayerImage->SetOpacity(0.3f); diff --git a/Source/ProjectMJ/Dialogue/MJDialogueWidget.h b/Source/ProjectMJ/Dialogue/MJDialogueWidget.h index 91451b51..d30050e2 100644 --- a/Source/ProjectMJ/Dialogue/MJDialogueWidget.h +++ b/Source/ProjectMJ/Dialogue/MJDialogueWidget.h @@ -5,9 +5,10 @@ #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "Components/Image.h" -//#include "MJDialogueRow.h" #include "MJDialogueWidget.generated.h" +class UMJDialogueChoiceWidget; +class UMJBacklogWidget; /** * Class Description: 다이어로그 위젯을 화면에 띄우기 위한 클래스 / BP_DialogueWidget의 부모 클래스로, 그래프에서 데이터 테이블 연동해줘야 함 * Author: 이지수 @@ -26,8 +27,7 @@ class PROJECTMJ_API UMJDialogueWidget : public UUserWidget UFUNCTION() virtual void NativeConstruct() override; - UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) - void ShowDialogue(const FMJDialogueRow& DialogueRow); + void SetTextBlock(const FString& InText, const FString& Speaker); void ShowBacklog(); @@ -38,16 +38,21 @@ class PROJECTMJ_API UMJDialogueWidget : public UUserWidget // Image void SetImageOpacity(const FString& SpeakerName); - - UPROPERTY(meta = (BindWidget)) - class UMJBacklogWidget* BacklogWidget; + bool GetIsTyping() {return bIsTyping;} + // Getter + UMJBacklogWidget* GetBacklogWidget() {return BacklogWidget;} + UMJDialogueChoiceWidget* GetDialogueChoiceWidget() {return DialogueChoice;} + protected: UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) UTextBlock* Text; + UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) + UTextBlock* Speaker; + UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) UImage* PlayerImage; @@ -56,6 +61,12 @@ class PROJECTMJ_API UMJDialogueWidget : public UUserWidget UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) UImage* BacklogKey; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BacklogWidget; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr DialogueChoice; FTimerHandle TypingTimerHandle; FString FullText; diff --git a/Source/ProjectMJ/GameMode/MJGameModeBase.cpp b/Source/ProjectMJ/GameMode/MJGameModeBase.cpp index 343351a6..5783dc3e 100644 --- a/Source/ProjectMJ/GameMode/MJGameModeBase.cpp +++ b/Source/ProjectMJ/GameMode/MJGameModeBase.cpp @@ -4,10 +4,11 @@ #include "GameMode/MJGameModeBase.h" #include "ProjectMJ.h" +#include "Controller/MJPlayerController.h" #include "Kismet/GameplayStatics.h" #include "Player/MJPlayerState.h" -#include "TG/MJGameInstanceTG.h" -#include "TG/GameState/MJGameStateDungeonTG.h" +#include "TG/MJGameInstance.h" +#include "TG/GameState/MJGameStateDungeon.h" AMJGameModeBase::AMJGameModeBase() { @@ -15,15 +16,6 @@ AMJGameModeBase::AMJGameModeBase() } -void AMJGameModeBase::BeginPlay() -{ - Super::BeginPlay(); - - - -} - - bool AMJGameModeBase::TravelToMap(const FString MapName) { bool bAbsolute = false; @@ -42,7 +34,7 @@ bool AMJGameModeBase::TravelToMap(const FString MapName) bool AMJGameModeBase::TravelToMapByNode(const FString MapName, const uint8 NodeNum) { - UMJGameInstanceTG* MJGI = GetGameInstance(); + UMJGameInstance* MJGI = GetGameInstance(); if (MJGI) { AMJPlayerState* PS = Cast(UGameplayStatics::GetPlayerState(this,0)); @@ -50,8 +42,8 @@ bool AMJGameModeBase::TravelToMapByNode(const FString MapName, const uint8 NodeN { PS->GetPlayerSessionDataRef().CurrentDungeonMapNum = NodeNum; PS->SaveToInstancedPlayerSessionData(); + } - MJGI->bIsPlayerStateDirty = true; } diff --git a/Source/ProjectMJ/GameMode/MJGameModeBase.h b/Source/ProjectMJ/GameMode/MJGameModeBase.h index f97f7382..8a01e888 100644 --- a/Source/ProjectMJ/GameMode/MJGameModeBase.h +++ b/Source/ProjectMJ/GameMode/MJGameModeBase.h @@ -16,10 +16,6 @@ class PROJECTMJ_API AMJGameModeBase : public AGameModeBase public: AMJGameModeBase(); - -protected: - - virtual void BeginPlay() override; UFUNCTION(BlueprintCallable) virtual bool TravelToMapByNode(const FString MapName, const uint8 NodeNum); diff --git a/Source/ProjectMJ/Item/MJItemBase.cpp b/Source/ProjectMJ/Item/MJItemBase.cpp new file mode 100644 index 00000000..dc9d22b1 --- /dev/null +++ b/Source/ProjectMJ/Item/MJItemBase.cpp @@ -0,0 +1,40 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Item/MJItemBase.h" + +#include "NavigationSystemTypes.h" +#include "TG/MJGameInstance.h" +#include "UI/Inventory/ItemDataRow.h" +#include "UI/World/MJNameBarWidget.h" +#include "UI/World/MJNameBarWidgetComponent.h" + + +AMJItemBase::AMJItemBase() +{ + MeshComp = CreateDefaultSubobject(TEXT("MJItemBase")); + SetRootComponent(MeshComp); + + NameBarWidgetComp = CreateDefaultSubobject(TEXT("NameBar")); + NameBarWidgetComp->SetupAttachment(MeshComp); +} + +void AMJItemBase::BeginPlay() +{ + Super::BeginPlay(); + + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->ItemDataTable) + { + return; + } + + const FItemDataRow* ItemRow = GI->ItemDataTable->FindRow(ItemTag.GetTagName(),TEXT("Find ItemData")); + + if (UMJNameBarWidget* NameBar = Cast(NameBarWidgetComp->GetUserWidgetObject())) + { + NameBar->SetNameText(ItemRow->ItemID); + } +} + + diff --git a/Source/ProjectMJ/Item/MJItemBase.h b/Source/ProjectMJ/Item/MJItemBase.h new file mode 100644 index 00000000..69cb157c --- /dev/null +++ b/Source/ProjectMJ/Item/MJItemBase.h @@ -0,0 +1,32 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "GameFramework/Actor.h" +#include "MJItemBase.generated.h" + +class UMJNameBarWidgetComponent; +class UDataTable; +class UMJInventoryComponent; +UCLASS() +class PROJECTMJ_API AMJItemBase : public AActor +{ + GENERATED_BODY() + +protected: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item) + FGameplayTag ItemTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item) + TObjectPtr NameBarWidgetComp; + + UPROPERTY(EditAnywhere, BlueprintReadOnly) + TObjectPtr MeshComp; // +public: + AMJItemBase(); + + virtual void BeginPlay() override; + FGameplayTag GetItemTag() {return ItemTag;} +}; diff --git a/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.cpp b/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.cpp index 68a40ba9..297545c1 100644 --- a/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.cpp +++ b/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.cpp @@ -3,15 +3,27 @@ #include "MJ/AI/BTService_MJCheckAttackRange.h" +#include "AbilitySystemBlueprintLibrary.h" +#include "AbilitySystemComponent.h" #include "AIController.h" #include "ProjectMJ.h" +#include "VectorTypes.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" #include "BehaviorTree/BlackboardComponent.h" -#include "MJ/Interface/MJCharacterAIInterface.h" +#include "Character/MJCharacterBase.h" +#include "Character/Component/MJEnemySkillComponent.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "Components/CapsuleComponent.h" +#include "DataTable/MJSkillDataRow.h" +#include "MJ/Character/MJMonsterCharacter.h" UBTService_MJCheckAttackRange::UBTService_MJCheckAttackRange() { NodeName = "CheckAttackRange"; Interval = 0.1f; + PreTargetLocation = FVector::ZeroVector; + CurrTargetLocation = FVector::ZeroVector; } void UBTService_MJCheckAttackRange::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) @@ -19,17 +31,25 @@ void UBTService_MJCheckAttackRange::TickNode(UBehaviorTreeComponent& OwnerComp, /* * How to: 타겟이 몬스터의 공격 범위에 들어갔을 때 Maximum 혹은 Minimum 범위에 들어오면 블랙보드 키를 설정 */ - + CachedOwnerComp = &OwnerComp; Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds); + OwnerComp.GetBlackboardComponent()->ClearValue("IsInAttackRange"); + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); if (ControlledPawn == nullptr) { return; } - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (AIPawn == nullptr) + AMJCharacterBase* Character = Cast(ControlledPawn); + if (Character == nullptr) + { + return; + } + + AMJMonsterCharacter* Enemy = Cast(ControlledPawn); + if (Enemy == nullptr) { return; } @@ -39,34 +59,138 @@ void UBTService_MJCheckAttackRange::TickNode(UBehaviorTreeComponent& OwnerComp, { return; } - DrawDebugCircle(GetWorld(), ControlledPawn->GetActorLocation(), AIPawn->GetAIMaximumAttackRange(), 16, FColor::Emerald, false, 0.2f, 0, 0, FVector::RightVector,FVector::ForwardVector, false); - DrawDebugCircle(GetWorld(), ControlledPawn->GetActorLocation(), AIPawn->GetAIMinimumAttackRange(), 16, FColor::Magenta, false, 0.2f, 0, 0, FVector::RightVector,FVector::ForwardVector, false); - float DistanceTo = FVector::Distance(Target->GetActorLocation(), ControlledPawn->GetActorLocation()); - float DistanceToTarget = ControlledPawn->GetDistanceTo(Target); - - // Minjin: 원거리 범위에 속할 경우 - bool IsRange = DistanceToTarget <= AIPawn->GetAIMaximumAttackRange() - && DistanceToTarget > AIPawn->GetAIMinimumAttackRange(); - // Minjin: 근거리 범위에 속할 경우 - bool IsMelee = DistanceToTarget <= AIPawn->GetAIMinimumAttackRange(); + /* + * Minjin + * HowTo: 설정한 태그를 가지고 있는 태그와 비교해서 구체적인 태그를 가져온다. + * 가져온 태그로 Skill Set 가져온다. + */ + + /*if (!Enemy->HasAnyMatchingGameplayTags(SkillTags)) + { + MJ_LOG(LogMJ, Log, TEXT("Has not AnyMatchingGameplayTags")); + return; + }*/ - if (IsRange) + UMJSkillComponentBase* SkillComp = Enemy->GetSkillComponent(); + FGameplayTag SkillTag = FGameplayTag::EmptyTag; + + for (FGameplayTag HasTag: SkillTags) + { + if (SkillComp->GetOwnedSkillMap().Contains(HasTag)) + { + SkillTag = HasTag; + } + } + + if (!SkillTag.IsValid()) { - OwnerComp.GetBlackboardComponent()->SetValueAsBool("MaximumAttackRange", true); - OwnerComp.GetBlackboardComponent()->ClearValue("MinimumAttackRange"); + return; } - else if (IsMelee) + int32 SkillLevel = SkillComp->GetOwnedSkillMap()[SkillTag].Level; + // 레벨 정보 가져옴 + + UDataTable* SkillDataTable = SkillComp->GetSkillDataTable(); + const FMJSkillDataRow* DataRow = SkillDataTable->FindRow(SkillTag.GetTagName(), TEXT("Find Skill Info")); + if (!DataRow) { - OwnerComp.GetBlackboardComponent()->SetValueAsBool("MinimumAttackRange", true); - OwnerComp.GetBlackboardComponent()->ClearValue("MaximumAttackRange"); + MJ_LOG(LogMJ, Log, TEXT("Not Exist DataRow")); + return; } - else + + UCurveTable* CurveTable = DataRow->SkillLevelDataTable.LoadSynchronous(); + if (!CurveTable) { - // Minjin: 아무 범위에도 안 속할 경우: 키 값 클리어 - OwnerComp.GetBlackboardComponent()->ClearValue("MinimumAttackRange"); - OwnerComp.GetBlackboardComponent()->ClearValue("MaximumAttackRange"); + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillLevelDataTable")); + return; } - return; + /* + * SkillRadius + * SkillRange + * SkillAttackLocationOffset + */ + FRealCurve* SkillRadiusCurve = CurveTable->FindCurve(TEXT("SkillRadius"), TEXT("FindSkillRadius")); + const float AttackRadius = SkillRadiusCurve->Eval(SkillLevel); + + FRealCurve* SkillRangeCurve = CurveTable->FindCurve(TEXT("SkillRange"), TEXT("FindSkillRange")); + const float SkillRange = SkillRangeCurve->Eval(SkillLevel); + + FRealCurve* SkillAttackLocationOffsetCurve = CurveTable->FindCurve(TEXT("SkillAttackLocationOffset"), TEXT("FindSkillAttackLocationOffset")); + const float Offset = SkillAttackLocationOffsetCurve->Eval(SkillLevel); + + const float AttackRange = FMath::Max(SkillRange , AttackRadius * 2.0f); + + // float DistanceToTarget = ControlledPawn->GetDistanceTo(Target); + int32 DistanceToTarget = FMath::FloorToInt(ControlledPawn->GetDistanceTo(Target)); + + const float MinimumAttackRange = Character->GetCapsuleComponent()->GetScaledCapsuleRadius() * ((Offset!=0)?Offset:1); + const float MaximumAttackRange = MinimumAttackRange + AttackRange; + + //DrawDebugCircle(GetWorld(), ControlledPawn->GetActorLocation(), MaximumAttackRange, 16, FColor::Emerald, false, 0.2f, 0, 0, FVector::RightVector,FVector::ForwardVector, false); + //DrawDebugCircle(GetWorld(), ControlledPawn->GetActorLocation(), MinimumAttackRange, 16, FColor::Magenta, false, 0.2f, 0, 0, FVector::RightVector,FVector::ForwardVector, false); + + bool IsInAttackRange = DistanceToTarget <= MaximumAttackRange + && DistanceToTarget >= MinimumAttackRange; + + // Minjin: 공격 범위에 속할 경우 + if (IsInAttackRange) + { + OwnerComp.GetBlackboardComponent()->SetValueAsBool("IsInAttackRange", true); + return; + } + + /* + * Minjin + * 공격 범위에 속하지 않을 경우 + * 키 값을 지워주고 이동해야 할 위치(KeepDistancePos) 설정해준다. + * 타겟 위치 변동이 있을 경우에만. + */ + + CurrTargetLocation = Target->GetActorLocation(); + if (PreTargetLocation == FVector::ZeroVector) + { + PreTargetLocation = CurrTargetLocation; + } + else if (PreTargetLocation == CurrTargetLocation) + { + return; + } + + PreTargetLocation = CurrTargetLocation; + + const int32 HalfAttackRange = FMath::FloorToInt(MinimumAttackRange + AttackRange * 0.5f); + + FVector EnemyLocation = ControlledPawn->GetActorLocation(); + float Distance = FVector::Dist(EnemyLocation, CurrTargetLocation); + + FVector MoveToLocation = FVector::ZeroVector; + + // Minjin: 거리가 MinimumAttackRange 미만일 경우->플레이어와 멀어진다 + if (Distance < MinimumAttackRange) + { + FVector AwayDir = (EnemyLocation - CurrTargetLocation).GetSafeNormal(); + FVector RandomDir = FMath::VRandCone(AwayDir, FMath::DegreesToRadians(30.f)); + MoveToLocation = EnemyLocation + RandomDir *(HalfAttackRange - Distance); + } + + // Minjin: 거리가 MaximumAttackRange보다 멀 경우->플레이어와 가까워진다 + if (Distance > MaximumAttackRange) + { + FVector ToDir = (CurrTargetLocation - EnemyLocation).GetSafeNormal(); + + MoveToLocation = EnemyLocation + ToDir * (Distance - HalfAttackRange); + } + + OwnerComp.GetBlackboardComponent()->SetValueAsVector("KeepDistancePos", MoveToLocation); +} + +void UBTService_MJCheckAttackRange::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); +} + +void UBTService_MJCheckAttackRange::InitializeFromAsset(UBehaviorTree& Asset) +{ + Super::InitializeFromAsset(Asset); } diff --git a/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.h b/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.h index 47bcae5e..0543a435 100644 --- a/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.h +++ b/Source/ProjectMJ/MJ/AI/BTService_MJCheckAttackRange.h @@ -10,8 +10,8 @@ * Class Description: ServiceNode-타겟이 공격범위에 들어와 있는지 확인(근거리, 원거리) * Author: Kim Minjin * Created Date: 2025.06.25. - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) + * Last Modified By: Kim Minjin + * Last Modified Date: (2025.08.10.) 하드코딩 수정. 스킬 태그를 받아와 스킬의 공격범위를 체크. KeepDistancePos 설정 */ UCLASS() class PROJECTMJ_API UBTService_MJCheckAttackRange : public UBTService @@ -23,4 +23,14 @@ class PROJECTMJ_API UBTService_MJCheckAttackRange : public UBTService protected: virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void InitializeFromAsset(UBehaviorTree& Asset) override; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tag", meta=(Categories = "Skill")) + FGameplayTagContainer SkillTags; + + FVector PreTargetLocation; + FVector CurrTargetLocation; + + UBehaviorTreeComponent* CachedOwnerComp; }; diff --git a/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.cpp b/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.cpp new file mode 100644 index 00000000..8d31df9f --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.cpp @@ -0,0 +1,51 @@ +// ThenOneDayStudio + + +#include "MJ/AI/BTService_MJCheckHealth.h" +#include "BTService_MJCheckHealth.h" +#include "AIController.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "Character/MJForestCreatureCharacter.h" +#include "AbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" + + +UBTService_MJCheckHealth::UBTService_MJCheckHealth() +{ + NodeName = TEXT("CheckHealth"); + Interval = 0.1f; +} + + +void UBTService_MJCheckHealth::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) +{ + Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds); + + ControlledPawn =Cast(OwnerComp.GetAIOwner()->GetPawn()); + if (!ControlledPawn) + { + return; + } + Blackboard = OwnerComp.GetBlackboardComponent(); + + CachedASC = ControlledPawn->GetAbilitySystemComponent(); + if (!CachedASC) + { + return; + } + + CachedASC->GetGameplayAttributeValueChangeDelegate(ControlledPawn->GetAttributeSet()->GetHealthAttribute()).AddUObject(this, &UBTService_MJCheckHealth::EnemyOnChangedHealth); + +} + +void UBTService_MJCheckHealth::EnemyOnChangedHealth(const FOnAttributeChangeData& Data) +{ + if (!Blackboard) + { + return; + } + float CurrentHP = Data.NewValue; + bool IsSet = (CurrentHP <= 200.f) ? true : false; + + Blackboard->SetValueAsBool("IsAttack",IsSet); +} \ No newline at end of file diff --git a/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.h b/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.h new file mode 100644 index 00000000..c4a1718e --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTService_MJCheckHealth.h @@ -0,0 +1,41 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "BehaviorTree/BTService.h" +#include "AbilitySystemComponent.h" +#include "BTService_MJCheckHealth.generated.h" + +/** + * Class Description: ServiceNode-Monster Health Check For Monster_Phase + * Author: Lee Ju Hyeon + * Created Date: 2025.08.04. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +class AMJForestCreatureCharacter; +class UAbilitySystemComponent; +class UBlackboardComponent; + +UCLASS() +class PROJECTMJ_API UBTService_MJCheckHealth : public UBTService +{ + GENERATED_BODY() + +public: + UBTService_MJCheckHealth(); + +protected: + + virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override; + +private: + + AMJForestCreatureCharacter* ControlledPawn = nullptr; + UAbilitySystemComponent* CachedASC; + UBlackboardComponent* Blackboard; +public: + + void EnemyOnChangedHealth(const FOnAttributeChangeData& Data); +}; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.cpp new file mode 100644 index 00000000..1061d637 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.cpp @@ -0,0 +1,40 @@ +// ThenOneDayStudio + + +#include "MJ/AI/BTTask_MJAppear.h" + +#include "AIController.h" +#include "Abilities/GameplayAbilityTypes.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "Character/MJCharacterBase.h" + +UBTTask_MJAppear::UBTTask_MJAppear() +{ +} + +EBTNodeResult::Type UBTTask_MJAppear::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = Cast(OwnerComp.GetAIOwner()->GetPawn()); + if (ControlledPawn == nullptr) + { + return EBTNodeResult::Failed; + } + + // ControlledPawn->SetActorHiddenInGame(false); + // ControlledPawn->SetActorEnableCollision(true); + + OwnerComp.GetBlackboardComponent()->SetValueAsBool("IsAppear", true); + + AMJCharacterBase* Character = Cast(ControlledPawn); + if (Character == nullptr) + { + return EBTNodeResult::Failed; + } + + FGameplayEventData EventData; + EventData.EventTag = FGameplayTag::RequestGameplayTag(FName("Event.Character.Appear")); + Character->ASC->HandleGameplayEvent(EventData.EventTag, &EventData); + + return EBTNodeResult::Succeeded; +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.h b/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.h new file mode 100644 index 00000000..147a8485 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJAppear.h @@ -0,0 +1,26 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "BehaviorTree/BTTaskNode.h" +#include "BTTask_MJAppear.generated.h" + +/** + * Class Description: 등장 어빌리티 실행 - 임시 제작 + * Author: Kim Minjin + * Created Date: 2025.08.08. + * Description of Change: + * Modified By: + * Modified Date: + */ +UCLASS() +class PROJECTMJ_API UBTTask_MJAppear : public UBTTaskNode +{ + GENERATED_BODY() + +public: + UBTTask_MJAppear(); + + virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; +}; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJAttack.cpp deleted file mode 100644 index 9459c6ca..00000000 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJAttack.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJ/AI/BTTask_MJAttack.h" -#include "AIController.h" -#include "BehaviorTree/BlackboardComponent.h" -#include "MJ/Interface/MJCharacterAIInterface.h" - -UBTTask_MJAttack::UBTTask_MJAttack() -{ -} - -EBTNodeResult::Type UBTTask_MJAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) -{ - EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory); - - APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); - if (nullptr == ControlledPawn) - { - return EBTNodeResult::Failed; - } - - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (nullptr == AIPawn) - { - return EBTNodeResult::Failed; - } - - AIPawn->AttackByAI(); - /* - * TODO - * 공격이 다 끝난 후에 완료 처리해야 한다. - */ - return EBTNodeResult::Succeeded; -} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.cpp new file mode 100644 index 00000000..aebcdd6d --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.cpp @@ -0,0 +1,59 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJ/AI/BTTask_MJChargeAttack.h" +#include "AIController.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "AbilitySystemComponent.h" +#include "Abilities/GameplayAbility.h" +#include "ProjectMJ.h" +#include "Character/Component/MJEnemySkillComponent.h" + +UBTTask_MJChargeAttack::UBTTask_MJChargeAttack() +{ + NodeName = TEXT("ChargeAttack"); +} + +EBTNodeResult::Type UBTTask_MJChargeAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); + if (ControlledPawn == nullptr) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Monster = Cast(ControlledPawn); + if (Monster == nullptr) + { + return EBTNodeResult::Failed; + } + + UAbilitySystemComponent* ASC = Monster->GetAbilitySystemComponent(); + UMJEnemySkillComponent* SkillComponent = Monster->GetSkillComponent(); + + FGameplayTag SkillTypeTag = FGameplayTag::RequestGameplayTag(FName("Skill.Charge")); + FGameplayTag AttackTag = SkillComponent->GetEquippedSkillMap()[SkillTypeTag]; + + if (!AttackTag.IsValid()) + { + return EBTNodeResult::Failed; + } + + FDelegateHandle Handle; + Handle = ASC->OnAbilityEnded.AddLambda( + [&, AttackTag, Handle](const FAbilityEndedData& EndedData) + { + MJ_LOG(LogMJ, Error,TEXT("A")); + if (EndedData.AbilityThatEnded->AbilityTags.HasTagExact(AttackTag)) + { + MJ_LOG(LogMJ, Error,TEXT("AA")); + FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); + ASC->OnAbilityEnded.Remove(Handle); + } + }); + MJ_LOG(LogMJ, Error,TEXT("AAA")); + SkillComponent->ActivateSkill(AttackTag); + + return EBTNodeResult::InProgress; +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.h b/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.h new file mode 100644 index 00000000..c4c97fa2 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJChargeAttack.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "BehaviorTree/BTTaskNode.h" +#include "BTTask_MJChargeAttack.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API UBTTask_MJChargeAttack : public UBTTaskNode +{ + GENERATED_BODY() + +public: + UBTTask_MJChargeAttack(); + +protected: + virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; +}; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.cpp index 37cc88ae..684fb6fc 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.cpp +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.cpp @@ -5,7 +5,10 @@ #include "AIController.h" #include "NavigationSystem.h" #include "BehaviorTree/BlackboardComponent.h" -#include "MJ/Interface/MJCharacterAIInterface.h" +#include "Perception/AIPerceptionComponent.h" +#include "Perception/AISenseConfig.h" +#include "Perception/AISenseConfig_Sight.h" +#include "Perception/AISense_Sight.h" UBTTask_MJFindPatrolPos::UBTTask_MJFindPatrolPos() { @@ -15,28 +18,40 @@ UBTTask_MJFindPatrolPos::UBTTask_MJFindPatrolPos() EBTNodeResult::Type UBTTask_MJFindPatrolPos::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory); + + AAIController* AIController = OwnerComp.GetAIOwner(); + if (AIController == nullptr) + { + return EBTNodeResult::Failed; + } - APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); - if (nullptr == ControlledPawn) + APawn* ControlledPawn = AIController->GetPawn(); + if (ControlledPawn == nullptr) { return EBTNodeResult::Failed; } UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetNavigationSystem(ControlledPawn->GetWorld()); - if (nullptr == NavSystem) + if (NavSystem == nullptr) { return EBTNodeResult::Failed; } - - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (nullptr == AIPawn) + + UAIPerceptionComponent* PerceptionComp = AIController->GetAIPerceptionComponent(); + if (PerceptionComp == nullptr) + { + return EBTNodeResult::Failed; + } + FAISenseID SightSenseID = UAISense::GetSenseID(); + UAISenseConfig_Sight* SightConfig = Cast(PerceptionComp->GetSenseConfig(SightSenseID)); + if (SightConfig == nullptr) { return EBTNodeResult::Failed; } - float PatrolRadius = AIPawn->GetAIPatrolRadius(); - FNavLocation NextPatrolPos; + float PatrolRadius = SightConfig->SightRadius; + FNavLocation NextPatrolPos; FVector Origin = ControlledPawn->GetActorLocation(); if (NavSystem->GetRandomPointInNavigableRadius(Origin, PatrolRadius, NextPatrolPos)) diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.h b/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.h index d2e8b229..2cf099ee 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJFindPatrolPos.h @@ -10,8 +10,8 @@ * Class Description: BBKey인 PatrolPos 설정 * Author: Kim Minjin * Created Date: 2025.06.19. - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) + * Last Modified By: Kim Minjin + * Last Modified Date: (2025.08.10.) Sight Perception 기반으로 Patrol Radius 설정 */ UCLASS() class PROJECTMJ_API UBTTask_MJFindPatrolPos : public UBTTaskNode diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.cpp new file mode 100644 index 00000000..b468b5aa --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.cpp @@ -0,0 +1,61 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJ/AI/BTTask_MJInstantAttack.h" +#include "AIController.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "AbilitySystemComponent.h" +#include "Abilities/GameplayAbility.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "ProjectMJ.h" +#include "Character/Component/MJEnemySkillComponent.h" + +UBTTask_MJInstantAttack::UBTTask_MJInstantAttack() +{ + NodeName = TEXT("InstantAttack"); +} + +EBTNodeResult::Type UBTTask_MJInstantAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); + if (ControlledPawn == nullptr) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Monster = Cast(ControlledPawn); + if (Monster == nullptr) + { + return EBTNodeResult::Failed; + } + + UAbilitySystemComponent* ASC = Monster->GetAbilitySystemComponent(); + UMJEnemySkillComponent* SkillComponent = Monster->GetSkillComponent(); + + FGameplayTag SkillTypeTag = FGameplayTag::RequestGameplayTag(FName("Skill.Instant")); + FGameplayTag AttackTag = SkillComponent->GetEquippedSkillMap()[SkillTypeTag]; + + if (!AttackTag.IsValid()) + { + return EBTNodeResult::Failed; + } + + FDelegateHandle Handle; + Handle = ASC->OnAbilityEnded.AddLambda( + [&, AttackTag, Handle](const FAbilityEndedData& EndedData) + { + MJ_LOG(LogMJ, Error,TEXT("A")); + if (EndedData.AbilityThatEnded->AbilityTags.HasTagExact(AttackTag)) + { + MJ_LOG(LogMJ, Error,TEXT("AA")); + FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); + ASC->OnAbilityEnded.Remove(Handle); + } + }); + MJ_LOG(LogMJ, Error,TEXT("AAA")); + + SkillComponent->ActivateSkill(AttackTag); + + return EBTNodeResult::Succeeded; +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.h b/Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.h similarity index 67% rename from Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.h rename to Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.h index 01eca15c..c17e0595 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJInstantAttack.h @@ -4,22 +4,22 @@ #include "CoreMinimal.h" #include "BehaviorTree/BTTaskNode.h" -#include "BTTask_MJMeleeAttack.generated.h" +#include "BTTask_MJInstantAttack.generated.h" /** - * Class Description: 근거리공격 노드 + * Class Description: Instant Attack * Author: Kim Minjin - * Created Date: 2025.07.01. + * Created Date: 2025.07.15. * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ UCLASS() -class PROJECTMJ_API UBTTask_MJMeleeAttack : public UBTTaskNode +class PROJECTMJ_API UBTTask_MJInstantAttack : public UBTTaskNode { GENERATED_BODY() public: - UBTTask_MJMeleeAttack(); + UBTTask_MJInstantAttack(); protected: virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.cpp index 35c923d8..8f453e7e 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.cpp +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.cpp @@ -5,8 +5,14 @@ #include "AIController.h" #include "NavigationSystem.h" +#include "ProjectMJ.h" #include "BehaviorTree/BlackboardComponent.h" -#include "MJ/Interface/MJCharacterAIInterface.h" +#include "Character/Component/MJEnemySkillComponent.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "Components/CapsuleComponent.h" +#include "DataTable/MJSkillDataRow.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "Navigation/PathFollowingComponent.h" UBTTask_MJKeepDistanceWhileMoveTo::UBTTask_MJKeepDistanceWhileMoveTo() { @@ -15,11 +21,19 @@ UBTTask_MJKeepDistanceWhileMoveTo::UBTTask_MJKeepDistanceWhileMoveTo() EBTNodeResult::Type UBTTask_MJKeepDistanceWhileMoveTo::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { + CachedOwnerComp = &OwnerComp; + AAIController* OwnerController = OwnerComp.GetAIOwner(); if (OwnerController == nullptr) { return EBTNodeResult::Failed; } + + UBlackboardComponent* BBComp = OwnerComp.GetBlackboardComponent(); + if (BBComp == nullptr) + { + return EBTNodeResult::Failed; + } APawn* ControlledPawn = Cast(OwnerComp.GetAIOwner()->GetPawn()); if (ControlledPawn == nullptr) @@ -27,8 +41,14 @@ EBTNodeResult::Type UBTTask_MJKeepDistanceWhileMoveTo::ExecuteTask(UBehaviorTree return EBTNodeResult::Failed; } - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (AIPawn == nullptr) + AMJCharacterBase* Character = Cast(ControlledPawn); + if (Character == nullptr) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Enemy = Cast(ControlledPawn); + if (Enemy == nullptr) { return EBTNodeResult::Failed; } @@ -38,57 +58,127 @@ EBTNodeResult::Type UBTTask_MJKeepDistanceWhileMoveTo::ExecuteTask(UBehaviorTree { return EBTNodeResult::Failed; } + + /* + * Minjin + * HowTo: 설정한 태그(스킬타입)를 통해 가지고 있는 태그와 비교해서 구체적인 태그를 가져온다. + * 가져온 태그로 Skill Set 가져온다. + * + */ + + UMJSkillComponentBase* SkillComp = Enemy->GetSkillComponent(); + int32 SkillLevel = SkillComp->GetOwnedSkillMap()[SkillTag].Level; + // 레벨 정보 가져옴 + + UDataTable* SkillDataTable = SkillComp->GetSkillDataTable(); + const FMJSkillDataRow* DataRow = SkillDataTable->FindRow(SkillTag.GetTagName(), TEXT("Find Skill Info")); + if (!DataRow) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist DataRow")); + return EBTNodeResult::Failed; return EBTNodeResult::Failed; + } + + UCurveTable* CurveTable = DataRow->SkillLevelDataTable.LoadSynchronous(); + if (!CurveTable) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist SkillLevelDataTable")); + return EBTNodeResult::Failed; + } + + FRealCurve* SkillRadiusCurve = CurveTable->FindCurve(TEXT("SkillRadius"), TEXT("FindSkillRadius")); + const float AttackRadius = SkillRadiusCurve->Eval(SkillLevel); + + FRealCurve* SkillRangeCurve = CurveTable->FindCurve(TEXT("SkillRange"), TEXT("FindSkillRange")); + const float SkillRange = SkillRangeCurve->Eval(SkillLevel); + + FRealCurve* SkillAttackLocationOffsetCurve = CurveTable->FindCurve(TEXT("SkillAttackLocationOffset"), TEXT("FindSkillAttackLocationOffset")); + const float Offset = SkillAttackLocationOffsetCurve->Eval(SkillLevel); + + const float AttackRange = FMath::Max(SkillRange , AttackRadius * 2.0f); + + const float MinimumAttackRange = Character->GetCapsuleComponent()->GetScaledCapsuleRadius() * ((Offset!=0)?Offset:1); + const float MaximumAttackRange = MinimumAttackRange + AttackRange; + + const int32 HalfAttackRange = FMath::FloorToInt(MinimumAttackRange + AttackRange * 0.5f); FVector AILocation = ControlledPawn->GetActorLocation(); FVector TargetLocation = TargetPawn->GetActorLocation(); float Distance = FVector::Dist(AILocation, TargetLocation); + + FVector MoveToLocation = FVector::ZeroVector; // Minjin: 거리가 MinimumAttackRange 미만일 경우->플레이어와 멀어진다 - if (Distance < AIPawn->GetAIMinimumAttackRange()|| - OwnerComp.GetBlackboardComponent()->GetValueAsBool("MinimumAttackRange")) + if (Distance < MinimumAttackRange) { FVector AwayDir = (AILocation - TargetLocation).GetSafeNormal(); FVector RandomDir = FMath::VRandCone(AwayDir, FMath::DegreesToRadians(30.f)); - FVector MoveToLocation = AILocation + RandomDir *(AIPawn->GetAIMaximumAttackRange() - Distance); - - // FNavLocation Destination; - // - // UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetNavigationSystem(ControlledPawn->GetWorld()); - // if (nullptr == NavSystem) - // { - // return EBTNodeResult::Failed; - // } - // // TODO 최소 이상 혹은 원거리 범위로 이동하도록 만들기 -> EQS_RandomPosInDonut - // - // NavSystem->GetRandomPointInNavigableRadius(AwayDir, AIPawn->GetAIMaximumAttackRange() * 1.5f , Destination); - // OwnerController->MoveToLocation(Destination); + MoveToLocation = AILocation + RandomDir *(HalfAttackRange - Distance); - OwnerController->MoveToLocation(MoveToLocation); + // OwnerController->MoveToLocation(MoveToLocation); - return EBTNodeResult::Succeeded; + // return EBTNodeResult::Succeeded; } // Minjin: 거리가 MaximumAttackRange보다 멀 경우->플레이어와 가까워진다 - if (Distance > AIPawn->GetAIMaximumAttackRange()|| - !OwnerComp.GetBlackboardComponent()->GetValueAsBool("MaximumAttackRange")) + if (Distance > MaximumAttackRange) { FVector ToDir = (TargetLocation - AILocation).GetSafeNormal(); - FVector RandomDir = FMath::VRandCone(ToDir, FMath::DegreesToRadians(30.f)); - - /* - * Minjin - * TODO - * 50.0f 안 더해주면 MaximumAttackRange가 true로 안 됨 - */ - FVector MoveToLocation = AILocation + RandomDir * (Distance +50.0f - AIPawn->GetAIMaximumAttackRange()); - OwnerController->MoveToLocation(MoveToLocation); + MoveToLocation = AILocation + ToDir * (Distance - HalfAttackRange); + + // // Minjin: AcceptanceRadius 0.0f로 설정-> 정확히 목표지점에 와야 도착으로 간주 + // OwnerController->MoveToLocation(MoveToLocation, 0.0f, false); - return EBTNodeResult::Succeeded; + // return EBTNodeResult::Succeeded; + } + + // // Minjin: Min~Max 범위에 있을 경우->제자리 + // OwnerController->StopMovement(); + + + // 이동 요청 생성 + FAIMoveRequest MoveReq; + MoveReq.SetGoalLocation(MoveToLocation); + MoveReq.SetAcceptanceRadius(0.0f); + MoveReq.SetUsePathfinding(true); + + FPathFollowingRequestResult MoveResult = OwnerController->MoveTo(MoveReq); + + + // 델리게이트 등록 (이동 완료 시 OnMoveCompleted 호출) + if (MoveResult.Code == EPathFollowingRequestResult::RequestSuccessful) + { + OwnerController->ReceiveMoveCompleted.AddDynamic(this, &UBTTask_MJKeepDistanceWhileMoveTo::OnMoveCompleted); + return EBTNodeResult::InProgress; + } + + return EBTNodeResult::Failed; +} + +EBTNodeResult::Type UBTTask_MJKeepDistanceWhileMoveTo::AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + AAIController* OwnerController = OwnerComp.GetAIOwner(); + if (OwnerController) + { + OwnerController->StopMovement(); + OwnerController->ReceiveMoveCompleted.RemoveDynamic(this, &UBTTask_MJKeepDistanceWhileMoveTo::OnMoveCompleted); } + return EBTNodeResult::Aborted; +} - // Minjin: Min~Max 범위에 있을 경우->제자리 - OwnerController->StopMovement(); +void UBTTask_MJKeepDistanceWhileMoveTo::OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result) +{ + if (!CachedOwnerComp) return; + + AAIController* OwnerController = CachedOwnerComp->GetAIOwner(); + OwnerController->ReceiveMoveCompleted.RemoveDynamic(this, &UBTTask_MJKeepDistanceWhileMoveTo::OnMoveCompleted); - return EBTNodeResult::Succeeded; + if (Result == EPathFollowingResult::Success) + { + FinishLatentTask(*CachedOwnerComp, EBTNodeResult::Succeeded); + } + else + { + FinishLatentTask(*CachedOwnerComp, EBTNodeResult::Failed); + } } diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.h b/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.h index bd7d7332..e0ed8922 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJKeepDistanceWhileMoveTo.h @@ -6,12 +6,17 @@ #include "BehaviorTree/BTTaskNode.h" #include "BTTask_MJKeepDistanceWhileMoveTo.generated.h" +namespace EPathFollowingResult +{ + enum Type : int; +} + /** * Class Description: 타겟과 거리를 유지하며 이동 * Author: Kim Minjin * Created Date: 2025.07.01. - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) + * Last Modified By: Kim Minjin + * Last Modified Date: (2025.08.10.) 하드코딩 수정했으나 사용 X */ UCLASS() class PROJECTMJ_API UBTTask_MJKeepDistanceWhileMoveTo : public UBTTaskNode @@ -23,4 +28,13 @@ class PROJECTMJ_API UBTTask_MJKeepDistanceWhileMoveTo : public UBTTaskNode protected: virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tag", meta=(Categories = "Skill")) + FGameplayTag SkillTag; + + UFUNCTION() + void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result); + + UBehaviorTreeComponent* CachedOwnerComp; }; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.cpp deleted file mode 100644 index 72f64202..00000000 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJMeleeAttack.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJ/AI/BTTask_MJMeleeAttack.h" - -#include "AIController.h" -#include "MJ/Interface/MJCharacterAIInterface.h" - -UBTTask_MJMeleeAttack::UBTTask_MJMeleeAttack() -{ - NodeName = TEXT("MeleeAttack"); -} - -EBTNodeResult::Type UBTTask_MJMeleeAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) -{ - EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory); - - APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); - if (nullptr == ControlledPawn) - { - return EBTNodeResult::Failed; - } - - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (nullptr == AIPawn) - { - return EBTNodeResult::Failed; - } - - AIPawn->MeleeAttackByAI(); - /* - * TODO - * 공격이 다 끝난 후에 완료 처리해야 한다. - */ - return EBTNodeResult::Succeeded; -} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.cpp new file mode 100644 index 00000000..ce535f40 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.cpp @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJ/AI/BTTask_MJNormalAttack.h" +#include "AIController.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "AbilitySystemComponent.h" +#include "Abilities/GameplayAbility.h" +#include "Character/Component/MJSkillComponentBase.h" +#include "ProjectMJ.h" +#include "Character/Component/MJEnemySkillComponent.h" + +UBTTask_MJNormalAttack::UBTTask_MJNormalAttack() +{ + NodeName = TEXT("Normal Attack"); +} + +EBTNodeResult::Type UBTTask_MJNormalAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); + if (ControlledPawn == nullptr) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Monster = Cast(ControlledPawn); + if (Monster == nullptr) + { + return EBTNodeResult::Failed; + } + + UAbilitySystemComponent* ASC = Monster->GetAbilitySystemComponent(); + UMJEnemySkillComponent* SkillComponent = Monster->GetSkillComponent(); + + SkillComponent->ActivateNormalSkill(); + + MJ_LOG(LogMJ, Warning, TEXT("Normal Attack")); + + return EBTNodeResult::Succeeded; +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJAttack.h b/Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.h similarity index 67% rename from Source/ProjectMJ/MJ/AI/BTTask_MJAttack.h rename to Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.h index 92d6c7ce..3ffd1633 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJAttack.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJNormalAttack.h @@ -4,22 +4,22 @@ #include "CoreMinimal.h" #include "BehaviorTree/BTTaskNode.h" -#include "BTTask_MJAttack.generated.h" +#include "BTTask_MJNormalAttack.generated.h" /** - * Class Description: 공격 + * Class Description: Normal Attack * Author: Kim Minjin - * Created Date: 2025.06.23. + * Created Date: 2025.07.15. * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ UCLASS() -class PROJECTMJ_API UBTTask_MJAttack : public UBTTaskNode +class PROJECTMJ_API UBTTask_MJNormalAttack : public UBTTaskNode { GENERATED_BODY() public: - UBTTask_MJAttack(); + UBTTask_MJNormalAttack(); protected: virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJPlayAppearance.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayAppearance.cpp index 54fc1a75..1a86aa54 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJPlayAppearance.cpp +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayAppearance.cpp @@ -2,7 +2,7 @@ #include "MJ/AI/BTTask_MJPlayAppearance.h" - +#include "MJ/Character/MJMonsterCharacter.h" #include "AIController.h" #include "BehaviorTree/BlackboardComponent.h" @@ -24,5 +24,14 @@ EBTNodeResult::Type UBTTask_MJPlayAppearance::ExecuteTask(UBehaviorTreeComponent OwnerComp.GetBlackboardComponent()->SetValueAsBool("IsAppear", true); + AMJMonsterCharacter* Character = Cast(ControlledPawn); + if (ControlledPawn) + { + if (Character->GetAppearanceAnimation()) + { + AnimationToPlay = Character->GetAppearanceAnimation(); + } + } + return Super::ExecuteTask(OwnerComp, NodeMemory); } diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.cpp new file mode 100644 index 00000000..658580b0 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.cpp @@ -0,0 +1,104 @@ +// ThenOneDayStudio + + +#include "MJ/AI/BTTask_MJPlayMontage.h" +#include "BTTask_MJPlayMontage.h" +#include "AIController.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "Kismet/GameplayStatics.h" +#include "Character/Component/MJEnemySkillComponent.h" + +UBTTask_MJPlayMontage::UBTTask_MJPlayMontage() +{ + NodeName=TEXT("Play Montage"); + + bNotifyTick = false; +} + + +EBTNodeResult::Type UBTTask_MJPlayMontage::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); + if (!ControlledPawn) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Monster = Cast(ControlledPawn); + if (!Monster) + { + return EBTNodeResult::Failed; + } + + APawn* Player = UGameplayStatics::GetPlayerPawn(GetWorld(), 0); + if (!Player) + { + return EBTNodeResult::Failed; + } + + UAnimInstance* Anim = ControlledPawn->FindComponentByClass()->GetAnimInstance(); + if (!Anim) + { + return EBTNodeResult::Failed; + } + + CachedComponent = &OwnerComp; + + FVector AILocation = ControlledPawn->GetActorLocation(); + FVector TargetLocation = Player->GetActorLocation(); + TargetLocation.Z = AILocation.Z; + + FRotator LookAtRotation = (TargetLocation - AILocation).Rotation(); + + ControlledPawn->SetActorRotation(LookAtRotation); + + + ControlledPawn->FindComponentByClass()->SetAnimationMode(EAnimationMode::AnimationBlueprint); + + UAbilitySystemComponent* ASC = Monster->GetAbilitySystemComponent(); + + + + //Anim->Montage_Play(MontagePlay); + if (!Anim->IsAnyMontagePlaying()) + { + Monster->GetSkillComponent()->ActivateIdentitySkill(); + } + if (SessionName != NAME_None) + { + ControlledPawn->GetWorldTimerManager().SetTimer( + WaitTimerHandle, + FTimerDelegate::CreateUObject(this, &UBTTask_MJPlayMontage::JumpToSection, Anim), + 3.0f, + false + ); + /*UBlackboardComponent* board = OwnerComp.GetBlackboardComponent(); + board->SetValueAsBool(TEXT("IsPatten"), false);*/ + } + UBlackboardComponent* board = OwnerComp.GetBlackboardComponent(); + board->SetValueAsBool(TEXT("IsPatten"), false); + Anim->OnMontageEnded.AddDynamic(this, &UBTTask_MJPlayMontage::OnMontageEnded); + + return EBTNodeResult::InProgress; +} + +void UBTTask_MJPlayMontage::JumpToSection(UAnimInstance* AnimInstance) +{ + if (AnimInstance ) + { + MontagePlay = AnimInstance->GetCurrentActiveMontage(); + AnimInstance->Montage_JumpToSection(SessionName, MontagePlay); + + } + +} + +void UBTTask_MJPlayMontage::OnMontageEnded(UAnimMontage* AnimMontage, bool bInterrupted) +{ + if (CachedComponent) + { + + FinishLatentTask(*CachedComponent, EBTNodeResult::Succeeded); + } +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.h b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.h new file mode 100644 index 00000000..27175f87 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJPlayMontage.h @@ -0,0 +1,42 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "BehaviorTree/BTTaskNode.h" +#include "BTTask_MJPlayMontage.generated.h" + +/** + * Class Description: BTTask_Play Montage + * Author: Lee Ju Hyeon + * Created Date: 2025.08.05. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +class UBehaviorTreeComponent; +UCLASS() +class PROJECTMJ_API UBTTask_MJPlayMontage : public UBTTaskNode +{ + GENERATED_BODY() + +public: + UBTTask_MJPlayMontage(); + + UPROPERTY(EditAnywhere, Category="Animation") + TObjectPtr MontagePlay; + UPROPERTY(EditAnywhere,Category="Animation") + FName SessionName; + + UFUNCTION() + void JumpToSection(UAnimInstance* AnimInstance); + + UFUNCTION() + void OnMontageEnded(UAnimMontage* AnimMontage, bool bInterrupted); +protected: + virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + +private: + FTimerHandle WaitTimerHandle; + UBehaviorTreeComponent* CachedComponent; + +}; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.cpp deleted file mode 100644 index 2f153426..00000000 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJ/AI/BTTask_MJRangedAttack.h" - -#include "AIController.h" -#include "MJ/Interface/MJCharacterAIInterface.h" - -UBTTask_MJRangedAttack::UBTTask_MJRangedAttack() -{ - NodeName = TEXT("RangedAttack"); -} - -EBTNodeResult::Type UBTTask_MJRangedAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) -{ - EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory); - - APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); - if (nullptr == ControlledPawn) - { - return EBTNodeResult::Failed; - } - - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (nullptr == AIPawn) - { - return EBTNodeResult::Failed; - } - - AIPawn->RangeAttackByAI(); - /* - * TODO - * 공격이 다 끝난 후에 완료 처리해야 한다. - */ - return EBTNodeResult::Succeeded; -} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.cpp new file mode 100644 index 00000000..923fc4f4 --- /dev/null +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.cpp @@ -0,0 +1,38 @@ +// ThenOneDayStudio + + +#include "MJ/AI/BTTask_MJSkillAttack.h" + +#include "AIController.h" +#include "ProjectMJ.h" +#include "Character/Component/MJEnemySkillComponent.h" +#include "MJ/Character/MJMonsterCharacter.h" + +UBTTask_MJSkillAttack::UBTTask_MJSkillAttack() +{ + NodeName = TEXT("SkillAttack"); +} + +EBTNodeResult::Type UBTTask_MJSkillAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + APawn* ControlledPawn = OwnerComp.GetAIOwner()->GetPawn(); + if (ControlledPawn == nullptr) + { + return EBTNodeResult::Failed; + } + + AMJMonsterCharacter* Monster = Cast(ControlledPawn); + if (Monster == nullptr) + { + return EBTNodeResult::Failed; + } + + UAbilitySystemComponent* ASC = Monster->GetAbilitySystemComponent(); + UMJEnemySkillComponent* SkillComponent = Monster->GetSkillComponent(); + + SkillComponent->ActivateIdentitySkill(); + + MJ_LOG(LogMJ, Warning, TEXT("Skill Attack")); + + return EBTNodeResult::Succeeded; +} diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.h b/Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.h similarity index 54% rename from Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.h rename to Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.h index 5eea85cc..3befa54e 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJRangedAttack.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJSkillAttack.h @@ -1,26 +1,27 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// ThenOneDayStudio #pragma once #include "CoreMinimal.h" #include "BehaviorTree/BTTaskNode.h" -#include "BTTask_MJRangedAttack.generated.h" +#include "BTTask_MJSkillAttack.generated.h" /** - * Class Description: 원거리공격 노드 + * Class Description: IdentitySkill을 이용한 공격 * Author: Kim Minjin - * Created Date: 2025.07.01. + * Created Date: 2025.08.01.. * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ UCLASS() -class PROJECTMJ_API UBTTask_MJRangedAttack : public UBTTaskNode +class PROJECTMJ_API UBTTask_MJSkillAttack : public UBTTaskNode { GENERATED_BODY() public: - UBTTask_MJRangedAttack(); + UBTTask_MJSkillAttack(); protected: virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + }; diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.cpp b/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.cpp index 7d327cb1..64d1b03f 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.cpp +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.cpp @@ -4,20 +4,32 @@ #include "MJ/AI/BTTask_MJTurnToTarget.h" #include "AIController.h" +#include "ProjectMJ.h" #include "BehaviorTree/BlackboardComponent.h" -#include "MJ/Interface/MJCharacterAIInterface.h" UBTTask_MJTurnToTarget::UBTTask_MJTurnToTarget() { NodeName = TEXT("Turn"); + + TimerDelegate = FTimerDelegate::CreateUObject(this, &UBTTask_MJTurnToTarget::OnRotated); } EBTNodeResult::Type UBTTask_MJTurnToTarget::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory); + // reset timer handle + // TimerHandle.Invalidate(); + MyOwnerComp = &OwnerComp; + + AAIController* Controller = Cast(OwnerComp.GetAIOwner()); + if (Controller == nullptr) + { + return EBTNodeResult::Failed; + } + APawn* ControlledPawn = Cast(OwnerComp.GetAIOwner()->GetPawn()); - if (nullptr == ControlledPawn) + if (ControlledPawn == nullptr) { return EBTNodeResult::Failed; } @@ -27,18 +39,60 @@ EBTNodeResult::Type UBTTask_MJTurnToTarget::ExecuteTask(UBehaviorTreeComponent& { return EBTNodeResult::Failed; } + + FVector LookVector = TargetPawn->GetActorLocation() - ControlledPawn->GetActorLocation(); + LookVector.Z = 0.0f; + TargetRot = FRotationMatrix::MakeFromX(LookVector).Rotator(); + + Controller->GetWorld()->GetTimerManager().SetTimer(TimerHandle, TimerDelegate, GetWorld()->GetDeltaSeconds(), /*bLoop=*/true); + + return EBTNodeResult::InProgress; +} - IMJCharacterAIInterface* AIPawn = Cast(ControlledPawn); - if (nullptr == AIPawn) +EBTNodeResult::Type UBTTask_MJTurnToTarget::AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +{ + AAIController* const Controller = OwnerComp.GetAIOwner(); + + if (Controller && TimerHandle.IsValid()) { - return EBTNodeResult::Failed; + Controller->GetWorld()->GetTimerManager().ClearTimer(TimerHandle); } + TimerHandle.Invalidate(); - float TurnSpeed = AIPawn->GetAITurnSpeed(); - FVector LookVector = TargetPawn->GetActorLocation() - ControlledPawn->GetActorLocation(); - LookVector.Z = 0.0f; - FRotator TargetRot = FRotationMatrix::MakeFromX(LookVector).Rotator(); - ControlledPawn->SetActorRotation(FMath::RInterpTo(ControlledPawn->GetActorRotation(), TargetRot, GetWorld()->GetDeltaSeconds(), TurnSpeed)); + return EBTNodeResult::Aborted; +} + +void UBTTask_MJTurnToTarget::OnRotated() +{ + AAIController* Controller = Cast(MyOwnerComp->GetOwner()); + if (Controller == nullptr) + { + FinishLatentTask(*MyOwnerComp, EBTNodeResult::Failed); + } - return EBTNodeResult::Succeeded; + APawn* ControlledPawn = Cast(Controller->GetPawn()); + if (ControlledPawn == nullptr) + { + FinishLatentTask(*MyOwnerComp, EBTNodeResult::Failed); + } + + /* + * Minjin + * TurnSpeed는 5.0f 이상이 좋아보입니다. + */ + float TurnSpeed = 5.0f; + FRotator NewRot = FMath::RInterpTo(ControlledPawn->GetActorRotation(), TargetRot, GetWorld()->GetDeltaSeconds(), TurnSpeed); + ControlledPawn->SetActorRotation(NewRot); + + // Minjin: 반내림 + int32 Angle = FMath::FloorToInt(FMath::Abs((NewRot - TargetRot).GetNormalized().Yaw)); + float AngleDiff = FMath::Abs((NewRot - TargetRot).GetNormalized().Yaw); + + if (/*Angle == 0*/AngleDiff < 1.0f) + { + MJ_LOG(LogMJ,Log, TEXT("회전완료")); + // we're done here, report success so that BT can pick next task + ControlledPawn->GetWorldTimerManager().ClearTimer(TimerHandle); + FinishLatentTask(*MyOwnerComp, EBTNodeResult::Succeeded); + } } diff --git a/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.h b/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.h index dc7c8cfe..5bf6bec7 100644 --- a/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.h +++ b/Source/ProjectMJ/MJ/AI/BTTask_MJTurnToTarget.h @@ -22,5 +22,16 @@ class PROJECTMJ_API UBTTask_MJTurnToTarget : public UBTTaskNode UBTTask_MJTurnToTarget(); protected: + UPROPERTY() + TObjectPtr MyOwnerComp; + + FRotator TargetRot; + + FTimerDelegate TimerDelegate; + FTimerHandle TimerHandle; + virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; + + void OnRotated(); }; diff --git a/Source/ProjectMJ/MJ/AI/MJMonsterAIControllerBase.cpp b/Source/ProjectMJ/MJ/AI/MJMonsterAIControllerBase.cpp index c932b05e..cc5f6a0a 100644 --- a/Source/ProjectMJ/MJ/AI/MJMonsterAIControllerBase.cpp +++ b/Source/ProjectMJ/MJ/AI/MJMonsterAIControllerBase.cpp @@ -66,16 +66,14 @@ void AMJMonsterAIControllerBase::RunAI() * 블랙보드 초기화, StartTree * StartLogic()에서 DefaultBehaviorTreeAsset(에디터에서 설정)을 시작에셋으로 설정해준다. */ + // BehaviorTreeComponent-> + // BehaviorTreeComponent.DefaultBehaviorTreeAsset BehaviorTreeComponent->StartLogic(); RunBehaviorTree(BehaviorTreeComponent->GetCurrentTree());; } void AMJMonsterAIControllerBase::StopAI() { - /* - * TODO - * Character가 죽을 때 호출해야 됨? - */ BehaviorTreeComponent->StopTree(); } diff --git a/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.cpp b/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.cpp index dc0115a6..06364d06 100644 --- a/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.cpp +++ b/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.cpp @@ -3,46 +3,16 @@ #include "MJ/Character/MJMeleeMonsterCharacter.h" -AMJMeleeMonsterCharacter::AMJMeleeMonsterCharacter() -{ -} - -float AMJMeleeMonsterCharacter::GetAIMaximumAttackRange() -{ - return 400.0f; -} -float AMJMeleeMonsterCharacter::GetAIMinimumAttackRange() +AMJMeleeMonsterCharacter::AMJMeleeMonsterCharacter() { - return 200.0f; } -void AMJMeleeMonsterCharacter::AttackByAI() +void AMJMeleeMonsterCharacter::BeginPlay() { - /* - * Minjin - * TODO - * 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Emerald, TEXT("MeleeMonster: 공격")); -} + Super::BeginPlay(); -void AMJMeleeMonsterCharacter::MeleeAttackByAI() -{ - /* - * Minjin - * TODO - * 근거리 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Emerald, TEXT("MeleeMonster: 근거리 공격")); -} + // // Minjin: 기본 공격 스킬 추가 + // AttackTag = FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack")); -void AMJMeleeMonsterCharacter::RangeAttackByAI() -{ - /* - * Minjin - * TODO - * 중거리 공격-돌진? - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Emerald, TEXT("MeleeMonster: 중거리 공격")); -} +} \ No newline at end of file diff --git a/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.h b/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.h index d6034e70..23e942c0 100644 --- a/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.h +++ b/Source/ProjectMJ/MJ/Character/MJMeleeMonsterCharacter.h @@ -18,11 +18,6 @@ class PROJECTMJ_API AMJMeleeMonsterCharacter : public AMJMonsterCharacter AMJMeleeMonsterCharacter(); protected: - virtual float GetAIMaximumAttackRange() override; - virtual float GetAIMinimumAttackRange() override; - virtual void AttackByAI() override; - virtual void MeleeAttackByAI() override; - virtual void RangeAttackByAI() override; - + virtual void BeginPlay() override; }; diff --git a/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.cpp b/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.cpp index a94f6bce..c5dd57d3 100644 --- a/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.cpp +++ b/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.cpp @@ -3,10 +3,30 @@ #include "MJ/Character/MJMonsterCharacter.h" +#include "UI/World/MJDamageComponent.h" +#include "ProjectMJ.h" #include "AbilitySystem/MJAbilitySystemComponent.h" #include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" #include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "Character/MJPlayerCharacter.h" +#include "Character/Component/MJEnemySkillComponent.h" +#include "Character/Component/MJEnemyStatComponent.h" +#include "DataAsset/DataAsset_StartDataBase.h" +#include "DataTable/MJEnemyDataRow.h" #include "GameFramework/CharacterMovementComponent.h" +#include "MJ/AI/MJMonsterAIControllerBase.h" +#include "Components/WidgetComponent.h" +#include "UI/World/MJDamageWidget.h" +#include "UI/MJUIManagerSubsystem.h" +#include "TG/MJGameInstance.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "DataAsset/MJStateAbilityDataAsset.h" +#include "Item/MJItemBase.h" +#include "Kismet/GameplayStatics.h" +#include "MJ/DataAssetMJ/MJDropItemsDataAsset.h" +#include "UI/Inventory/ItemDataRow.h" +#include "UI/Bar/MJEnemyHPBar.h" +#include "UI/Component/MJHealthBarComponent.h" AMJMonsterCharacter::AMJMonsterCharacter() { @@ -21,15 +41,25 @@ AMJMonsterCharacter::AMJMonsterCharacter() GetCharacterMovement()->bUseControllerDesiredRotation = true; bUseControllerRotationYaw = false; - - // GAS, Attr Set ASC = CreateDefaultSubobject(TEXT("ASC")); CharacterAttributeSet = CreateDefaultSubobject(TEXT("CharacterAttributeSet")); + // Stat Component + StatComponent = CreateDefaultSubobject(TEXT("StatComponent")); + CharacterSkillAttributeSet = CreateDefaultSubobject(TEXT("CharacterSkillAttributeSet")); - + + // Skill Component + SkillComponent = CreateDefaultSubobject(TEXT("SkillComponent")); + + // UI Component + HPBarComponent = CreateDefaultSubobject(TEXT("HPBarComponent")); + HPBarComponent->SetupAttachment(GetRootComponent()); + + // Minjin: ID 설정 + ID = ETeam_ID::MONSTER; } void AMJMonsterCharacter::BeginPlay() @@ -37,86 +67,186 @@ void AMJMonsterCharacter::BeginPlay() Super::BeginPlay(); // Minjin: 캐릭터는 미리 맵에 스폰되어 있다. 안 보이게 설정-콜리전을 비활성화 하면 맵 밑으로 꺼짐 - AActor::SetActorHiddenInGame(true); - SetActorEnableCollision(true); + // AActor::SetActorHiddenInGame(true); + // SetActorEnableCollision(true); + + // Jisoo + if (UMJEnemyHPBar* EnemyHPBar = Cast(HPBarComponent->GetUserWidgetObject())) + { + EnemyHPBar->BindToAttributes(ASC,CharacterAttributeSet); + } } -float AMJMonsterCharacter::GetAIPatrolRadius() +UAbilitySystemComponent* AMJMonsterCharacter::GetAbilitySystemComponent() const { - return 800.0f; + if (ASC) + { + return ASC; + } + return nullptr; } -float AMJMonsterCharacter::GetAITurnSpeed() +void AMJMonsterCharacter::PossessedBy(AController* NewController) { - return 2.0f; -} + Super::PossessedBy(NewController); -float AMJMonsterCharacter::GetAIMaximumAttackRange() -{ - // Minjin: 원거리 공격(근거리 몬스터의 경우 중거리 공격으로 활용 가능할 듯) - /* - * TODO - * 우선 하드코딩. 스탯 기반으로 바꾸기 - */ - //return Stat->GetTotalStat().AttackRange + Stat->GetAttackRadius()*2; - return 500.0f; -} + + if (ASC) + { + ASC->InitAbilityActorInfo(this,this); + } + + if (!CharacterStartData.IsNull()) + { + if (UDataAsset_StartDataBase* LoadData = CharacterStartData.LoadSynchronous()) + { + LoadData->GiveToAbilitySystemComponent(Cast(GetAbilitySystemComponent())); + } + } + + // TODO: + // 여기서 DT 받아와서 Attribute랑 몬스터가 가지고 있는 스킬 넣을거야 + // 그래서 이 몬스터를 구븐 할 수 있는 RowName이 Tag인 형식의 DT와 + // RowName이랑 똑같은 이름의 Tag(ex- Enemy.Dongmin)를 속성에서 가지고 있어서 BP로 양산할 때 넣는거야 + // 그런데 자동으로 DT에 있는 값으로 Attribute를 넣어 줄 거면 전체 덮어주는 Effect 가지고 있고, 노가다가 필요해서 + // 나중에 DT만들고 집중 안될 때 와서 작업 함 + // -동민 - + StatComponent->OnDamage.AddDynamic(this,&ThisClass::OnDamage); + StatComponent->OnDeath.AddDynamic(this, &ThisClass::OnDead); + // 알겠긔 -민진- -float AMJMonsterCharacter::GetAIMinimumAttackRange() -{ - // Minjin: 근거리 공격 /* - * TODO - * 우선 하드코딩. 스탯 기반으로 바꾸기 + * Minjin + * EnemyDataTable을 통해 스탯 초기화 + * 기본 태그와 행이름이 동일. 기본 태그를 통해 행을 가져온다. 기본 태그는 BP에서 설정 */ - //return Stat->GetTotalStat().AttackRange + Stat->GetAttackRadius()*2; - return 200.0f; -} + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->EnemyDataTable) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist GI or EnemyDataTable")); + return; + } -void AMJMonsterCharacter::AttackByAI() -{ - /* - * TODO - * 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("Attack")); -} + const FMJEnemyDataRow* DataRow = GI->EnemyDataTable->FindRow(DefaultEnemyTag.GetTagName(), TEXT("Find EnemyData")); + if (!DataRow) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist DataRow")); + return; + } + + // Minjin: Attribute Setting + UCurveTable* AttributeCurve = DataRow->AttributeCurve.LoadSynchronous(); + if (!AttributeCurve) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist AttributeCurve")); + return; + } + + StatComponent->SetStatTable(AttributeCurve); + StatComponent->InitializeStat(); + + SkillComponent->InitializeComponent(); + + // Minjin: Animation Setting + AppearanceAnimation = DataRow->AppearanceAnimation; + DeathAnimation = DataRow->DeathAnimation; + + // // Minjin: 죽었을 때 전달할 정보 Setting-SkillComponent에서 얻어온다. + EnemyBequest.IdentitySkillTag = SkillComponent->TryGiveMemory(); + EnemyBequest.Exp = ASC->GetSet()->GetDropExperience(); + + // Minjin: 지정한 확률로 아이템 태그를 얻음 + EnemyBequest.ItemTag = DataRow->DropItems->TryDropItem(); -void AMJMonsterCharacter::MeleeAttackByAI() -{ /* * Minjin - * TODO - * 근거리 공격 + * StateAbilityDataAsset 설정 */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("MeleeAttack")); + UMJStateAbilityDataAsset* StateAbility = DataRow->StateAbility; + if (!StateAbility) + { + MJ_LOG(LogMJ, Log, TEXT("Not Exist StateAbility")); + return; + } + + StateAbilityDataAsset = StateAbility; } -void AMJMonsterCharacter::RangeAttackByAI() +void AMJMonsterCharacter::GiveDeathRewardTo() { - /* - * Minjin - * TODO - * 원거리 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("RangeAttack")); -} + // Minjin: 애니메이션 재생되는 동안 경험치 전달 + AMJPlayerCharacter* Player = Cast(EnemyBequest.Target); + if (Player) + { + Player->StatComponent->GainExperience(EnemyBequest.Exp); + MJ_LOG(LogMJ, Warning, TEXT("경험치 전달: %d"), EnemyBequest.Exp); + } -UAbilitySystemComponent* AMJMonsterCharacter::GetAbilitySystemComponent() const -{ - if (ASC) + if (EnemyBequest.ItemTag.IsValid()) { - return ASC; + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->ItemDataTable) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist GI or ItemDataTable")); + return; + } + + const FItemDataRow* DataRow = GI->ItemDataTable->FindRow(EnemyBequest.ItemTag.GetTagName(), TEXT("Find ItemData")); + if (!DataRow) + { + MJ_LOG(LogMJ, Error, TEXT("Not Exist DataRow")); + return; + } + + TSubclassOf ItemClass = DataRow->ItemClass; + if (ItemClass) + { + FVector SpawnLocation = GetActorLocation(); + //SpawnLocation.Z = 0.0f; + FTransform SpawnTransform(SpawnLocation); + if (GetWorld()) + { + GetWorld()->SpawnActor(ItemClass, SpawnTransform); + } + } } - return nullptr; } -void AMJMonsterCharacter::PossessedBy(AController* NewController) +void AMJMonsterCharacter::OnDead(AActor* InEffectCauser) { - Super::PossessedBy(NewController); + if (bIsDying) + { + MJ_LOG(LogMJ, Warning, TEXT("%s: 죽는 중..."), *GetName()); + return; + } + + MJ_LOG(LogMJ, Warning, TEXT("%s: Death Start"), *GetName()); + // TODO: + // 애니메이션과 기타 등등 세팅 + // - 동민 - - if (ASC) + bIsDying = true; + + // Minin: Target 정보 저장 + EnemyBequest.Target = InEffectCauser; + + AMJMonsterAIControllerBase* AIController = Cast(GetController()); + if (AIController) { - ASC->InitAbilityActorInfo(this,this); + AIController->StopAI(); } + + // Minjin: Death Ability + FGameplayEventData EventData; + EventData.EventTag = FGameplayTag::RequestGameplayTag(FName("Event.Character.Dead")); + ASC->HandleGameplayEvent(EventData.EventTag, &EventData); + + DamageIndex = 0; +} + +void AMJMonsterCharacter::OnDamage(float Magnitude, bool bIsCritical) +{ + HPBarComponent->SetVisibility(true); + + FloatDamage(Magnitude, bIsCritical, EOwnerType::Monster); } diff --git a/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.h b/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.h index e1695d9a..dc31b7a9 100644 --- a/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.h +++ b/Source/ProjectMJ/MJ/Character/MJMonsterCharacter.h @@ -3,11 +3,21 @@ #pragma once #include "CoreMinimal.h" +#include "GameplayTagAssetInterface.h" +#include "GameplayTagContainer.h" #include "Character/MJCharacterBase.h" -#include "MJ/Interface/MJCharacterAIInterface.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystemComponent.h" #include "MJMonsterCharacter.generated.h" - +class UMJEnemySkillComponent; +class UMJDropItemsDataAsset; +class UMJHealthBarComponent; +class UMJDamageComponent; +class UWidgetComponent; +class UMJEnemyHPBar; +class UMJUIManagerSubsystem; +class UMJEnemyStatComponent; class UMJCharacterAttributeSet; class UMJCharacterSkillAttributeSet; /** @@ -17,37 +27,114 @@ class UMJCharacterSkillAttributeSet; * Last Modified By: (Last Modifier) * Last Modified Date: (Last Modified Date) */ +struct EnemyTransferData +{ + FGameplayTag IdentitySkillTag; + int32 Exp; + FGameplayTag ItemTag; + AActor* Target; +}; + UCLASS() -class PROJECTMJ_API AMJMonsterCharacter : public AMJCharacterBase, public IMJCharacterAIInterface +class PROJECTMJ_API AMJMonsterCharacter : public AMJCharacterBase, public IGameplayTagAssetInterface { GENERATED_BODY() public: AMJMonsterCharacter(); + FGameplayTag GetAttackTag() {return AttackTag;} + UMJEnemySkillComponent* GetSkillComponent() {return SkillComponent;} + virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; + + TObjectPtr GetAppearanceAnimation(){return AppearanceAnimation;} + + const EnemyTransferData& GetEnemyBequest(){return EnemyBequest;} + + const FGameplayTag& GetDefaultEnemyTag() {return DefaultEnemyTag;} + + UFUNCTION() + virtual void GiveDeathRewardTo(); + + // IGameplayTagAssetInterface + virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override + { + ASC->GetOwnedGameplayTags(TagContainer); + } + + virtual bool HasMatchingGameplayTag(FGameplayTag TagToCheck) const override + { + return ASC->HasMatchingGameplayTag(TagToCheck); + } + + bool HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override + { + return ASC->HasAllMatchingGameplayTags(TagContainer); + } + + bool HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override + { + return ASC->HasAllMatchingGameplayTags(TagContainer); + } protected: virtual void BeginPlay() override; - - // Minjin: Interface - virtual float GetAIPatrolRadius() override; - virtual float GetAITurnSpeed() override; - virtual float GetAIMaximumAttackRange() override; - virtual float GetAIMinimumAttackRange() override; + virtual void PossessedBy(AController* NewController) override; - virtual void AttackByAI() override; - virtual void MeleeAttackByAI() override; - virtual void RangeAttackByAI() override; +protected: + UFUNCTION() + virtual void OnDead(AActor* InEffectCauser); - virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; - virtual void PossessedBy(AController* NewController) override; + UFUNCTION() + void OnDamage(float Magnitude, bool bIsCritical); protected: + // Jisoo : UI Section + UPROPERTY() + TObjectPtr UIManager; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Widget, Meta = (AllowPrivateAccess = "true")) + TObjectPtr HPBarComponent; + + // UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Widget, Meta = (AllowPrivateAccess = "true")) + // TArray> DamageComponents; + int DamageIndex = 0; + float OffSet = 0; +protected: UPROPERTY(EditDefaultsOnly) TObjectPtr CharacterAttributeSet; UPROPERTY(EditDefaultsOnly) TObjectPtr CharacterSkillAttributeSet; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Gas") + TObjectPtr SkillComponent; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stat") + TObjectPtr StatComponent; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Enemy.Name")) + FGameplayTag DefaultEnemyTag; + + UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly) + bool bIsDead; + + UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly) + bool bIsDying = false; + + // Minjin: Animation-안 쓸 예정 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr AppearanceAnimation; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TObjectPtr DeathAnimation; + + // Minjin: Ability Tag-안씀 + FGameplayTag AttackTag; + + // Minjin: 죽고 플레이어한테 줘야 하는 정보 + EnemyTransferData EnemyBequest; + + FTimerHandle DeadTimerHandle; }; diff --git a/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.cpp b/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.cpp index 793a6c33..33d26ebb 100644 --- a/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.cpp +++ b/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.cpp @@ -7,37 +7,15 @@ AMJRangedMonsterCharacter::AMJRangedMonsterCharacter() { } -float AMJRangedMonsterCharacter::GetAIMaximumAttackRange() +void AMJRangedMonsterCharacter::BeginPlay() { - return 800.0f; -} - -float AMJRangedMonsterCharacter::GetAIMinimumAttackRange() -{ - return 200.0f; -} - -void AMJRangedMonsterCharacter::AttackByAI() -{ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("RangedMonster: 공격")); -} - -void AMJRangedMonsterCharacter::MeleeAttackByAI() -{ - /* - * Minjin - * TODO - * 근거리 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("RangedMonster: 근거리공격")); -} - -void AMJRangedMonsterCharacter::RangeAttackByAI() -{ - /* - * Minjin - * TODO - * 원거리 공격 - */ - GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("RangedMonster: 원거리공격")); -} + Super::BeginPlay(); + + // Minjin: 기본 원거리 공격 스킬 추가 + // SkillComponent->LearnSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Charge.Catastrophe"))); + // SkillComponent->EquipSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Charge.Catastrophe"))); + // + // // Minjin: 근거리 공격 스킬 추가 + // SkillComponent->LearnSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack"))); + // SkillComponent->EquipSkill(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.BasicMeleeAttack"))); +} \ No newline at end of file diff --git a/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.h b/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.h index 01b8de99..5587ef7b 100644 --- a/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.h +++ b/Source/ProjectMJ/MJ/Character/MJRangedMonsterCharacter.h @@ -22,10 +22,5 @@ class PROJECTMJ_API AMJRangedMonsterCharacter : public AMJMonsterCharacter AMJRangedMonsterCharacter(); protected: - virtual float GetAIMaximumAttackRange() override; - virtual float GetAIMinimumAttackRange() override; - - virtual void AttackByAI() override; - virtual void MeleeAttackByAI() override; - virtual void RangeAttackByAI() override; + virtual void BeginPlay() override; }; diff --git a/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.cpp b/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.cpp new file mode 100644 index 00000000..e62d257b --- /dev/null +++ b/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.cpp @@ -0,0 +1,20 @@ +// ThenOneDayStudio + + +#include "MJ/Character/MJSpiderMinionCharacter.h" +#include "AnimInstance/MJAnimInstanceBase.h" + +void AMJSpiderMinionCharacter::BeginPlay() +{ + Super::BeginPlay(); + + SetActorHiddenInGame(false); + SetActorEnableCollision(true); + +} + +void AMJSpiderMinionCharacter::SetbIsOpen(bool Value) +{ + UMJAnimInstanceBase* AnimInstance = Cast(GetMesh()->GetAnimInstance()); + AnimInstance->SetbIsOpen(Value); +} diff --git a/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.h b/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.h new file mode 100644 index 00000000..1c77a45e --- /dev/null +++ b/Source/ProjectMJ/MJ/Character/MJSpiderMinionCharacter.h @@ -0,0 +1,25 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "MJSpiderMinionCharacter.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API AMJSpiderMinionCharacter : public AMJMonsterCharacter +{ + GENERATED_BODY() + +protected: + + virtual void BeginPlay() override; + +public: + UFUNCTION(BlueprintCallable) + void SetbIsOpen(bool Value); + +}; diff --git a/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.cpp b/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.cpp new file mode 100644 index 00000000..d4f9400a --- /dev/null +++ b/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.cpp @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJ/Character/MJStoneElementCharacter.h" + +AMJStoneElementCharacter::AMJStoneElementCharacter() +{ +} + +void AMJStoneElementCharacter::BeginPlay() +{ + Super::BeginPlay(); + AttackTag = FGameplayTag::RequestGameplayTag(FName("Skill.Instant.StoneElementalMeleeAttack")); + +} diff --git a/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.h b/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.h new file mode 100644 index 00000000..fca7e404 --- /dev/null +++ b/Source/ProjectMJ/MJ/Character/MJStoneElementCharacter.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJ/Character/MJMonsterCharacter.h" +#include "MJStoneElementCharacter.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API AMJStoneElementCharacter : public AMJMonsterCharacter +{ + GENERATED_BODY() + +public: + AMJStoneElementCharacter(); + +protected: + virtual void BeginPlay() override; +}; diff --git a/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.cpp b/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.cpp new file mode 100644 index 00000000..f929a8a5 --- /dev/null +++ b/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.cpp @@ -0,0 +1,38 @@ +// ThenOneDayStudio + + +#include "MJ/DataAssetMJ/MJDropItemsDataAsset.h" + +#include "ProjectMJ.h" +#include "Kismet/KismetMathLibrary.h" +#include "TG/MJGameInstance.h" +#include "UI/Inventory/ItemDataRow.h" +#include "Item/MJItemBase.h" + +UMJDropItemsDataAsset::UMJDropItemsDataAsset() +{ +} + +FGameplayTag UMJDropItemsDataAsset::TryDropItem() const +{ + // Minjin: 아이템 배열 중 랜덤한 아이템 선택, 확률 적용해 드랍할지 말지 정함 + if(DropItems.IsEmpty()) + { + return FGameplayTag::EmptyTag; + } + + int32 IndexRamge = DropItems.Num()-1; + + int32 RandIndex = UKismetMathLibrary::RandomIntegerInRange(0, IndexRamge); + + FGameplayTag ItemTag = DropItems[RandIndex].ItemTag; + + bool IsCanDrop = UKismetMathLibrary::RandomBoolWithWeight(DropItems[RandIndex].DropChance); + + if (IsCanDrop) + { + return ItemTag; + } + + return FGameplayTag::EmptyTag; +} diff --git a/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.h b/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.h new file mode 100644 index 00000000..8dfd415d --- /dev/null +++ b/Source/ProjectMJ/MJ/DataAssetMJ/MJDropItemsDataAsset.h @@ -0,0 +1,46 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Engine/DataAsset.h" +#include "MJDropItemsDataAsset.generated.h" + +/** + * Class Description: Enemy가 가지고 있는 아이템에 대한 데이터에셋. + * Author: Kim Minjin + * Created Date: 2025.07.29. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ + +class AMJItemBase; + +USTRUCT(BlueprintType) +struct FDropItemInfo +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(Categories = "Item")) + FGameplayTag ItemTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0", Delta = "0.01", DisplayName = "Drop Chance (%)")) + float DropChance; // 0.0f ~ 1.0f; + // Required Condition // 아이템을 얻을 수 있는 조건(퀘스트 등) +}; + +UCLASS() +class PROJECTMJ_API UMJDropItemsDataAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + UMJDropItemsDataAsset(); + + FGameplayTag TryDropItem() const; + +protected: + UPROPERTY(EditAnywhere) + TArray DropItems; +}; diff --git a/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.cpp b/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.cpp new file mode 100644 index 00000000..0a3f3ecb --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.cpp @@ -0,0 +1,64 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJ/Drop/MJSkillDropTest.h" + +#include "GameplayTagContainer.h" +#include "Character/MJPlayerCharacter.h" +#include "Components/BoxComponent.h" +#include "Character/Component/MJPlayerSkillComponent.h" + +// Sets default values +AMJSkillDropTest::AMJSkillDropTest() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + Trigger = CreateDefaultSubobject(TEXT("Trigger")); + Mesh = CreateDefaultSubobject(TEXT("Mesh")); + + RootComponent = Trigger; + Mesh->SetupAttachment(Trigger); + + Trigger->SetBoxExtent(FVector(15.0f, 15.0f, 30.0f)); + + static ConstructorHelpers::FObjectFinder MeshRef( + TEXT("/Script/Engine.StaticMesh'/Game/Assets/StylizedLandscapeKit/Meshes/Flowers_02_SM.Flowers_02_SM'")); + if (MeshRef.Object) + { + Mesh->SetStaticMesh(MeshRef.Object); + } + Mesh->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f)); + Mesh->SetCollisionProfileName(TEXT("NoCollision")); + + // 임시 테스트 + //SkillTag = FGameplayTag::RequestGameplayTag(FName("Skill.Instant.AirArrow")); + + Trigger->OnComponentBeginOverlap.AddDynamic(this, &AMJSkillDropTest::OnOverlapBegin); +} + +// Called when the game starts or when spawned +void AMJSkillDropTest::BeginPlay() +{ + Super::BeginPlay(); + +} + +void AMJSkillDropTest::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, + UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult) +{ + if (!SkillTag.IsValid()) + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("Skill is not Valid")); + Destroy(); + return; + } + + AMJPlayerCharacter* Player = Cast(OtherActor); + if (Player) + { + Player->SkillComponent->LearnSkill(SkillTag); + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("LearnSkill!")); + Destroy(); + } +} + diff --git a/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.h b/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.h new file mode 100644 index 00000000..c40c3a6f --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/MJSkillDropTest.h @@ -0,0 +1,46 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "GameFramework/Actor.h" +#include "MJSkillDropTest.generated.h" + +/** + * Class Description: SkillDropTest-테스트용 + * Author: Kim Minjin + * Created Date: 2025.07. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +UCLASS() +class PROJECTMJ_API AMJSkillDropTest : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + AMJSkillDropTest(); + + void SetSkillTag(const FGameplayTag& Tag) { SkillTag = Tag;} + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + UFUNCTION() + void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult); + + + UPROPERTY(VisibleAnywhere, Category = Box) + TObjectPtr Trigger; + + UPROPERTY(VisibleAnywhere, Category = Box) + TObjectPtr Mesh; + + FGameplayTag SkillTag; + +public: + +}; diff --git a/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.cpp b/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.cpp new file mode 100644 index 00000000..a00744b2 --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.cpp @@ -0,0 +1,166 @@ +// ThenOneDayStudio + + +#include "MJ/Drop/MJTargetingProjectileBase.h" + +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" +#include "ProjectMJ.h" +#include "AbilitySystem/Actor/MJProjectileBase.h" +#include "Character/MJPlayerCharacter.h" +#include "Character/Component/MJPlayerSkillComponent.h" +#include "Components/SphereComponent.h" +#include "GameFramework/ProjectileMovementComponent.h" +#include "Kismet/GameplayStatics.h" +#include "Kismet/KismetMathLibrary.h" +#include "Physics/MJCollision.h" +#include "Sound/SoundCue.h" + +// Sets default values +AMJTargetingProjectileBase::AMJTargetingProjectileBase() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + + // Sphere Section + Sphere = CreateDefaultSubobject("Projectile"); + SetRootComponent(Sphere); + Sphere->SetCollisionObjectType(CCHANNEL_MJPROJECTILE); + Sphere->SetCollisionProfileName(CRPOFILE_MJPROJECTILE); + + // Niagara Section + NiagaraComponent = CreateDefaultSubobject("NiagaraComponent"); + NiagaraComponent->SetupAttachment(RootComponent); + + // Movement Section + ProjectileMovement = CreateDefaultSubobject("ProjectileMovement"); + ProjectileMovement->ProjectileGravityScale = 0.0f; +} + +// Called when the game starts or when spawned +void AMJTargetingProjectileBase::BeginPlay() +{ + Super::BeginPlay(); + + UNiagaraComponent* MuzzleFxComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, MuzzleFX, GetActorLocation()); + + Sphere->OnComponentBeginOverlap.AddDynamic(this, &AMJTargetingProjectileBase::OnSphereOverlap); + TargetLocationChanged.BindUObject(this, &AMJTargetingProjectileBase::OnTargetUpdated); + + FVector ProjectileLocation = GetActorLocation(); + CurrentTargetLocation = Target->GetActorLocation(); + + PreTargetLocation = CurrentTargetLocation; + FRotator GoalRotation = UKismetMathLibrary::FindLookAtRotation(ProjectileLocation, CurrentTargetLocation); + + SetActorRotation(GoalRotation); + + FRotator ProjectileRotation = GetActorRotation(); + FVector Velocity = GetVelocity(); + double Length = Velocity.Length(); + FVector NewVelocity = UKismetMathLibrary::CreateVectorFromYawPitch(ProjectileRotation.Yaw, ProjectileRotation.Pitch, Length); + ProjectileMovement->Velocity = NewVelocity; + + ProjectileMovement->InitialSpeed = 300.0f; + ProjectileMovement->MaxSpeed = 600.0f; + + ProjectileMovement->bIsHomingProjectile = true; + ProjectileMovement->HomingAccelerationMagnitude = 300.0f; + ProjectileMovement->HomingTargetComponent = Target->GetRootComponent(); + + if(!ProjectileMovement->HomingTargetComponent.IsValid()) + { + MJ_LOG(LogMJ, Warning, TEXT("HomingTarget is not Valid.")); + Destroy(); + } +} + +void AMJTargetingProjectileBase::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (Target) + { + FVector TargetLocation = Target->GetActorLocation(); + if (TargetLocation != CurrentTargetLocation) + { + PreTargetLocation = CurrentTargetLocation; + CurrentTargetLocation = TargetLocation; + + TargetLocationChanged.ExecuteIfBound(); + } + + float Distance = GetDistanceTo(Target); + ProjectileMovement->MaxSpeed = FMath::GetMappedRangeValueClamped(FVector2D(0.0f, 600.0f), FVector2D(1.0f, 600.0f), Distance); + } + // MJ_LOG(LogMJ, Warning, TEXT("MaxSpeed: %f"), ProjectileMovement->MaxSpeed); + + // if (GEngine) + // { + // GEngine->AddOnScreenDebugMessage( + // -1, 3.f, FColor::Green,FString::Printf(TEXT("Velocity> %f"), ProjectileMovement->Velocity.Size())); + // } +} + +void AMJTargetingProjectileBase::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) +{ + // Minjin: Target과 같으면 성공 + if (OtherActor != Target) + { + return; + } + + // Minjin: 플레이어라면 skill 전달 + AMJPlayerCharacter* Player = Cast(OtherActor); + if (Player) + { + if (SkillTag.IsValid()) + { + Player->SkillComponent->LearnSkill(SkillTag); + UGameplayStatics::PlaySound2D(GetWorld(),OverlapSFX); + if (GEngine) + { + GEngine->AddOnScreenDebugMessage( + -1, 3.f, FColor::Green,TEXT("Learn Skill!")); + } + } + } + + UNiagaraComponent* FxComp = UNiagaraFunctionLibrary::SpawnSystemAttached( + HitFX, + Player->GetMesh(), + FName("Aim_Target")/*SweepResult.BoneName*/, + FVector::ZeroVector, // 시작 위치 + FRotator::ZeroRotator, + EAttachLocation::Type::SnapToTarget, // 처음 위치는 유지, 이후엔 부모 따라감 + true, // bAutoDestroy + true, + ENCPoolMethod::None, + true + ); + //FxComp->OnSystemFinished.AddDynamic(this, &AMJTargetingProjectileBase::OnEnded);; + + Destroy(); +} + +void AMJTargetingProjectileBase::OnTargetUpdated() +{ + FVector ProjectileLocation = GetActorLocation(); + FRotator GoalRotation = UKismetMathLibrary::FindLookAtRotation(ProjectileLocation, CurrentTargetLocation); + + SetActorRotation(GoalRotation); + + FRotator ProjectileRotation = GetActorRotation(); + FVector Velocity = GetVelocity(); + double Length = Velocity.Length(); + + FVector NewVelocity = UKismetMathLibrary::CreateVectorFromYawPitch(ProjectileRotation.Yaw, ProjectileRotation.Pitch, Length); + ProjectileMovement->Velocity = NewVelocity; +} + +void AMJTargetingProjectileBase::OnEnded(UNiagaraComponent* PSystem) +{ + MJ_LOG(LogMJ, Warning, TEXT("Projectile is Destroyed")); + Destroy(); +} + diff --git a/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.h b/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.h new file mode 100644 index 00000000..e118b2a5 --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/MJTargetingProjectileBase.h @@ -0,0 +1,83 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "GameFramework/Actor.h" +#include "MJTargetingProjectileBase.generated.h" + +DECLARE_DELEGATE(FOnLocationChangedDelegate); + +class UProjectileMovementComponent; +class UNiagaraComponent; +class USphereComponent; +class AMJCharacterBase; +class UNiagaraSystem; +/** + * Class Description: 나이아가라 적용. 이벤트 처리 + * Author: Kim Minjin + * Created Date: 2025.07.24. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +UCLASS() +class PROJECTMJ_API AMJTargetingProjectileBase : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + AMJTargetingProjectileBase(); + + void SetTarget(AActor* TargetActor) {Target = TargetActor;}; + + void SetSkillTag(const FGameplayTag& Tag) {SkillTag = Tag;} + + FOnLocationChangedDelegate TargetLocationChanged; + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + virtual void Tick(float DeltaSeconds) override; + + UFUNCTION() + virtual void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); + + UFUNCTION() + virtual void OnTargetUpdated(); + + UFUNCTION() + virtual void OnEnded(UNiagaraComponent* PSystem); + + UPROPERTY() + TObjectPtr Sphere; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile", meta = (AllowPrivateAccess = "true")) + TObjectPtr NiagaraComponent; + + UPROPERTY(VisibleAnywhere) + TObjectPtr ProjectileMovement; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) + TObjectPtr MuzzleFX; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) + TObjectPtr ProjectileFX; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) + TObjectPtr HitFX; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) + TObjectPtr Target; + + FGameplayTag SkillTag; + + FVector PreTargetLocation; + FVector CurrentTargetLocation; + + UPROPERTY(EditDefaultsOnly) // TG : 25.08.11 + TObjectPtr OverlapSFX; + +}; diff --git a/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.cpp b/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.cpp new file mode 100644 index 00000000..de4702c2 --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.cpp @@ -0,0 +1,77 @@ +// ThenOneDayStudio + + +#include "MJ/Drop/Notify_MJSpawnTargetProjectile.h" + +#include "MJTargetingProjectileBase.h" +#include "ProjectMJ.h" +#include "Character/MJPlayerCharacter.h" +#include "MJ/Character/MJMonsterCharacter.h" + +UNotify_MJSpawnTargetProjectile::UNotify_MJSpawnTargetProjectile() +{ +} + +void UNotify_MJSpawnTargetProjectile::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, + const FAnimNotifyEventReference& EventReference) +{ + Super::Notify(MeshComp, Animation, EventReference); + if (MeshComp) + { + // TODO: Get Bone Location by Name 알아보기 + // FTransform SpawnTransform(MeshComp->GetSkeletalCenterOfMass()); + + AActor* OwnerActor = MeshComp->GetOwner(); + if (OwnerActor == nullptr) + { + // Minjin: Actor로 사용한다면 수정하면 될 것 같슴니다. + return; + } + + // Minjin: PlayerCharacter일 때 + AMJPlayerCharacter* Player = Cast(OwnerActor); + if (Player) + { + // Minjin: 필요할진 모르겠지만 놔둬봅니다. + if (TargetingProjectile) + { + FVector SpawnLocation = Player->GetActorLocation(); + SpawnLocation.Z = 0.0f; + FTransform SpawnTransform(SpawnLocation); + AMJTargetingProjectileBase* Projectile = Player->GetWorld()->SpawnActorDeferred(TargetingProjectile, SpawnTransform/*, this, this, ESpawnActorCollisionHandlingMethod::AlwaysSpawn*/); + MJ_LOG(LogMJ, Warning, TEXT("Create TargetProjectile")); + + Projectile->FinishSpawning(SpawnTransform); + } + } + + // Minjin: Enemy일 때 + AMJMonsterCharacter* Enemy = Cast(OwnerActor); + if (Enemy) + { + if (TargetingProjectile) + { + if (Enemy->GetEnemyBequest().Target == nullptr) + { + MJ_LOG(LogMJ, Warning, TEXT("Target is nullptr")); + return; + } + if (!Enemy->GetEnemyBequest().IdentitySkillTag.IsValid()) + { + MJ_LOG(LogMJ, Warning, TEXT("SkillTage is not valid")); + return; + } + FVector SpawnLocation = Enemy->GetActorLocation(); + SpawnLocation.Z = 0.0f; + FTransform SpawnTransform(SpawnLocation); + AMJTargetingProjectileBase* Projectile = Enemy->GetWorld()->SpawnActorDeferred(TargetingProjectile, SpawnTransform/*, this, this, ESpawnActorCollisionHandlingMethod::AlwaysSpawn*/); + MJ_LOG(LogMJ, Warning, TEXT("Create TargetProjectile")); + + Projectile->SetTarget(Enemy->GetEnemyBequest().Target); + Projectile->SetSkillTag(Enemy->GetEnemyBequest().IdentitySkillTag); + + Projectile->FinishSpawning(SpawnTransform); + } + } + } +} diff --git a/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.h b/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.h new file mode 100644 index 00000000..c4c50a96 --- /dev/null +++ b/Source/ProjectMJ/MJ/Drop/Notify_MJSpawnTargetProjectile.h @@ -0,0 +1,31 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimNotifies/AnimNotify.h" +#include "Notify_MJSpawnTargetProjectile.generated.h" + +class AMJTargetingProjectileBase; +/** + * Class Description: 타게팅 발사체 스폰. 노티파이를 통해 스폰됨 + * Author: Kim Minjin + * Created Date: 2025.07.26. + * Last Modified By: (Last Modifier) + * Last Modified Date: (Last Modified Date) + */ +UCLASS() +class PROJECTMJ_API UNotify_MJSpawnTargetProjectile : public UAnimNotify +{ + GENERATED_BODY() + +public: + UNotify_MJSpawnTargetProjectile(); + +protected: + virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile") + TSubclassOf TargetingProjectile; + +}; diff --git a/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.cpp b/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.cpp deleted file mode 100644 index c91fbaf5..00000000 --- a/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJ/Interface/MJCharacterAIInterface.h" - -// Add default functionality here for any IMJCharacterAIInterface functions that are not pure virtual. diff --git a/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.h b/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.h deleted file mode 100644 index 9550411f..00000000 --- a/Source/ProjectMJ/MJ/Interface/MJCharacterAIInterface.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 "UObject/Interface.h" -#include "MJCharacterAIInterface.generated.h" - -// This class does not need to be modified. -UINTERFACE(MinimalAPI) -class UMJCharacterAIInterface : public UInterface -{ - GENERATED_BODY() -}; - -/** - * Class Description: AICharacter Interface-Task, Perception 관련 데이터 - * Author: Kim Minjin - * Created Date: 2025.06.22. - * Last Modified By: (Last Modifier) - * Last Modified Date: (Last Modified Date) - */ -class PROJECTMJ_API IMJCharacterAIInterface -{ - GENERATED_BODY() - - // Add interface functions to this class. This is the class that will be inherited to implement this interface. -public: - virtual float GetAIPatrolRadius() = 0; - virtual float GetAITurnSpeed() = 0; - virtual float GetAIMaximumAttackRange() = 0; - virtual float GetAIMinimumAttackRange() = 0; - - virtual void AttackByAI() = 0; - virtual void MeleeAttackByAI() = 0; - virtual void RangeAttackByAI() = 0; -}; diff --git a/Source/ProjectMJ/Physics/MJCollision.h b/Source/ProjectMJ/Physics/MJCollision.h index 239b6278..48400040 100644 --- a/Source/ProjectMJ/Physics/MJCollision.h +++ b/Source/ProjectMJ/Physics/MJCollision.h @@ -5,4 +5,10 @@ #define CPROFILE_MJCAPSULE TEXT("MJCapsule") #define CPROFILE_MJTRIGGER TEXT("MJTrigger") -#define CCHANNEL_MJAbilityTargetTrace ECC_GameTraceChannel1 \ No newline at end of file +#define CRPOFILE_MJPROJECTILE TEXT("MJProjectile") + +#define CCHANNEL_MJAbilityTargetTrace ECC_GameTraceChannel1 +#define CCHANNEL_MJPROJECTILE ECC_GameTraceChannel2 +#define CCHANNEL_MJGROUND ECC_GameTraceChannel3 +// ECollisionChannel::ECC_GameTraceChannel2 +// 이렇게 하는 방식 있는데 뭐가 다른지 찾아볼것 \ No newline at end of file diff --git a/Source/ProjectMJ/Player/MJPlayerState.cpp b/Source/ProjectMJ/Player/MJPlayerState.cpp index b09ddfa9..77a72714 100644 --- a/Source/ProjectMJ/Player/MJPlayerState.cpp +++ b/Source/ProjectMJ/Player/MJPlayerState.cpp @@ -6,8 +6,11 @@ #include "AbilitySystem/MJAbilitySystemComponent.h" #include "AbilitySystem//Attributes/MJCharacterAttributeSet.h" #include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" +#include "Character/MJPlayerCharacter.h" +#include "Character/Component/MJPlayerSkillComponent.h" +#include "Character/Component/MJPlayerStatComponent.h" #include "Kismet/GameplayStatics.h" -#include "TG/MJGameInstanceTG.h" +#include "TG/MJGameInstance.h" AMJPlayerState::AMJPlayerState() { @@ -18,7 +21,6 @@ AMJPlayerState::AMJPlayerState() CharacterSkillAttributeSet = CreateDefaultSubobject(TEXT("CharacterSkillAttributeSet")); } - UAbilitySystemComponent* AMJPlayerState::GetAbilitySystemComponent() const { return ASC; @@ -36,18 +38,57 @@ FMJPlayerSessionData& AMJPlayerState::GetPlayerSessionDataRef() void AMJPlayerState::SaveToInstancedPlayerSessionData() { - PlayerSessionData.CharacterAttribute = *CharacterAttributeSet; - PlayerSessionData.CharacterSkillAttribute = *CharacterSkillAttributeSet; - GetGameInstance()->GetPlayerSessionDataRef() = PlayerSessionData; + FMJPlayerSessionData& MJGIPlayerSessionData = GetGameInstance()->GetPlayerSessionDataRef(); - MJ_LOG(LogTG,Warning, TEXT("PlayerState Copied!!")); + UMJPlayerStatComponent* PlayerStatComp = GetPawn()->FindComponentByClass(); + if (PlayerStatComp) + { + MJGIPlayerSessionData.PlayerLevel = PlayerStatComp->GetPlayerLevel(); + MJGIPlayerSessionData.PlayerExp = PlayerStatComp->GetTotalCumulativeExperience(); + } + + UMJPlayerSkillComponent* PlayerSkillComponent = GetPawn()->FindComponentByClass(); + if (PlayerSkillComponent) + { + MJGIPlayerSessionData.SetCurrentEquippedSkillMap(PlayerSkillComponent->GetEquippedSkillMap()); + MJGIPlayerSessionData.SetCurrentOwnedSKillMap(PlayerSkillComponent->GetOwnedSkillMap()); + } } void AMJPlayerState::LoadFromInstancedPlayerSessionData() { - PlayerSessionData = GetGameInstance()->GetPlayerSessionDataRef(); - PlayerSessionData.CharacterAttribute.ApplyToAttributeSet(*CharacterAttributeSet); - PlayerSessionData.CharacterSkillAttribute.ApplyToAttributeSet(*CharacterSkillAttributeSet); + PlayerSessionData = GetGameInstance()->GetPlayerSessionDataRef(); + PlayerSessionData.PlayerLevel = GetGameInstance()->GetPlayerSessionDataRef().PlayerLevel; + PlayerSessionData.PlayerExp = GetGameInstance()->GetPlayerSessionDataRef().PlayerExp; + + if (AMJPlayerCharacter* MJPlayer = Cast(GetPawn())) + { + if (UMJPlayerStatComponent* PlayerStatComp = MJPlayer->FindComponentByClass()) + { + PlayerStatComp->SetPlayerLevel(PlayerSessionData.PlayerLevel); + PlayerStatComp->SetTotalCumulativeExperience(PlayerSessionData.PlayerExp); + PlayerStatComp->InitializeStat(); + } + UMJPlayerSkillComponent* PlayerSkillComponent = GetPawn()->FindComponentByClass(); + if (PlayerSkillComponent) + { + PlayerSkillComponent->SetOwnedSkillMap(PlayerSessionData.CurrentOwnedSkillMap); + PlayerSkillComponent->SetEquippedSkillMap(PlayerSessionData.CurrentEquippedSkillMap); + } + + } +} + +void AMJPlayerState::PostInitializeComponents() +{ + Super::PostInitializeComponents(); +} + +void AMJPlayerState::BeginPlay() +{ + Super::BeginPlay(); + + LoadFromInstancedPlayerSessionData(); } diff --git a/Source/ProjectMJ/Player/MJPlayerState.h b/Source/ProjectMJ/Player/MJPlayerState.h index d0a99d0d..0083f096 100644 --- a/Source/ProjectMJ/Player/MJPlayerState.h +++ b/Source/ProjectMJ/Player/MJPlayerState.h @@ -39,6 +39,9 @@ class PROJECTMJ_API AMJPlayerState : public APlayerState, public IAbilitySystemI protected: + virtual void PostInitializeComponents() override; + + virtual void BeginPlay() override; UPROPERTY(EditAnywhere, Category = "GAS") TObjectPtr ASC; @@ -55,3 +58,4 @@ class PROJECTMJ_API AMJPlayerState : public APlayerState, public IAbilitySystemI }; + diff --git a/Source/ProjectMJ/ProjectMJ.Build.cs b/Source/ProjectMJ/ProjectMJ.Build.cs index e0499e8f..6d083732 100644 --- a/Source/ProjectMJ/ProjectMJ.Build.cs +++ b/Source/ProjectMJ/ProjectMJ.Build.cs @@ -15,7 +15,8 @@ public ProjectMJ(ReadOnlyTargetRules Target) : base(Target) "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Niagara", "AIModule", "GameplayAbilities", "GameplayTags", "GameplayTasks", "NavigationSystem", "PhysicsCore", "Json", "JsonUtilities", "HTTP", - "EnhancedInput", "MoviePlayer" + "EnhancedInput", "MoviePlayer", "Slate", "SlateCore", "MotionWarping", + "ProceduralMeshComponent", "Landscape" }); PrivateDependencyModuleNames.AddRange(new string[] { }); diff --git a/Source/ProjectMJ/ProjectMJ.h b/Source/ProjectMJ/ProjectMJ.h index 1b347020..3b9d9845 100644 --- a/Source/ProjectMJ/ProjectMJ.h +++ b/Source/ProjectMJ/ProjectMJ.h @@ -6,6 +6,11 @@ #define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__) #define MJ_LOG(LogCat, Verbosity, Format, ...) UE_LOG(LogCat, Verbosity, TEXT("%s %s"), LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__)) +#define MJ_SLOG(Delay, Color, Format, ...) GEngine->AddOnScreenDebugMessage(-1, Delay, Color, FString::Printf(Format, ##__VA_ARGS__)); + +// static MapName +#define MAP_TOWN TEXT("TG_Town") +#define MAP_MAINMENU TEXT("TG_MainMenu") // cpp 파일에 DEFINE_LOG_CATEGORY 선언 필요함 DECLARE_LOG_CATEGORY_EXTERN(LogMJ, Log, All); diff --git a/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.cpp b/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.cpp new file mode 100644 index 00000000..b0b9238e --- /dev/null +++ b/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.cpp @@ -0,0 +1,95 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJEnvQueryGenerator_MJDonut.h" + +#include "EnvironmentQuery/EQSTestingPawn.h" +#include "EnvironmentQuery/Contexts/EnvQueryContext_Querier.h" +#include "EnvironmentQuery/Generators/EnvQueryGenerator_Donut.h" +#include "Kismet/GameplayStatics.h" + + +UMJEnvQueryGenerator_MJDonut::UMJEnvQueryGenerator_MJDonut(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) +{ + Center = UEnvQueryContext_Querier::StaticClass(); + InnerRadius.DefaultValue = 700.0f; + OuterRadius.DefaultValue = 1300.0f; + NumberOfRandomPoints.DefaultValue = 10; + NumberOfRings.DefaultValue = 3; + PointsPerRing.DefaultValue = 8; + ArcDirection.DirMode = EEnvDirection::TwoPoints; + ArcDirection.LineFrom = UEnvQueryContext_Querier::StaticClass(); + ArcDirection.Rotation = UEnvQueryContext_Querier::StaticClass(); + ArcAngle.DefaultValue = 360.f; + bUseSpiralPattern = false; + + ProjectionData.TraceMode = EEnvQueryTrace::Navigation; +} + +void UMJEnvQueryGenerator_MJDonut::GenerateItems(FEnvQueryInstance& QueryInstance) const +{ + // Didn`t Call Super cuz it`s customization of Engine API. + //Super::GenerateItems(QueryInstance); + + TArray CenterPoints; + QueryInstance.PrepareContext(Center, CenterPoints); + + if (CenterPoints.Num() <= 0) + { + return; + } + + UObject* BindOwner = QueryInstance.Owner.Get(); + InnerRadius.BindData(BindOwner, QueryInstance.QueryID); + OuterRadius.BindData(BindOwner, QueryInstance.QueryID); + + FVector::FReal InnerRadiusValue = InnerRadius.GetValue(); + FVector::FReal OuterRadiusValue = OuterRadius.GetValue(); + int32 NumPoints = NumberOfRandomPoints.GetValue(); + + if ((InnerRadiusValue < 0.) || (OuterRadiusValue <= 0.) || + (InnerRadiusValue > OuterRadiusValue)) + { + return; + } + + TArray Points; + Points.Reserve(NumPoints); + + for (int32 PointIdx = 0 ; PointIdx < NumPoints ; ++PointIdx) + { + FVector Dir = FMath::VRand(); + Dir.Z = 0.f; + Dir.Normalize(); + + float Dist = FMath::FRandRange(InnerRadiusValue,OuterRadiusValue); + FVector Location = CenterPoints[0] + Dir * Dist; + + const FNavLocation PointPos = FNavLocation(Location); + Points.Add(PointPos); + } + +#if WITH_EDITOR + // Not properly working now + if (ShowDebugDonut) + { + AActor* TestingPawn = UGameplayStatics::GetActorOfClass(GetWorld(),AEQSTestingPawn::StaticClass()); + + if (TestingPawn) + { + if (TestingPawn->IsSelected()) + { + //DrawDebugCircle(GetWorld(), CenterPoints[0], InnerRadiusValue, 20, FColor::Red, false,ShowDebugLifeTime,0,5.0f,FVector(1,0,0), FVector(0,1,0), false); + //DrawDebugCircle(GetWorld(), CenterPoints[0], OuterRadiusValue, 20, FColor::Red, false,ShowDebugLifeTime,0,5.0f,FVector(1,0,0), FVector(0,1,0), false); + } + else + { + FlushPersistentDebugLines(GetWorld()); + } + } + } +#endif + + ProjectAndFilterNavPoints(Points, QueryInstance); + StoreNavPoints(Points, QueryInstance); +} diff --git a/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.h b/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.h new file mode 100644 index 00000000..033e8c8d --- /dev/null +++ b/Source/ProjectMJ/TG/AI/EQS/MJEnvQueryGenerator_MJDonut.h @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "EnvironmentQuery/Generators/EnvQueryGenerator_Donut.h" +#include "MJEnvQueryGenerator_MJDonut.generated.h" + +/** + * Class Description: EQS Query which generate random points in donut range + * Author: Cha Tae Gwan + * Created Date: 2025-07-03 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-03 + */ +UCLASS() +class PROJECTMJ_API UMJEnvQueryGenerator_MJDonut : public UEnvQueryGenerator_Donut +{ + GENERATED_BODY() + +public: + UMJEnvQueryGenerator_MJDonut(const FObjectInitializer& ObjectInitializer); + +protected: + + UPROPERTY(EditDefaultsOnly, Category = Generator) + FAIDataProviderIntValue NumberOfRandomPoints; + + UPROPERTY(EditDefaultsOnly, Category = Debug) + bool ShowDebugDonut = true; + + UPROPERTY(EditDefaultsOnly, Category = Debug) + float ShowDebugLifeTime = 3.0f; + + virtual void GenerateItems(FEnvQueryInstance& QueryInstance) const override; + +}; diff --git a/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.cpp b/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.cpp index 8f248b79..81e240ad 100644 --- a/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.cpp +++ b/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.cpp @@ -7,3 +7,16 @@ AMJAIBossCharacterTG::AMJAIBossCharacterTG() { } + +void AMJAIBossCharacterTG::BeginPlay() +{ + Super::BeginPlay(); + // @fixme : change based on design + SetActorHiddenInGame(false); + SetActorEnableCollision(true); +} + +void AMJAIBossCharacterTG::PossessedBy(AController* NewController) +{ + Super::PossessedBy(NewController); +} diff --git a/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.h b/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.h index a0bd1c38..781b1158 100644 --- a/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.h +++ b/Source/ProjectMJ/TG/AI/MJAIBossCharacterTG.h @@ -3,7 +3,7 @@ #pragma once #include "CoreMinimal.h" -#include "Character/MJCharacterBase.h" +#include "MJ/Character/MJMonsterCharacter.h" #include "MJAIBossCharacterTG.generated.h" /** @@ -14,7 +14,7 @@ * Last Modified Date: 2025-06-28 */ UCLASS() -class PROJECTMJ_API AMJAIBossCharacterTG : public AMJCharacterBase +class PROJECTMJ_API AMJAIBossCharacterTG : public AMJMonsterCharacter { GENERATED_BODY() @@ -22,6 +22,9 @@ class PROJECTMJ_API AMJAIBossCharacterTG : public AMJCharacterBase AMJAIBossCharacterTG(); protected: + + virtual void BeginPlay() override; + virtual void PossessedBy(AController* NewController) override; }; diff --git a/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.cpp b/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.cpp deleted file mode 100644 index 4699b14b..00000000 --- a/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "TG/AI/MJAIBossControllerTG.h" - -#include "Perception/AIPerceptionTypes.h" - -AMJAIBossControllerTG::AMJAIBossControllerTG() -{ - -} - -void AMJAIBossControllerTG::TargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) -{ - Super::TargetPerceptionUpdated(Actor, Stimulus); - - -} diff --git a/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.h b/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.h deleted file mode 100644 index afe71811..00000000 --- a/Source/ProjectMJ/TG/AI/MJAIBossControllerTG.h +++ /dev/null @@ -1,29 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "TG/AI/MJAIControllerBaseTG.h" -#include "MJAIBossControllerTG.generated.h" - -/** - * Class Description: Boss AI Controller class - * Author: Cha Tae Gwan - * Created Date: 2025-06-28 - * Last Modified By: Cha Tae Gwan - * Last Modified Date: 2025-06-28 - */ -UCLASS() -class PROJECTMJ_API AMJAIBossControllerTG : public AMJAIControllerBaseTG -{ - GENERATED_BODY() - -public: - AMJAIBossControllerTG(); - -protected: - - virtual void TargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) override; - - -}; diff --git a/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.cpp b/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.cpp deleted file mode 100644 index 49d3f056..00000000 --- a/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "TG/AI/MJAIControllerBaseTG.h" - -#include "BehaviorTree/BehaviorTreeComponent.h" -#include "MJ/AI/AIPerceptionInfo.h" -#include "Perception/AIPerceptionComponent.h" -#include "Perception/AIPerceptionTypes.h" - -AMJAIControllerBaseTG::AMJAIControllerBaseTG() -{ - AIPerceptionComponent = CreateDefaultSubobject(TEXT("AIPerceptionComponent")); - BehaviorTreeComponent = CreateDefaultSubobject(TEXT("BTComponent")); - TeamId = 255; - -} - -void AMJAIControllerBaseTG::PostInitializeComponents() -{ - Super::PostInitializeComponents(); - - AIPerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &AMJAIControllerBaseTG::TargetPerceptionUpdated); - -} - -void AMJAIControllerBaseTG::OnPossess(APawn* InPawn) -{ - Super::OnPossess(InPawn); - -} - -void AMJAIControllerBaseTG::BeginPlay() -{ - Super::BeginPlay(); - - RunBehaviorTree(BehaviorTreeComponent->GetCurrentTree()); -} - -void AMJAIControllerBaseTG::TargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) -{ - -} - -ETeamAttitude::Type AMJAIControllerBaseTG::GetTeamAttitudeTowards(const AActor& Other) const -{ - ETeamAttitude::Type Result = Super::GetTeamAttitudeTowards(Other); - - /* - * How TO: TeamId 비교해서 플레이어, NPC, 몬스터로 나눈다. - */ - - const APawn* OtherPawn = Cast(&Other); - if (nullptr==OtherPawn) - { - return ETeamAttitude::Neutral; - } - - const IGenericTeamAgentInterface* OtherCharacter = Cast (&Other); - if (nullptr== OtherCharacter) - { - return ETeamAttitude::Neutral; - } - - FGenericTeamId OtherTeamId = OtherCharacter->GetGenericTeamId(); - if (OtherTeamId == 255) - { - // NONE - return ETeamAttitude::Neutral; - } - if (OtherTeamId == 0) - { - // 플레이어인 경우 - return ETeamAttitude::Hostile; - } - else if ((OtherTeamId>=static_cast(ETeam_ID::NPC))&& - (OtherTeamId(ETeam_ID::MONSTER))) - { - // NPC인 경우 - return ETeamAttitude::Neutral; - } - else if (OtherTeamId>=static_cast(ETeam_ID::MONSTER)) - { - if (OtherTeamId == GetGenericTeamId()) - { - // 아군인 경우 - return ETeamAttitude::Friendly; - } - else - { - // 다른 몬스터인 경우 - return ETeamAttitude::Neutral; - } - } - else - { - return Result; - } -} - -FGenericTeamId AMJAIControllerBaseTG::GetGenericTeamId() const -{ - return TeamId; -} diff --git a/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.h b/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.h deleted file mode 100644 index 24916ca8..00000000 --- a/Source/ProjectMJ/TG/AI/MJAIControllerBaseTG.h +++ /dev/null @@ -1,53 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "AIController.h" -#include "MJAIControllerBaseTG.generated.h" - -class UBehaviorTreeComponent; -struct FAIStimulus; -/** - * Class Description: AIControellerBase class - * Author: Cha Tae Gwan - * Created Date: 2025-06-28 - * Last Modified By: Cha Tae Gwan - * Last Modified Date: 2025-06-28 - */ -UCLASS() -class PROJECTMJ_API AMJAIControllerBaseTG : public AAIController -{ - GENERATED_BODY() - -public: - AMJAIControllerBaseTG(); - -protected: - - virtual void PostInitializeComponents() override; - - virtual void OnPossess(APawn* InPawn) override; - - virtual void BeginPlay() override; - - virtual FGenericTeamId GetGenericTeamId() const override; - - virtual ETeamAttitude::Type GetTeamAttitudeTowards(const AActor& Other) const override; - - UFUNCTION(BlueprintCallable) - virtual void TargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus); - - UPROPERTY(EditDefaultsOnly) - TObjectPtr AIPerceptionComponent; - - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="AI|BehaviorTree") - TObjectPtr BehaviorTreeComponent; - - FGenericTeamId TeamId; - - - - - -}; diff --git a/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.cpp b/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.cpp deleted file mode 100644 index 5cd412b5..00000000 --- a/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "TG/AI/MJAISpawnTriggerBox.h" - -#include "ProjectMJ.h" -#include "EnvironmentQuery/EnvQueryManager.h" - -AMJAISpawnTriggerBox::AMJAISpawnTriggerBox() -{ - - -} - -void AMJAISpawnTriggerBox::PostInitializeComponents() -{ - Super::PostInitializeComponents(); - - OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnBeginOverlap); -} - -void AMJAISpawnTriggerBox::OnBeginOverlap(AActor* OverlappedActor, AActor* OtherActor) -{ - GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Cyan,TEXT("overlapped")); - if (EQSQuery) - { - FEnvQueryRequest Request(EQSQuery,SpawnCenterActor); - Request.Execute(EEnvQueryRunMode::AllMatching, this, &AMJAISpawnTriggerBox::OnQueryFininshed); - } -} - -void AMJAISpawnTriggerBox::OnQueryFininshed(TSharedPtr Result) -{ - GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Cyan,TEXT("Query Executed")); - - TArray Locations; - Result->GetAllAsLocations(Locations); - - for (const FVector& Loc : Locations) - { - FActorSpawnParameters SpawnParams; - SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; - GetWorld()->SpawnActor(SpawnTargetActor, Loc, FRotator(0.0f,90.f,0.0f), SpawnParams); - } - - if (Destroy()) - { - GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Cyan,TEXT("Spawner Destroyed")); - } - -} - diff --git a/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.h b/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.h deleted file mode 100644 index 5d1a03e9..00000000 --- a/Source/ProjectMJ/TG/AI/MJAISpawnTriggerBox.h +++ /dev/null @@ -1,45 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Templates/SharedPointer.h" -#include "Engine/TriggerBox.h" -#include "MJAISpawnTriggerBox.generated.h" - -struct FEnvQueryResult; -class UEnvQuery; -/** - * Class Description: TriggerBox which spawns AI - * Author: Cha Tae Gwan - * Created Date: 2025-06-23 - * Last Modified By: Cha Tae Gwan - * Last Modified Date: 2025-06-23 - */ -UCLASS() -class PROJECTMJ_API AMJAISpawnTriggerBox : public ATriggerBox -{ - GENERATED_BODY() - -public: - AMJAISpawnTriggerBox(); - - UPROPERTY(EditAnywhere) - TObjectPtr EQSQuery; - - UPROPERTY(EditAnywhere) - TSubclassOf SpawnTargetActor; - - UPROPERTY(EditAnywhere) - TObjectPtr SpawnCenterActor; - -protected: - virtual void PostInitializeComponents() override; - - - UFUNCTION() - void OnBeginOverlap( AActor* OverlappedActor, AActor* OtherActor); - - void OnQueryFininshed(TSharedPtr Result); - -}; diff --git a/Source/ProjectMJ/TG/Actor/MJDummyActorTG.cpp b/Source/ProjectMJ/TG/Actor/MJDummyActorTG.cpp index ae94444f..3457781d 100644 --- a/Source/ProjectMJ/TG/Actor/MJDummyActorTG.cpp +++ b/Source/ProjectMJ/TG/Actor/MJDummyActorTG.cpp @@ -3,17 +3,12 @@ #include "MJDummyActorTG.h" -#include "ProjectMJ.h" -#include "Kismet/GameplayStatics.h" -#include "TG/GameState/MJGameStateDungeonTG.h" - AMJDummyActorTG::AMJDummyActorTG() { Mesh = CreateDefaultSubobject(TEXT("Mesh")); Mesh->SetupAttachment(RootComponent); - SetActorLocation(FVector(970.f, 250.f, -450.f)); } void AMJDummyActorTG::BeginPlay() diff --git a/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.cpp b/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.cpp index 3ab3f016..50255de7 100644 --- a/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.cpp +++ b/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.cpp @@ -10,6 +10,11 @@ void AMJDummyObjectTG::PostInitializeComponents() Super::PostInitializeComponents(); } +void AMJDummyObjectTG::BeginPlay() +{ + Super::BeginPlay(); +} + void AMJDummyObjectTG::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); diff --git a/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.h b/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.h index 5f6d5b4c..654fd5d1 100644 --- a/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.h +++ b/Source/ProjectMJ/TG/Actor/MJDummyObjectTG.h @@ -19,6 +19,7 @@ class PROJECTMJ_API AMJDummyObjectTG : public AActor, public IMJInstancedActorIn virtual void PostInitializeComponents() override; + virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; }; diff --git a/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.cpp b/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.cpp new file mode 100644 index 00000000..858d01cd --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.cpp @@ -0,0 +1,15 @@ + +#include "TG/Actor/MJDungeonAISpawnPointActor.h" + +AMJDungeonAISpawnPointActor::AMJDungeonAISpawnPointActor() +{ + Mesh = CreateDefaultSubobject(TEXT("Mesh")); + RootComponent = Mesh; + + // Only Visible in Editor + Mesh->SetHiddenInGame(true); + + NumberToSpawn = 0; + +} + diff --git a/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.h b/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.h new file mode 100644 index 00000000..1e6cb997 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJDungeonAISpawnPointActor.h @@ -0,0 +1,32 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "MJDungeonAISpawnPointActor.generated.h" + +/** + * Class Description: static Spawning Point Actor + * Author: Cha Tae Gwan + * Created Date: 2025-07-03 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-03 + */ +UCLASS() +class PROJECTMJ_API AMJDungeonAISpawnPointActor : public AActor +{ + GENERATED_BODY() + +public: + AMJDungeonAISpawnPointActor(); + + UPROPERTY(EditAnywhere) + uint8 NumberToSpawn; + +protected: + + UPROPERTY(EditDefaultsOnly) + TObjectPtr Mesh; + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJInteractableActor.cpp b/Source/ProjectMJ/TG/Actor/MJInteractableActor.cpp new file mode 100644 index 00000000..3be87a76 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJInteractableActor.cpp @@ -0,0 +1,30 @@ +// ThenOneDayStudio + +#include "TG/Actor/MJInteractableActor.h" + +#include "Components/SphereComponent.h" +#include "TG/Component/MJMiniMapIconMeshComponent.h" +#include "UI/Component/MJInteractComponent.h" + +AMJInteractableActor::AMJInteractableActor() +{ + StaticMesh = CreateDefaultSubobject(TEXT("StaticMesh")); + RootComponent = StaticMesh; + + SphereCollision = CreateDefaultSubobject(TEXT("Collision")); + SphereCollision->SetupAttachment(StaticMesh); + + InteractComponent = CreateDefaultSubobject(TEXT("InteractComp")); + + MiniMapIconMesh = CreateDefaultSubobject(TEXT("MiniMapIconMesh")); + MiniMapIconMesh->SetupAttachment(StaticMesh); + +} + +void AMJInteractableActor::Execute() +{ + +} + + + diff --git a/Source/ProjectMJ/TG/Actor/MJInteractableActor.h b/Source/ProjectMJ/TG/Actor/MJInteractableActor.h new file mode 100644 index 00000000..2664c2a1 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJInteractableActor.h @@ -0,0 +1,50 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "MJInteractableActor.generated.h" + + +class UMJMiniMapIconMeshComponent; +class UMJInteractComponent; +class USphereComponent; +class USoundCue; +/** + * Class Description: AMJInteractableActor Base + * Author: Cha Tae Gwan + * Created Date: 2025-08-05 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-08-05 + */ +UCLASS() +class PROJECTMJ_API AMJInteractableActor : public AActor +{ + GENERATED_BODY() + +public: + AMJInteractableActor(); + + virtual void Execute(); + +protected: + + UPROPERTY(EditDefaultsOnly) + TObjectPtr StaticMesh; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr MiniMapIconMesh; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SphereCollision; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr InteractComponent; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr CollisionSFX; + + + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.cpp b/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.cpp new file mode 100644 index 00000000..9d904c39 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.cpp @@ -0,0 +1,12 @@ +// ThenOneDayStudio + + +#include "TG/Actor/MJMiniMapBackgroundActor.h" + +AMJMiniMapBackgroundActor::AMJMiniMapBackgroundActor() +{ + StaticMesh = CreateDefaultSubobject(TEXT("Mesh")); + RootComponent = StaticMesh; + + StaticMesh->bCastDynamicShadow = false; +} diff --git a/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.h b/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.h new file mode 100644 index 00000000..a7194a73 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJMiniMapBackgroundActor.h @@ -0,0 +1,32 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "MJMiniMapBackgroundActor.generated.h" + + +/** + * Class Description: MinMapBackgroundActor + * Author: Cha Tae Gwan + * Created Date: 2025_07_31 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025_07_31 + */ +UCLASS() +class PROJECTMJ_API AMJMiniMapBackgroundActor : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + AMJMiniMapBackgroundActor(); + +protected: + + UPROPERTY(EditDefaultsOnly) + TObjectPtr StaticMesh; + + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.cpp b/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.cpp new file mode 100644 index 00000000..7f4a40ec --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.cpp @@ -0,0 +1,73 @@ +// ThenOneDayStudio + +#include "TG/Actor/MJPortalToDungeon.h" + +#include "GameMode/MJGameModeBase.h" +#include "Kismet/GameplayStatics.h" +#include "TG/MJGameInstance.h" +#include "TG/GameMode/MJGameModeTown.h" +#include "TG/SubSystem/MJDungeonGenerationSubSystem.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + +AMJPortalToDungeon::AMJPortalToDungeon() +{ + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + + +void AMJPortalToDungeon::Execute() +{ + Super::Execute(); + + UMJDungeonGenerationSubSystem* SubSystem = GetGameInstance()->GetSubsystem(); + if (SubSystem) + { + SubSystem->GenerateDungeonGraph(); + } + + AMJGameModeTown* GMTown = Cast(UGameplayStatics::GetGameMode(GetWorld())); + if (GMTown && SubSystem) + { + uint8 NodeId = SubSystem->GetDungeonGraph()->Nodes[GetGameInstance()->GetPlayerSessionDataRef().CurrentDungeonMapNum].AssignedMapID; + EMJNodeType NodeType = SubSystem->GetDungeonGraph()->Nodes[GetGameInstance()->GetPlayerSessionDataRef().CurrentDungeonMapNum].NodeType; + + FString MapName; + + switch (NodeType) + { + case EMJNodeType::Battle: + MapName = FString::Printf(TEXT("Dungeon_Chunk_Battle0%d"),NodeId); + break; + + case EMJNodeType::Boss: + MapName = TEXT("Dungeon_Chunk_Boss"); + break; + case EMJNodeType::Reward: + MapName = TEXT("Dungeon_Chunk_Reward"); + break; + } + + // GameSaveTiming + + GetGameInstance()->GetSubsystem()->SaveGameToCurrentSlotNum(); + + GMTown->TravelToMapByNode(MapName, NodeId); + + + } +} + +void AMJPortalToDungeon::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + + +void AMJPortalToDungeon::BeginPlay() +{ + Super::BeginPlay(); +} \ No newline at end of file diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.h b/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.h new file mode 100644 index 00000000..a56b8546 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToDungeon.h @@ -0,0 +1,24 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJInteractableActor.h" +#include "GameFramework/Actor.h" +#include "MJPortalToDungeon.generated.h" + +UCLASS() +class PROJECTMJ_API AMJPortalToDungeon : public AMJInteractableActor +{ + GENERATED_BODY() + +public: + AMJPortalToDungeon(); + + virtual void Execute() override; +protected: + + virtual void PostInitializeComponents() override; + virtual void BeginPlay() override; + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.cpp b/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.cpp new file mode 100644 index 00000000..79973970 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.cpp @@ -0,0 +1,51 @@ +// ThenOneDayStudio + +#include "TG/Actor/MJPortalToNextDungeon.h" + +#include "Controller/MJPlayerController.h" +#include "Kismet/GameplayStatics.h" +#include "Sound/SoundCue.h" +#include "TG/UI/MJDungeonMapWidget.h" +#include "TG/UI/MJGameFlowHUDWidget.h" + +AMJPortalToNextDungeon::AMJPortalToNextDungeon() +{ + // MiniMapIconMesh = CreateDefaultSubobject(TEXT("MiniMapIcon")); + // MiniMapIconMesh->SetupAttachment(StaticMesh); + + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + +void AMJPortalToNextDungeon::Execute() +{ + Super::Execute(); + + AMJPlayerController* PC = Cast(UGameplayStatics::GetPlayerController(GetWorld(),0)); + if (PC) + { + PC->StopMovement(); + PC->SetInputMode(FInputModeUIOnly()); + UGameplayStatics::PlaySound2D(GetWorld(),CollisionSFX); + if (UMJDungeonMapWidget* DungeonMapWidget = PC->GetGameFlowHUD()->GetDungeonMapWidget()) + { + DungeonMapWidget->SetVisibility(ESlateVisibility::Visible); + DungeonMapWidget->SetAllVisibility(ESlateVisibility::Visible); + } + } + +} + +void AMJPortalToNextDungeon::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + +void AMJPortalToNextDungeon::BeginPlay() +{ + Super::BeginPlay(); + +} diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.h b/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.h new file mode 100644 index 00000000..a320c1ab --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToNextDungeon.h @@ -0,0 +1,40 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJInteractableActor.h" +#include "GameFramework/Actor.h" +#include "TG/Interface/MJInstancedActorInterface.h" +#include "MJPortalToNextDungeon.generated.h" + +class UMJMiniMapIconMeshComponent; +/** + * Class Description: (currently not using) PortalToNextDungeon + * Author: Cha Tae Gwan + * Created Date: 2025_07_29 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025_07_29 + */ +UCLASS() +class PROJECTMJ_API AMJPortalToNextDungeon : public AMJInteractableActor, public IMJInstancedActorInterface +{ + GENERATED_BODY() + +public: + AMJPortalToNextDungeon(); + + virtual void Execute() override; + +protected: + + virtual void PostInitializeComponents() override; + + virtual void BeginPlay() override; + + // UPROPERTY(EditDefaultsOnly) + // TObjectPtr MiniMapIconMesh; + // + + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToTown.cpp b/Source/ProjectMJ/TG/Actor/MJPortalToTown.cpp new file mode 100644 index 00000000..0269cc8b --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToTown.cpp @@ -0,0 +1,37 @@ +// ThenOneDayStudio + + +#include "TG/Actor/MJPortalToTown.h" + +#include "ProjectMJ.h" +#include "GameMode/MJGameModeBase.h" +#include "Kismet/GameplayStatics.h" + +AMJPortalToTown::AMJPortalToTown() +{ + + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + +void AMJPortalToTown::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + // TG : Don`t change this for proper SpawnActor + SetActorRelativeScale3D(FVector(0.38f,0.2f,0.38f)); +} + +void AMJPortalToTown::Execute() +{ + Super::Execute(); + AGameModeBase* GM = UGameplayStatics::GetGameMode(GetWorld()); + if (IsValid(GM)) + { + AMJGameModeBase* MJGM = Cast(GM); + if (IsValid(MJGM)) + { + MJGM->TravelToMap(MAP_TOWN); + } + } +} diff --git a/Source/ProjectMJ/TG/Actor/MJPortalToTown.h b/Source/ProjectMJ/TG/Actor/MJPortalToTown.h new file mode 100644 index 00000000..52b12111 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJPortalToTown.h @@ -0,0 +1,28 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJInteractableActor.h" +#include "GameFramework/Actor.h" +#include "MJPortalToTown.generated.h" + +/** + * Class Description: Portal to Town + * Author: Cha Tae Gwan + * Created Date: 2025-07-24 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-24 + */ +UCLASS() +class PROJECTMJ_API AMJPortalToTown : public AMJInteractableActor +{ + GENERATED_BODY() + +public: + AMJPortalToTown(); + + virtual void PostInitializeComponents() override; + virtual void Execute() override; + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJSavePointActor.cpp b/Source/ProjectMJ/TG/Actor/MJSavePointActor.cpp new file mode 100644 index 00000000..a7a6b816 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJSavePointActor.cpp @@ -0,0 +1,25 @@ +// ThenOneDayStudio + +#include "TG/Actor/MJSavePointActor.h" + +#include "Controller/MJPlayerController.h" +#include "Kismet/GameplayStatics.h" +#include "TG/UI/MJGameFlowHUDWidget.h" +#include "TG/UI/MJLoadGameWidget.h" + +AMJSavePointActor::AMJSavePointActor() +{ +} + +void AMJSavePointActor::Execute() +{ + Super::Execute(); + + AMJPlayerController* MJPC = Cast(UGameplayStatics::GetPlayerController(GetWorld(),0)); + if (MJPC) + { + MJPC->GetGameFlowHUD()->GetSaveGameWidget()->UpdateSlot(); + MJPC->GetGameFlowHUD()->GetSaveGameWidget()->SetVisibility(ESlateVisibility::Visible); + } +} + diff --git a/Source/ProjectMJ/TG/Actor/MJSavePointActor.h b/Source/ProjectMJ/TG/Actor/MJSavePointActor.h new file mode 100644 index 00000000..c4a69dbd --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJSavePointActor.h @@ -0,0 +1,27 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJInteractableActor.h" +#include "GameFramework/Actor.h" +#include "MJSavePointActor.generated.h" + +/** + * Class Description: SavePoint + * Author: Cha Tae Gwan + * Created Date: 2025-08-05 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-08-05 + */ +UCLASS() +class PROJECTMJ_API AMJSavePointActor : public AMJInteractableActor +{ + GENERATED_BODY() + +public: + AMJSavePointActor(); + + virtual void Execute() override; + +}; diff --git a/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.cpp b/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.cpp new file mode 100644 index 00000000..6e4b955c --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.cpp @@ -0,0 +1,187 @@ +// ThenOneDayStudio + + +#include "TG/Actor/MJSceneCapture2D.h" + +#include "MJPortalToDungeon.h" +#include "NavigationSystem.h" +#include "Components/SceneCaptureComponent2D.h" +#include "Kismet/GameplayStatics.h" +#include "NavMesh/RecastNavMesh.h" +#include "ProceduralMeshComponent.h" +#include "GameFramework/Character.h" +#include "TG/Component/MJMiniMapIconMeshComponent.h" +#include "TG/GameMode/MJGameModeTown.h" +#include "TG/GameState/MJGameStateDungeon.h" + + +AMJSceneCapture2D::AMJSceneCapture2D() +{ + PrimaryActorTick.bCanEverTick = true; + + CameraHeight = 3000.f; + + SetActorLocation(FVector(0.0f,0.0f,CameraHeight)); + SetActorRotation(FRotator3d(-90.0f,0.0f,0.0f)); + ProceduralMeshComponent = CreateDefaultSubobject(TEXT("ProceduralMeshComponent")); + //ProceduralMeshComponent->SetupAttachment(RootComponent); + + ProceduralMeshComponent->bCastDynamicShadow = false; +} + +void AMJSceneCapture2D::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + AMJGameStateDungeon* GSDungeon = Cast(UGameplayStatics::GetGameState(GetWorld())); + if (GSDungeon) + { + GSDungeon->OnActorSpawned.AddDynamic(this, &AMJSceneCapture2D::OnActorSpawned); + } +} + +void AMJSceneCapture2D::BeginPlay() +{ + Super::BeginPlay(); + + if (Cast(UGameplayStatics::GetGameMode(GetWorld()))) + { + TArray OutActors; + UGameplayStatics::GetAllActorsOfClass(GetWorld(),AMJInteractableActor::StaticClass(), OutActors); + + for (const auto& Iter : OutActors) + { + if (IsValid(Iter)) + { + GetCaptureComponent2D()->ShowOnlyComponents.Add(Iter->FindComponentByClass()); + } + } + + + } + + + Player = TWeakObjectPtr(UGameplayStatics::GetPlayerCharacter(GetWorld(),0)); + if (Player.IsValid()) + { + UPrimitiveComponent* PlayerMiniMapMeshComp = Cast(Player->FindComponentByClass()); + if (PlayerMiniMapMeshComp) + { + GetCaptureComponent2D()->ShowOnlyComponents.Add(PlayerMiniMapMeshComp); + } + } + + if (MiniMapBackgroundActor) + { + GetCaptureComponent2D()->ShowOnlyActors.Add(MiniMapBackgroundActor); + } + + UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld()); + if (NavSys) + { + APawn* Pawn = Cast(Player); + if (Pawn) + { + FNavAgentProperties AgentProps = Cast(Player)->GetNavAgentPropertiesRef(); + ARecastNavMesh* NavMesh = Cast(NavSys->GetNavDataForProps(AgentProps)); + if (NavMesh) + { + int32 TileCount = NavMesh->GetNavMeshTilesCount(); + + TArray AllVertices; + TArray AllTriangles; + TArray AllColors; + + int32 VertexOffset = 0; + + for (int i = 0; i < TileCount; ++i) + { + TArray Polys; + NavMesh->GetPolysInTile(i, Polys); + + for (const FNavPoly& Poly : Polys) + { + TArray Verts; + if (!NavMesh->GetPolyVerts(Poly.Ref, Verts)) continue; + + int NumVerts = Verts.Num(); + + for (const FVector& V : Verts) + { + AllVertices.Add(V); + AllColors.Add(FColor::Green); + } + + + for (int32 v = 1; v < NumVerts - 1; ++v) + { + AllTriangles.Add(VertexOffset + 0); + AllTriangles.Add(VertexOffset + v); + AllTriangles.Add(VertexOffset + v + 1); + } + VertexOffset += NumVerts; + } + } + + TArray Normals; Normals.Init(FVector::UpVector, AllVertices.Num()); + TArray UV0; UV0.Init(FVector2D::ZeroVector, AllVertices.Num()); + TArray Tangents; + Tangents.Init(FProcMeshTangent(), AllVertices.Num()); + + ProceduralMeshComponent->CreateMeshSection( + 0, + AllVertices, + AllTriangles, + Normals, + UV0, + AllColors, + Tangents, + true + ); + ProceduralMeshComponent->SetMaterial(0,ProceduralMeshMaterial); + ProceduralMeshComponent->SetVisibility(true); + GetCaptureComponent2D()->ShowOnlyComponents.Add(ProceduralMeshComponent); + } + } + } +} + +void AMJSceneCapture2D::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (Player.IsValid()) + { + FVector3d PlayerLoc = Player->GetActorLocation(); + + SetActorLocation(FVector3d(PlayerLoc.X,PlayerLoc.Y, CameraHeight)); + } +} + +void AMJSceneCapture2D::OnActorSpawned(AActor* InputActor) +{ + if (IsValid(InputActor)) + { + UMJMiniMapIconMeshComponent* MiniMapIconMeshComponent = InputActor->FindComponentByClass(); + if (IsValid(MiniMapIconMeshComponent)) + { + GetCaptureComponent2D()->ShowOnlyComponents.Add(MiniMapIconMeshComponent); + + InputActor->OnDestroyed.AddDynamic(this, &AMJSceneCapture2D::OnActorDestroyed); + + } + } +} + +void AMJSceneCapture2D::OnActorDestroyed(AActor* InputActor) +{ + if (IsValid(InputActor)) + { + UMJMiniMapIconMeshComponent* MiniMapIconMesh = Cast(InputActor->FindComponentByClass()); + if (IsValid(MiniMapIconMesh)) + { + GetCaptureComponent2D()->ShowOnlyComponents.Remove(MiniMapIconMesh); + } + } +} + diff --git a/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.h b/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.h new file mode 100644 index 00000000..d3838ed7 --- /dev/null +++ b/Source/ProjectMJ/TG/Actor/MJSceneCapture2D.h @@ -0,0 +1,59 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/SceneCapture2D.h" +#include "MJSceneCapture2D.generated.h" + +class UProceduralMeshComponent; + + +/** + * Class Description: SceneCapture2D + * Author: Cha Tae Gwan + * Created Date: 2025_07_31 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025_07_31 + */ +UCLASS() +class PROJECTMJ_API AMJSceneCapture2D : public ASceneCapture2D +{ + GENERATED_BODY() + +public: + + AMJSceneCapture2D(); + + UPROPERTY(EditAnywhere) + TObjectPtr MiniMapBackgroundActor; + +protected: + + virtual void PostInitializeComponents() override; + + virtual void BeginPlay() override; + + virtual void Tick(float DeltaSeconds) override; + + UFUNCTION() + void OnActorSpawned(AActor* InputActor); + + UFUNCTION() + void OnActorDestroyed(AActor* InputActor); + + UPROPERTY() + TWeakObjectPtr Player; + + UPROPERTY(EditDefaultsOnly) + float CameraHeight; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr ProceduralMeshComponent; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr ProceduralMeshMaterial; + + +}; + diff --git a/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.cpp b/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.cpp new file mode 100644 index 00000000..93711183 --- /dev/null +++ b/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.cpp @@ -0,0 +1,59 @@ +// ThenOneDayStudio + +#include "TG/Component/MJMiniMapIconMeshComponent.h" + +#include "Character/MJPlayerCharacter.h" +#include "MJ/Character/MJMonsterCharacter.h" + +UMJMiniMapIconMeshComponent::UMJMiniMapIconMeshComponent() +{ + + SetRelativeLocation(FVector(0.0f,0.0f,300.f)); + + bVisibleInSceneCaptureOnly = true; + bCanEverAffectNavigation = false; + + // have to do this cuz there`s no parent BP of Enemy. and no need for change icon per enemy + ConstructorHelpers::FObjectFinder EnemyMesh(TEXT("/Game/TG/StaticMesh/SM_MJSphere.SM_MJSphere")); + if (EnemyMesh.Succeeded()) + { + EnemyIcon = EnemyMesh.Object; + } + + ConstructorHelpers::FObjectFinder ArrowMesh(TEXT("/Game/TG/StaticMesh/SM_MJArrow.SM_MJArrow")); + if (ArrowMesh.Succeeded()) + { + PlayerIcon = ArrowMesh.Object; + } + +} + +void UMJMiniMapIconMeshComponent::BeginPlay() +{ + Super::BeginPlay(); + + + SetUsingAbsoluteRotation(true); + SetUsingAbsoluteScale(true); + SetRelativeRotation(FRotator(0.0f,90.0f,0.0f)); + SetRelativeLocation(FVector(0.0f,0.0f,300.f)); + SetWorldScale3D(FVector(2.0f,2.0f,1.0f)); + + SetCollisionEnabled(ECollisionEnabled::Type::NoCollision); + SetVisibleInSceneCaptureOnly(true); + + if (AMJMonsterCharacter* MonsterCharacter = Cast(GetOwner())) + { + SetRelativeScale3D(FVector(1.0f,1.0f,1.0f)); + SetRelativeLocation(FVector(0.0f,0.0f,0.f)); + SetStaticMesh(EnemyIcon); + } + else if (AMJPlayerCharacter* Player = Cast(GetOwner())) + { + + SetUsingAbsoluteRotation(false); + SetUsingAbsoluteScale(false); + SetRelativeRotation(FRotator(0.0f,0.0f,0.0f)); + SetStaticMesh(PlayerIcon); + } +} diff --git a/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.h b/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.h new file mode 100644 index 00000000..498ec227 --- /dev/null +++ b/Source/ProjectMJ/TG/Component/MJMiniMapIconMeshComponent.h @@ -0,0 +1,37 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Components/StaticMeshComponent.h" +#include "MJMiniMapIconMeshComponent.generated.h" + +/** + * Class Description: MiniMapIconMeshComponent + * Author: Cha Tae Gwan + * Created Date: 2025-07-31 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-31 + */ +UCLASS(Blueprintable) +class PROJECTMJ_API UMJMiniMapIconMeshComponent : public UStaticMeshComponent +{ + GENERATED_BODY() + +public: + UMJMiniMapIconMeshComponent(); + +protected: + virtual void BeginPlay() override; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr PlayerIcon; + // + UPROPERTY(EditDefaultsOnly) + TObjectPtr EnemyIcon; + // + // UPROPERTY(EditDefaultsOnly) + // TObjectPtr PortalIcon; + + +}; diff --git a/Source/ProjectMJ/TG/DataTable/MJStaticAISpawnRow.h b/Source/ProjectMJ/TG/DataTable/MJStaticAISpawnRow.h new file mode 100644 index 00000000..8174e31f --- /dev/null +++ b/Source/ProjectMJ/TG/DataTable/MJStaticAISpawnRow.h @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "MJStaticAISpawnRow.generated.h" + +/** + * Class Description: static AI Spawn Data Row + * Author: Cha Tae Gwan + * Created Date: 2025-07-17 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-17 + */ +USTRUCT(BlueprintType) +struct FMJStaticAISpawnDataRow : public FTableRowBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + TSubclassOf EnemyClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float SpawnWeight; + + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/TG/DataTable/MJWaveAISpawnRow.h b/Source/ProjectMJ/TG/DataTable/MJWaveAISpawnRow.h new file mode 100644 index 00000000..dc47b169 --- /dev/null +++ b/Source/ProjectMJ/TG/DataTable/MJWaveAISpawnRow.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "MJWaveAISpawnRow.generated.h" + +/** + * Class Description: Wave Datatable Row + * Author: Cha Tae Gwan + * Created Date: 2025-07-03 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-03 + */ +USTRUCT(BlueprintType) +struct FMJWaveAISpawnDataRow : public FTableRowBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + uint8 WaveNum; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 EnemyCount; + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + TMap, int32> EnemyPool; +}; \ No newline at end of file diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.cpp b/Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.cpp new file mode 100644 index 00000000..93e4e67f --- /dev/null +++ b/Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.cpp @@ -0,0 +1,55 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJGameModeDungeon.h" + +#include "ProjectMJ.h" +#include "Kismet/GameplayStatics.h" +#include "Player/MJPlayerState.h" +#include "TG/MJGameInstance.h" +#include "TG/GameState/MJGameStateDungeon.h" + + +AMJGameModeDungeon::AMJGameModeDungeon() +{ +} + +void AMJGameModeDungeon::BeginPlay() +{ + Super::BeginPlay(); + + AMJPlayerState* MJPS = Cast(UGameplayStatics::GetPlayerState(this,0)); + + if (MJPS) + { + // MJPS->LoadFromInstancedPlayerSessionData(); + } + +} + +bool AMJGameModeDungeon::TravelToMapByNode(const FString MapName, const uint8 NodeNum) +{ + + UMJGameInstance* MJGI = GetGameInstance(); + if (MJGI) + { + AMJPlayerState* PS = Cast(UGameplayStatics::GetPlayerState(this,0)); + + MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum = NodeNum; + + AMJGameStateDungeon* MJGS = GetGameState(); + + if (MJGS ) + { + MJGS->SaveToInstancedDungeonSessionData(PS->GetPlayerSessionDataRef().CurrentDungeonMapNum); + } + + MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum = NodeNum; + + } + + return Super::TravelToMapByNode(MapName, NodeNum); + + +} + diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.h b/Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.h similarity index 78% rename from Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.h rename to Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.h index 6259ca33..259ccd76 100644 --- a/Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.h +++ b/Source/ProjectMJ/TG/GameMode/MJGameModeDungeon.h @@ -4,7 +4,7 @@ #include "CoreMinimal.h" #include "GameMode/MJGameModeBase.h" -#include "MJGameModeDungeonTG.generated.h" +#include "MJGameModeDungeon.generated.h" /** * Class Description: Dungeon`s Gamemode @@ -14,20 +14,20 @@ * Last Modified Date: 2025-06-14 */ UCLASS() -class PROJECTMJ_API AMJGameModeDungeonTG : public AMJGameModeBase +class PROJECTMJ_API AMJGameModeDungeon : public AMJGameModeBase { GENERATED_BODY() public: - AMJGameModeDungeonTG(); - + AMJGameModeDungeon(); + + virtual bool TravelToMapByNode(const FString MapName, const uint8 NodeNum) override; protected: virtual void BeginPlay() override; - virtual bool TravelToMapByNode(const FString MapName, const uint8 NodeNum) override; }; diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.cpp b/Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.cpp deleted file mode 100644 index f51199d5..00000000 --- a/Source/ProjectMJ/TG/GameMode/MJGameModeDungeonTG.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJGameModeDungeonTG.h" - -#include "ProjectMJ.h" -#include "Kismet/GameplayStatics.h" -#include "Player/MJPlayerState.h" -#include "TG/MJGameInstanceTG.h" -#include "TG/GameState/MJGameStateDungeonTG.h" - - -AMJGameModeDungeonTG::AMJGameModeDungeonTG() -{ - MJ_LOG(LogTG,Log,TEXT("New GameMode Instance Created")); -} - -void AMJGameModeDungeonTG::BeginPlay() -{ - Super::BeginPlay(); - - AMJPlayerState* MJPS = Cast(UGameplayStatics::GetPlayerState(this,0)); - - if (GetGameInstance()->bIsPlayerStateDirty) - { - if (MJPS) - { - MJPS->LoadFromInstancedPlayerSessionData(); - } - } - - AMJGameStateDungeonTG* MJGS = GetGameState(); - if (MJGS && GetGameInstance()->bIsDungeonGameStateDirty) - { - MJGS->LoadFromInstancedDungeonSessionData(MJPS->GetPlayerSessionDataRef().CurrentDungeonMapNum); - } - -} - -bool AMJGameModeDungeonTG::TravelToMapByNode(const FString MapName, const uint8 NodeNum) -{ - - UMJGameInstanceTG* MJGI = GetGameInstance(); - if (MJGI) - { - AMJPlayerState* PS = Cast(UGameplayStatics::GetPlayerState(this,0)); - - MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum = NodeNum; - - AMJGameStateDungeonTG* MJGS = GetGameState(); - - if (MJGS) - { - MJGS->SaveToInstancedDungeonSessionData(PS->GetPlayerSessionDataRef().CurrentDungeonMapNum); - } - - MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum = NodeNum; - - } - - return Super::TravelToMapByNode(MapName, NodeNum); - - -} - diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeTown.cpp b/Source/ProjectMJ/TG/GameMode/MJGameModeTown.cpp new file mode 100644 index 00000000..74f1735e --- /dev/null +++ b/Source/ProjectMJ/TG/GameMode/MJGameModeTown.cpp @@ -0,0 +1,24 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJGameModeTown.h" + +#include "ProjectMJ.h" +#include "Kismet/GameplayStatics.h" + +AMJGameModeTown::AMJGameModeTown() +{ + MJ_LOG(LogTG,Log,TEXT("New GameMode Instance Created")); +} + +void AMJGameModeTown::BeginPlay() +{ + Super::BeginPlay(); + + APlayerController* NewPC = UGameplayStatics::GetPlayerController(GetWorld(), 0); + if (NewPC) + { + UE_LOG(LogTemp, Warning, TEXT("PC After OpenLevel: %p"), NewPC); + } + +} diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.h b/Source/ProjectMJ/TG/GameMode/MJGameModeTown.h similarity index 77% rename from Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.h rename to Source/ProjectMJ/TG/GameMode/MJGameModeTown.h index a59d39b9..3d80b2a7 100644 --- a/Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.h +++ b/Source/ProjectMJ/TG/GameMode/MJGameModeTown.h @@ -4,7 +4,7 @@ #include "CoreMinimal.h" #include "GameMode/MJGameModeBase.h" -#include "MJGameModeTownTG.generated.h" +#include "MJGameModeTown.generated.h" /** * Class Description: Town`s Gamemode @@ -14,12 +14,12 @@ * Last Modified Date: 2025-06-11 */ UCLASS() -class PROJECTMJ_API AMJGameModeTownTG : public AMJGameModeBase +class PROJECTMJ_API AMJGameModeTown : public AMJGameModeBase { GENERATED_BODY() public: - AMJGameModeTownTG(); + AMJGameModeTown(); virtual void BeginPlay() override; diff --git a/Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.cpp b/Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.cpp deleted file mode 100644 index 3df48501..00000000 --- a/Source/ProjectMJ/TG/GameMode/MJGameModeTownTG.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJGameModeTownTG.h" - -#include "ProjectMJ.h" - -AMJGameModeTownTG::AMJGameModeTownTG() -{ - MJ_LOG(LogTG,Log,TEXT("New GameMode Instance Created")); -} - -void AMJGameModeTownTG::BeginPlay() -{ - Super::BeginPlay(); - -} diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.cpp b/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.cpp new file mode 100644 index 00000000..115b210d --- /dev/null +++ b/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.cpp @@ -0,0 +1,518 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJGameStateDungeon.h" + +#include "EngineUtils.h" +#include "NavigationSystem.h" +#include "Character/MJForestCreatureCharacter.h" +#include "EnvironmentQuery/EnvQueryManager.h" +#include "Kismet/GameplayStatics.h" +#include "TG/MJGameInstance.h" +#include "TG/Actor/MJDungeonAISpawnPointActor.h" +#include "TG/AI/MJAIBossCharacterTG.h" +#include "TG/DataTable/MJStaticAISpawnRow.h" +#include "TG/Interface/MJInstancedActorInterface.h" +#include "TG/SubSystem/MJDungeonGenerationSubSystem.h" + + +AMJGameStateDungeon::AMJGameStateDungeon() +{ + LoadedDungeonNodeNum = 255; + + CurrentWaveNum = 1; + WaveAISpawnMaxNum = 5; + + StaticAISpawnMaxNum = 10; + + CurrentSpawnedAINum = 0; +} + +void AMJGameStateDungeon::BeginPlay() +{ + Super::BeginPlay(); + + UMJDungeonGenerationSubSystem* GS = GetGameInstance()->GetSubsystem(); + + UMJGameInstance * MJGI = GetGameInstance(); + if (MJGI) + { + //LoadedDungeonSessionData = FMJDungeonSessionData(); + LoadFromInstancedDungeonSessionData(MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum); + } + + if (GS) + { + EMJNodeType CurrentNodeType = GS->GetDungeonGraph()->Nodes[LoadedDungeonSessionData.DungeonNodeNum].NodeType; + + switch (CurrentNodeType) + { + case EMJNodeType::Battle: + Initialize_BattleNode(); + break; + + case EMJNodeType::Boss: + Initialize_BossNode(); + break; + + case EMJNodeType::Reward: + Initialize_RewardNode(); + break; + default: + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Magenta, FString::Printf(TEXT("InValid!!!"))); + break; + } + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Magenta, FString::Printf(TEXT("GameState is nullptr!!!"))); + } +} + +void AMJGameStateDungeon::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + Super::EndPlay(EndPlayReason); + + for (auto& Iter : SpawnedActorRefs) + { + if (Iter.IsValid()) + { + Iter.Get()->OnDestroyed.RemoveDynamic(this,&AMJGameStateDungeon::OnAIDestroy); + + AMJCharacterBase* Char = Cast(Iter.Get()); + if (IsValid(Char)) + { + Char->GetMesh()->GetAnimInstance()->StopAllMontages(0.2f); + } + + } + } + GetWorldTimerManager().ClearAllTimersForObject(this); + + +} + + +void AMJGameStateDungeon::Initialize_BattleNode() +{ + if (LoadedDungeonSessionData.DungeonContext == EMJDungeonContext::InActive) + { + if (LoadedDungeonSessionData.AISpawnType == EMJAISpawnType::Static) + { + UGameplayStatics::GetAllActorsOfClass(GetWorld(),AMJDungeonAISpawnPointActor::StaticClass(),StaticSpawnPointActors); + + // using StaticAISpawnData + TArray LoadedAllRows; + LoadedStaticDataTable->GetAllRows(TEXT("Load All StaticAISpawnData Rows"),LoadedAllRows); + + float TotalWeight = 0.0f; + + for (const auto& Iter : LoadedAllRows) + { + TotalWeight += Iter->SpawnWeight; + } + + + for (const auto& Iter : StaticSpawnPointActors) + { + AMJDungeonAISpawnPointActor* SpawnPointActor = Cast(Iter); + + if (IsValid(SpawnPointActor)) + { + FVector IterSpawnPoint = Iter->GetActorLocation(); + uint8 IterSpawnAIMaxNum = SpawnPointActor->NumberToSpawn; + + FNavLocation ResultLocation; + + UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld()); + + if (NavSys) + { + int32 CurrentPointSpawnedAINum = 0; + while (CurrentPointSpawnedAINum < IterSpawnAIMaxNum) + { + TSubclassOf WeightedChosenClass; + + float RandWeight = FMath::RandRange(0.0f,TotalWeight); + + float AccumulatedWeight = 0.0f; + + for (const auto& Row : LoadedAllRows) + { + AccumulatedWeight += Row->SpawnWeight; + + if (AccumulatedWeight >= RandWeight) + { + WeightedChosenClass = Row->EnemyClass; + break; + } + } + + bool bIsFound = NavSys->GetRandomPointInNavigableRadius(IterSpawnPoint, 1000.f,ResultLocation); + + if (bIsFound) + { + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = this; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + + AActor* NewAIActor = GetWorld()->SpawnActor(WeightedChosenClass,ResultLocation,FRotator(), SpawnParams); + + check(NewAIActor); + + if (NewAIActor) + { + NewAIActor->OnDestroyed.AddDynamic(this, &AMJGameStateDungeon::OnAIDestroy); + SpawnedActorRefs.Add(NewAIActor); + ++CurrentSpawnedAINum; + ++CurrentPointSpawnedAINum; + + OnActorSpawned.Broadcast(NewAIActor); + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("AISpawn Failed!!"))); + } + } + } + } + } + } + } + else if (LoadedDungeonSessionData.AISpawnType == EMJAISpawnType::Wave) + { + GetWorldTimerManager().SetTimer(WaveAISpawn_ConditionCheckTimerHandle, this, &AMJGameStateDungeon::CheckSpawnAICondition, 2.0f, true); + + if (LoadedWaveDataTable) + { + GetWaveDataRowByIndex(CurrentWaveNum); + } + } + + LoadedDungeonSessionData.DungeonContext = EMJDungeonContext::Activated; + + } + +} + +void AMJGameStateDungeon::Initialize_BossNode() +{ + if (LoadedDungeonSessionData.DungeonContext == EMJDungeonContext::InActive) + { + BossAIRef = Cast(UGameplayStatics::GetActorOfClass(GetWorld(),AMJForestCreatureCharacter::StaticClass())); + if (BossAIRef) + { + BossAIRef->OnDestroyed.AddDynamic(this ,&AMJGameStateDungeon::OnAIDestroy); + SpawnedActorRefs.Add(BossAIRef); + CurrentSpawnedAINum++; + GetWorldTimerManager().SetTimer(OnBossSpawnedBroadCastTimerHandle,this, &ThisClass::PublishOnBossSpawned,3.0f); + + OnActorSpawned.Broadcast(BossAIRef); + OnAIBossSpawned.Broadcast(); + } + LoadedDungeonSessionData.DungeonContext = EMJDungeonContext::Activated; + } +} + +void AMJGameStateDungeon::Initialize_RewardNode() +{ + +} + +bool AMJGameStateDungeon::GetWaveDataRowByIndex(int32 InputWaveRowNum) +{ + // Hard coded wave name for now + FName Name = *FString::Printf(TEXT("Wave%d"), InputWaveRowNum); + FMJWaveAISpawnDataRow* RowPtr = LoadedWaveDataTable->FindRow(Name, TEXT("FindRow is Failed")); + + if (RowPtr) + { + LoadedWaveDataRow.EnemyCount = RowPtr->EnemyCount; + LoadedWaveDataRow.EnemyPool = RowPtr->EnemyPool; + LoadedWaveDataRow.WaveNum = RowPtr->WaveNum; + + return true; + } + return false; +} + + + +void AMJGameStateDungeon::CheckSpawnAICondition() +{ + if (LoadedDungeonSessionData.DungeonContext != EMJDungeonContext::Activated) + { + return; + } + + GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Magenta, FString::Printf(TEXT("Check AISpawn Condition Call"))); + + if (CurrentSpawnedAINum < WaveAISpawnMaxNum) + { + if (LoadedWaveDataRow.EnemyCount <= 0) + { + bool GetNextWave = GetWaveDataRowByIndex(CurrentWaveNum + 1); + + if (GetNextWave) + { + CurrentWaveNum += 1; + } + else if (!GetNextWave && CurrentSpawnedAINum <= 0) + { + GetWorldTimerManager().ClearTimer(WaveAISpawn_ConditionCheckTimerHandle); + LoadedDungeonSessionData.DungeonContext = EMJDungeonContext::Cleared; + TargetPortalToSpawn = DungeonPortalActorClass; + GetWorldTimerManager().SetTimer(DungeonPortalSpawnTimerHandle, this, &AMJGameStateDungeon::SpawnDungeonPortal, 2.0f, false); + } + } + else + { + SpawnAI(); + } + } +} + +void AMJGameStateDungeon::SpawnAI() +{ + if (LoadedDungeonSessionData.DungeonContext != EMJDungeonContext::Activated) + { + return; + } + + ACharacter* Player = UGameplayStatics::GetPlayerCharacter(this,0); + + if (Player) + { + FEnvQueryRequest Request(EQSQuery_WaveRandomSpawn, Player); + + // Async Call Lamda func + Request.Execute(EEnvQueryRunMode::AllMatching, FQueryFinishedSignature::CreateLambda([this, Player](TSharedPtr Result) + { + if (Result->IsSuccessful()) + { + TArray AllLocations; + + AllLocations.Reserve(Result->Items.Num()); + + Result->GetAllAsLocations(AllLocations); + + int i = 0; + while (CurrentSpawnedAINum < WaveAISpawnMaxNum) + { + + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = this; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + TSubclassOf GetActor = GetActorFromPool(); + AActor* NewAIActor = GetWorld()->SpawnActor(GetActor, AllLocations[i],FRotator(), SpawnParams); + + //check(NewAIActor); + + if (NewAIActor) + { + + // Add Delegate when it`s spawned by GameState + NewAIActor->OnDestroyed.AddDynamic(this, &AMJGameStateDungeon::OnAIDestroy); + SpawnedActorRefs.Add(NewAIActor); + ++CurrentSpawnedAINum; + --LoadedWaveDataRow.EnemyCount; + ++i; + + OnActorSpawned.Broadcast(NewAIActor); + + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("AISpawn Failed!!"))); + } + } + } + })); + } +} + +void AMJGameStateDungeon::OnAIDestroy(AActor* DestroyedActor) +{ + if (DestroyedActor->IsPendingKillPending()) + { + --CurrentSpawnedAINum; + + UMJDungeonGenerationSubSystem* GS = GetGameInstance()->GetSubsystem(); + + if (GS) + { + EMJNodeType CurrentNodeType = GS->GetDungeonGraph()->Nodes[LoadedDungeonSessionData.DungeonNodeNum].NodeType; + + EMJAISpawnType CurrentAISpawnType = LoadedDungeonSessionData.AISpawnType; + bool bIsActivatedMap = (LoadedDungeonSessionData.DungeonContext == EMJDungeonContext::Activated); + bool bIsAllEnemyDied = CurrentSpawnedAINum <= 0; + + if (CurrentNodeType == EMJNodeType::Battle) + { + if (CurrentAISpawnType == EMJAISpawnType::Static && bIsActivatedMap && bIsAllEnemyDied) + { + LoadedDungeonSessionData.DungeonContext = EMJDungeonContext::Cleared; + TargetPortalToSpawn = DungeonPortalActorClass; + + GetWorldTimerManager().SetTimer(DungeonPortalSpawnTimerHandle, this, &AMJGameStateDungeon::SpawnDungeonPortal, 2.0f, false); + } + else if (CurrentAISpawnType == EMJAISpawnType::Wave && bIsActivatedMap && bIsAllEnemyDied ) + { + // Wave AISpawn Mode`s End called at CheckAISpawn callback + } + + } + else if (CurrentNodeType == EMJNodeType::Boss) + { + if (CurrentAISpawnType == EMJAISpawnType::Static && bIsActivatedMap && bIsAllEnemyDied) + { + LoadedDungeonSessionData.DungeonContext = EMJDungeonContext::Cleared; + TargetPortalToSpawn = EndPortalActorClass; + + OnAIBossDied.Broadcast(); + GetWorldTimerManager().SetTimer(DungeonPortalSpawnTimerHandle, this, &AMJGameStateDungeon::SpawnDungeonPortal, 2.0f, false); + } + } + else if (CurrentNodeType == EMJNodeType::Reward) + { + + } + } + } +} + +TSubclassOf AMJGameStateDungeon::GetActorFromPool() +{ + // pick Actor class randomly + TArray> Keys; + LoadedWaveDataRow.EnemyPool.GetKeys(Keys); + + if (Keys.Num() == 0) + { + return nullptr; + } + + const int32 RandomIndex = FMath::RandRange(0,Keys.Num() - 1); + + if (Keys[RandomIndex] == nullptr) + { + return GetActorFromPool(); + } + + return Keys[RandomIndex]; +} + +void AMJGameStateDungeon::Test_DeleteAI() +{ + for (auto& Iter : SpawnedActorRefs) + { + if (Iter.IsValid()) + { + Iter.Get()->Destroy(); + } + } +} + +void AMJGameStateDungeon::SpawnDungeonPortal() +{ + if (LoadedDungeonSessionData.DungeonContext != EMJDungeonContext::Cleared) + { + return; + } + + ACharacter* Player = UGameplayStatics::GetPlayerCharacter(this,0); + if (Player) + { + FEnvQueryRequest Request(EQSQuery_WaveRandomSpawn, Player); + + Request.Execute(EEnvQueryRunMode::AllMatching, FQueryFinishedSignature::CreateLambda([this, Player](TSharedPtr Result) + { + if (Result->IsSuccessful()) + { + FVector Location; + + Location = Result->GetItemAsLocation(0); + + FActorSpawnParameters Params; + Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + AActor* Portal = GetWorld()->SpawnActor(TargetPortalToSpawn,Location,FRotator(),Params); + if (Portal) + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Magenta, FString::Printf(TEXT("PortalActor is Successfully Spawned"))); + + OnActorSpawned.Broadcast(Portal); + } + } + })); + + } +} + + +void AMJGameStateDungeon::SetDungeonSessionData(const FMJDungeonSessionData& DungeonSessionData) +{ + LoadedDungeonSessionData = DungeonSessionData; +} + +void AMJGameStateDungeon::SaveToInstancedDungeonSessionData(uint8 SaveToNum) +{ + UMJGameInstance* MJGI = GetGameInstance(); + + FMJDungeonSessionData NewDungeonSessionData = LoadedDungeonSessionData; + NewDungeonSessionData.SpawnInfos.Empty(); + + if (MJGI) + { + for (TActorIterator Iter(GetWorld()); Iter ; ++Iter) + { + if (Iter->Implements()) + { + AActor* Actor = *Iter; + FMJDungeonActorInfo Info; + Info.ActorClass = Actor->GetClass(); + Info.Transform = Actor->GetActorTransform(); + NewDungeonSessionData.SpawnInfos.Add(Info); + } + } + + MJGI->GetDungeonSessionDataRef()[SaveToNum] = NewDungeonSessionData; + } +} + +void AMJGameStateDungeon::LoadFromInstancedDungeonSessionData(uint8 LoadFromNum) +{ + UMJGameInstance* MJGI = GetGameInstance(); + + if (MJGI) + { + SetDungeonSessionData(MJGI->GetDungeonSessionDataRef()[LoadFromNum]); + + for (auto iter : LoadedDungeonSessionData.SpawnInfos) + { + if (iter.ActorClass->ImplementsInterface(UMJInstancedActorInterface::StaticClass())) + { + iter.ActorClass.LoadSynchronous(); + UClass* LoadedClass = iter.ActorClass.Get(); + FActorSpawnParameters Params; + Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + AActor* SavedActor = GetWorld()->SpawnActor(LoadedClass,iter.Transform,Params); + + if (SavedActor) + { + //SavedActor->SetActorScale3D(iter.Transform.GetScale3D()); + OnActorSpawned.Broadcast(SavedActor); + } + } + } + } +} + + +void AMJGameStateDungeon::PublishOnBossSpawned() +{ + OnAIBossSpawned.Broadcast(); +} \ No newline at end of file diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.h b/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.h new file mode 100644 index 00000000..e5c7ddfb --- /dev/null +++ b/Source/ProjectMJ/TG/GameState/MJGameStateDungeon.h @@ -0,0 +1,177 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/GameStateBase.h" +#include "TG/Interface/MJBossEventManager.h" +#include "TG/DataTable/MJWaveAISpawnRow.h" +#include "TG/Struct/MJDungeonSessionDataStruct.h" +#include "MJGameStateDungeon.generated.h" + +/** + * Class Description: 던전의 상태를 저장할 GameState + * Author: 차태관 + * Created Date: 2025-06-13 + * Last Modified By: 차태관 + * Last Modified Date: 2025-06-13 + */ + +class UMJMiniMapIconMeshComponent; +class AMJForestCreatureCharacter; +class UEnvQuery; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMJAIBossOnHealthChangedSignature, float, Delta); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMJAIBossOnSpawnedSignature); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMJAIBossOnDiedSignature); + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMJActorSpawnedSignature, AActor*, Actor); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMJAIOnDestroyedSignature); + +UCLASS() +class PROJECTMJ_API AMJGameStateDungeon : public AGameStateBase, public IMJBossEventManager +{ + GENERATED_BODY() + +public: + // Initialize Section + + AMJGameStateDungeon(); + + virtual void BeginPlay() override; + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + + + UFUNCTION(BlueprintCallable) + void SetDungeonSessionData(const FMJDungeonSessionData& DungeonSessionData); + + UFUNCTION(BlueprintCallable) + void SaveToInstancedDungeonSessionData(uint8 SaveToNum); + + UFUNCTION() + void LoadFromInstancedDungeonSessionData(uint8 LoadFromNum); + + // Miscellaneous + + UPROPERTY(BlueprintAssignable) + FMJAIBossOnHealthChangedSignature OnAIBossHealthChanged; + + UPROPERTY(BlueprintAssignable) + FMJActorSpawnedSignature OnActorSpawned; + + UPROPERTY(BlueprintAssignable) + FMJAIBossOnSpawnedSignature OnAIBossSpawned; + + UPROPERTY(BlueprintAssignable) + FMJAIBossOnDiedSignature OnAIBossDied; + + + UFUNCTION(BlueprintCallable) + virtual void PublishOnBossSpawned() override; + +protected: + + + // Initialize Section + UFUNCTION() + void Initialize_BattleNode(); + + UFUNCTION() + void Initialize_BossNode(); + + UFUNCTION() + void Initialize_RewardNode(); + + // AI Spawn Section + + UFUNCTION(BlueprintCallable) + bool GetWaveDataRowByIndex(int32 InputWaveRowNum); + + UFUNCTION(BlueprintCallable) + void CheckSpawnAICondition(); + + UFUNCTION(BlueprintCallable) + void SpawnAI(); + + UFUNCTION(BlueprintCallable) + TSubclassOf GetActorFromPool(); + + UFUNCTION(BlueprintCallable) + void Test_DeleteAI(); + UFUNCTION(BlueprintCallable) + void OnAIDestroy(AActor* DestroyedActor); + + UFUNCTION() + void SpawnDungeonPortal(); + + // Dungeon Session Section + + UPROPERTY(BlueprintReadOnly) + FMJDungeonSessionData LoadedDungeonSessionData; + + UPROPERTY(BlueprintReadOnly) + uint8 LoadedDungeonNodeNum; + + // Static AISpawn Section + + UPROPERTY(BlueprintReadOnly) + TArray StaticSpawnPointActors; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr LoadedStaticDataTable; + + UPROPERTY(EditDefaultsOnly) + int32 StaticAISpawnMaxNum; + + // Wave Section + + UPROPERTY() + FTimerHandle WaveAISpawn_ConditionCheckTimerHandle; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr EQSQuery_WaveRandomSpawn; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr LoadedWaveDataTable; + + UPROPERTY(BlueprintReadOnly) + FMJWaveAISpawnDataRow LoadedWaveDataRow; + + UPROPERTY(BlueprintReadOnly) + int32 CurrentWaveNum; + + // AI Spawn Condition Section + + UPROPERTY(BlueprintReadOnly) + int32 WaveAISpawnMaxNum; + + UPROPERTY(BlueprintReadWrite) + int32 CurrentSpawnedAINum; + + UPROPERTY() + TArray> SpawnedActorRefs; + + UPROPERTY(BlueprintAssignable) + FMJAIOnDestroyedSignature OnAIDestroyed; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf DungeonPortalActorClass; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf EndPortalActorClass; + + UPROPERTY() + TSubclassOf TargetPortalToSpawn; + + UPROPERTY() + FTimerHandle DungeonPortalSpawnTimerHandle; + + // Boss Section + + UPROPERTY() + TObjectPtr BossAIRef; + + UPROPERTY() + FTimerHandle OnBossSpawnedBroadCastTimerHandle; + +}; diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.cpp b/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.cpp deleted file mode 100644 index e4c5b67e..00000000 --- a/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJGameStateDungeonTG.h" - -#include "EngineUtils.h" -#include "TG/MJGameInstanceTG.h" -#include "TG/Actor/MJDummyActorTG.h" - - -AMJGameStateDungeonTG::AMJGameStateDungeonTG() -{ - LoadedDungeonSessionData = FMJDungeonSessionData(); - LoadedDungeonNodeNum = 255; -} - -void AMJGameStateDungeonTG::PostInitializeComponents() -{ - Super::PostInitializeComponents(); - -} - -void AMJGameStateDungeonTG::BeginPlay() -{ - Super::BeginPlay(); -} - -void AMJGameStateDungeonTG::EndPlay(const EEndPlayReason::Type EndPlayReason) -{ - Super::EndPlay(EndPlayReason); -} - -void AMJGameStateDungeonTG::SetDungeonSessionData(FMJDungeonSessionData& DungeonSessionData) -{ - LoadedDungeonSessionData = DungeonSessionData; -} - -void AMJGameStateDungeonTG::SaveToInstancedDungeonSessionData(uint8 SaveToNum) -{ - UMJGameInstanceTG* MJGI = GetGameInstance(); - - FMJDungeonSessionData NewDungeonSessionData; - if (MJGI && !MJGI->bIsDungeonGameStateDirty) - { - for (TActorIterator Iter(GetWorld()); Iter ; ++Iter) - { - if (Iter->Implements()) - { - - AActor* Actor = *Iter; - FMJDungeonActorInfo Info; - Info.ActorClass = Actor->GetClass(); - Info.Transform = Actor->GetActorTransform(); - NewDungeonSessionData.SpawnInfos.Add(Info); - - } - } - - - - MJGI->GetDungeonSessionDataRef()[SaveToNum] = NewDungeonSessionData; - MJGI->bIsDungeonGameStateDirty = true; - } -} - -void AMJGameStateDungeonTG::LoadFromInstancedDungeonSessionData(uint8 LoadFromNum) -{ - UMJGameInstanceTG* MJGI = GetGameInstance(); - - if (MJGI) - { - - SetDungeonSessionData(MJGI->GetDungeonSessionDataRef()[LoadFromNum]); - - for (auto iter : LoadedDungeonSessionData.SpawnInfos) - { - if (iter.ActorClass->ImplementsInterface(UMJInstancedActorInterface::StaticClass())) - { - iter.ActorClass.LoadSynchronous(); - UClass* LoadedClass = iter.ActorClass.Get(); - FActorSpawnParameters Params; - GetWorld()->SpawnActor(LoadedClass,iter.Transform,Params); - } - } - } -} - -void AMJGameStateDungeonTG::PublishOnBossHealthChanged(float Delta) -{ - - OnAIBossHealthChanged.Broadcast(Delta); -} - -void AMJGameStateDungeonTG::PublishOnBossSpawned(float Health) -{ - OnAIBossSpawned.Broadcast(Health); -} diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.h b/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.h deleted file mode 100644 index 6931bc62..00000000 --- a/Source/ProjectMJ/TG/GameState/MJGameStateDungeonTG.h +++ /dev/null @@ -1,67 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/GameStateBase.h" -#include "TG/Interface/MJBossEventManagerTG.h" -#include "TG/Struct/MJDungeonSessionDataStruct.h" -#include "MJGameStateDungeonTG.generated.h" - -/** - * Class Description: 던전의 상태를 저장할 GameState - * Author: 차태관 - * Created Date: 2025-06-13 - * Last Modified By: 차태관 - * Last Modified Date: 2025-06-13 - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMJAIBossOnHealthChangedSignature, float, Delta); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMJAIBossOnSpawnedSignature, float, Health); - -UCLASS() -class PROJECTMJ_API AMJGameStateDungeonTG : public AGameStateBase, public IMJBossEventManagerTG -{ - GENERATED_BODY() - -public: - AMJGameStateDungeonTG(); - - virtual void PostInitializeComponents() override; - virtual void BeginPlay() override; - virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; - - UFUNCTION(BlueprintCallable) - void SetDungeonSessionData(FMJDungeonSessionData& DungeonSessionData); - - UFUNCTION(BlueprintCallable) - void SaveToInstancedDungeonSessionData(uint8 SaveToNum); - - UFUNCTION() - void LoadFromInstancedDungeonSessionData(uint8 LoadFromNum); - - UPROPERTY(BlueprintAssignable) - FMJAIBossOnHealthChangedSignature OnAIBossHealthChanged; - - UPROPERTY(BlueprintAssignable) - FMJAIBossOnSpawnedSignature OnAIBossSpawned; - - UFUNCTION(BlueprintCallable) - virtual void PublishOnBossHealthChanged(float Delta) override; - - UFUNCTION(BlueprintCallable) - virtual void PublishOnBossSpawned(float Health) override; - -protected: - - UPROPERTY(BlueprintReadOnly) - FMJDungeonSessionData LoadedDungeonSessionData; - - UPROPERTY(BlueprintReadOnly) - uint8 LoadedDungeonNodeNum; - - - - - -}; diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateTownTG.cpp b/Source/ProjectMJ/TG/GameState/MJGameStateTown.cpp similarity index 54% rename from Source/ProjectMJ/TG/GameState/MJGameStateTownTG.cpp rename to Source/ProjectMJ/TG/GameState/MJGameStateTown.cpp index 1e8183f1..2d1292b8 100644 --- a/Source/ProjectMJ/TG/GameState/MJGameStateTownTG.cpp +++ b/Source/ProjectMJ/TG/GameState/MJGameStateTown.cpp @@ -1,8 +1,8 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "MJGameStateTownTG.h" +#include "MJGameStateTown.h" -AMJGameStateTownTG::AMJGameStateTownTG() +AMJGameStateTown::AMJGameStateTown() { } diff --git a/Source/ProjectMJ/TG/GameState/MJGameStateTownTG.h b/Source/ProjectMJ/TG/GameState/MJGameStateTown.h similarity index 76% rename from Source/ProjectMJ/TG/GameState/MJGameStateTownTG.h rename to Source/ProjectMJ/TG/GameState/MJGameStateTown.h index 1978129a..9c62369f 100644 --- a/Source/ProjectMJ/TG/GameState/MJGameStateTownTG.h +++ b/Source/ProjectMJ/TG/GameState/MJGameStateTown.h @@ -4,7 +4,7 @@ #include "CoreMinimal.h" #include "GameFramework/GameStateBase.h" -#include "MJGameStateTownTG.generated.h" +#include "MJGameStateTown.generated.h" /** * Class Description: 마을의 상태를 저장할 GameState @@ -14,12 +14,12 @@ * Last Modified Date: 2025-06-14 */ UCLASS() -class PROJECTMJ_API AMJGameStateTownTG : public AGameStateBase +class PROJECTMJ_API AMJGameStateTown : public AGameStateBase { GENERATED_BODY() public: - AMJGameStateTownTG(); + AMJGameStateTown(); protected: diff --git a/Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.h b/Source/ProjectMJ/TG/Interface/MJBossEventManager.h similarity index 64% rename from Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.h rename to Source/ProjectMJ/TG/Interface/MJBossEventManager.h index 3cf8fd79..c36d5194 100644 --- a/Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.h +++ b/Source/ProjectMJ/TG/Interface/MJBossEventManager.h @@ -4,11 +4,11 @@ #include "CoreMinimal.h" #include "UObject/Interface.h" -#include "MJBossEventManagerTG.generated.h" +#include "MJBossEventManager.generated.h" // This class does not need to be modified. UINTERFACE(MinimalAPI) -class UMJBossEventManagerTG : public UInterface +class UMJBossEventManager : public UInterface { GENERATED_BODY() }; @@ -20,14 +20,12 @@ class UMJBossEventManagerTG : public UInterface * Last Modified By: Cha Tae Gwan * Last Modified Date: 2025-06-30 */ -class PROJECTMJ_API IMJBossEventManagerTG +class PROJECTMJ_API IMJBossEventManager { GENERATED_BODY() public: - - virtual void PublishOnBossHealthChanged(float Delta) = 0; - - virtual void PublishOnBossSpawned(float Health) = 0; + + virtual void PublishOnBossSpawned() = 0; }; diff --git a/Source/ProjectMJ/TG/MJGameInstance.cpp b/Source/ProjectMJ/TG/MJGameInstance.cpp new file mode 100644 index 00000000..32389bc1 --- /dev/null +++ b/Source/ProjectMJ/TG/MJGameInstance.cpp @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "MJGameInstance.h" + +#include "GameFramework/GameUserSettings.h" + +UMJGameInstance::UMJGameInstance() +{ + PlayerSessionData.CurrentDungeonMapNum = -1; + + + +} + +void UMJGameInstance::Init() +{ + Super::Init(); +} + +void UMJGameInstance::OnStart() +{ + Super::OnStart(); + + // Apply Default Settings before Starting Level + UGameUserSettings* CurrentGameUserSettings = GEngine->GetGameUserSettings(); + + if (CurrentGameUserSettings) + { + CurrentGameUserSettings->SetFullscreenMode(EWindowMode::Type::Windowed); + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1600,900)); + CurrentGameUserSettings->SetOverallScalabilityLevel(1); // Medium + + CurrentGameUserSettings->ApplySettings(false); + CurrentGameUserSettings->SaveSettings(); + } + +} + +FMJPlayerSessionData& UMJGameInstance::GetPlayerSessionDataRef() +{ + return PlayerSessionData; +} + +TArray& UMJGameInstance::GetDungeonSessionDataRef() +{ + return DungeonSessionData; +} diff --git a/Source/ProjectMJ/TG/MJGameInstanceTG.h b/Source/ProjectMJ/TG/MJGameInstance.h similarity index 62% rename from Source/ProjectMJ/TG/MJGameInstanceTG.h rename to Source/ProjectMJ/TG/MJGameInstance.h index f672dffe..fe4c24bd 100644 --- a/Source/ProjectMJ/TG/MJGameInstanceTG.h +++ b/Source/ProjectMJ/TG/MJGameInstance.h @@ -6,9 +6,11 @@ #include "Engine/GameInstance.h" #include "Struct/MJDungeonSessionDataStruct.h" #include "Struct/MJPlayerSessionDataStruct.h" -#include "MJGameInstanceTG.generated.h" +#include "MJGameInstance.generated.h" +class UMJAnimMontageDataAsset; +class UMJProjectileDataAsset; class AMJPlayerCharacter; class UMJHttpDownloadManager; class UMJSaveGame; @@ -19,20 +21,23 @@ class UDataTable; * Created Date: 2025-06-11 * Last Modified By: 차태관 * Last Modified Date: 2025-06-11 + * Modified Date: 2025.07.04 + * Modified By: 신동민 + * Modified Description: 전역에서 접근할 DataAsset 생성 */ UCLASS() -class PROJECTMJ_API UMJGameInstanceTG : public UGameInstance +class PROJECTMJ_API UMJGameInstance : public UGameInstance { GENERATED_BODY() public: - UMJGameInstanceTG(); + UMJGameInstance(); virtual void Init() override; + virtual void OnStart() override; TObjectPtr HttpDownloader; bool bIsPlayerStateDirty = false; - bool bIsDungeonGameStateDirty = false; UFUNCTION(BlueprintCallable) @@ -42,7 +47,17 @@ class PROJECTMJ_API UMJGameInstanceTG : public UGameInstance TArray& GetDungeonSessionDataRef(); UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - TObjectPtr SkillDataTable; + TObjectPtr PlayerSkillDataTable; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + TObjectPtr EnemySkillDataTable; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + TObjectPtr ItemDataTable; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + TObjectPtr EnemyDataTable; + protected: UPROPERTY(VisibleAnywhere) diff --git a/Source/ProjectMJ/TG/MJGameInstanceTG.cpp b/Source/ProjectMJ/TG/MJGameInstanceTG.cpp deleted file mode 100644 index f2773940..00000000 --- a/Source/ProjectMJ/TG/MJGameInstanceTG.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "MJGameInstanceTG.h" - -UMJGameInstanceTG::UMJGameInstanceTG() -{ - PlayerSessionData.CurrentDungeonMapNum = -1; - -} - -void UMJGameInstanceTG::Init() -{ - Super::Init(); -} - -FMJPlayerSessionData& UMJGameInstanceTG::GetPlayerSessionDataRef() -{ - return PlayerSessionData; -} - -TArray& UMJGameInstanceTG::GetDungeonSessionDataRef() -{ - return DungeonSessionData; -} diff --git a/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.cpp b/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.cpp deleted file mode 100644 index 19e882b4..00000000 --- a/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "MJCharacterAttributeSaveDataStruct.h" - -FMJCharacterAttributeSaveData& FMJCharacterAttributeSaveData::operator=(const UMJCharacterAttributeSet& AttributeSet) -{ -// Stat | Level - Level = AttributeSet.GetLevel(); - MaxLevel = AttributeSet.GetMaxLevel(); - Experience = AttributeSet.GetExperience(); - MaxExperience = AttributeSet.GetMaxExperience(); - DropExperience = AttributeSet.GetDropExperience(); - MaxDropExperience = AttributeSet.GetMaxDropExperience(); - - // Stat | Resource - Health = AttributeSet.GetHealth(); - MaxHealth = AttributeSet.GetMaxHealth(); - HealthRegeneration = AttributeSet.GetHealthRegeneration(); - MaxHealthRegeneration = AttributeSet.GetMaxHealthRegeneration(); - - Stamina = AttributeSet.GetStamina(); - MaxStamina = AttributeSet.GetMaxStamina(); - StaminaRegeneration = AttributeSet.GetStaminaRegeneration(); - MaxStaminaRegeneration = AttributeSet.GetMaxStaminaRegeneration(); - - Mana = AttributeSet.GetMana(); - MaxMana = AttributeSet.GetMaxMana(); - ManaRegeneration = AttributeSet.GetManaRegeneration(); - MaxManaRegeneration = AttributeSet.GetMaxManaRegeneration(); - - Focus = AttributeSet.GetFocus(); - MaxFocus = AttributeSet.GetMaxFocus(); - FocusRegeneration = AttributeSet.GetFocusRegeneration(); - MaxFocusRegeneration = AttributeSet.GetMaxFocusRegeneration(); - - // Attack / Ability - AttackDamage = AttributeSet.GetAttackDamage(); - MaxAttackDamage = AttributeSet.GetMaxAttackDamage(); - AbilityPower = AttributeSet.GetAbilityPower(); - MaxAbilityPower = AttributeSet.GetMaxAbilityPower(); - - // Armor / Resistance - Armor = AttributeSet.GetArmor(); - MaxArmor = AttributeSet.GetMaxArmor(); - Resistance = AttributeSet.GetResistance(); - MaxResistance = AttributeSet.GetMaxResistance(); - - // Attack Speed - AttackSpeed = AttributeSet.GetAttackSpeed(); - MaxAttackSpeed = AttributeSet.GetMaxAttackSpeed(); - - // Skill Cooldown - SkillCooldown = AttributeSet.GetSkillCooldown(); - MaxSkillCooldown = AttributeSet.GetMaxSkillCooldown(); - - // Critical - CriticalChance = AttributeSet.GetCriticalChance(); - MaxCriticalChance = AttributeSet.GetMaxCriticalChance(); - CriticalDamage = AttributeSet.GetCriticalDamage(); - MaxCriticalDamage = AttributeSet.GetMaxCriticalDamage(); - - // Movement Speed - MovementSpeed = AttributeSet.GetMovementSpeed(); - MaxMovementSpeed = AttributeSet.GetMaxMovementSpeed(); - - // Damage - Damage = AttributeSet.GetDamage(); - - return *this; -} - -void FMJCharacterAttributeSaveData::ApplyToAttributeSet(UMJCharacterAttributeSet& AttributeSet) const -{ - // Stat | Level - AttributeSet.SetLevel(Level); - AttributeSet.SetMaxLevel(MaxLevel); - AttributeSet.SetExperience(Experience); - AttributeSet.SetMaxExperience(MaxExperience); - AttributeSet.SetDropExperience(DropExperience); - AttributeSet.SetMaxDropExperience(MaxDropExperience); - - // Stat | Resource - AttributeSet.SetHealth(Health); - AttributeSet.SetMaxHealth(MaxHealth); - AttributeSet.SetHealthRegeneration(HealthRegeneration); - AttributeSet.SetMaxHealthRegeneration(MaxHealthRegeneration); - - AttributeSet.SetStamina(Stamina); - AttributeSet.SetMaxStamina(MaxStamina); - AttributeSet.SetStaminaRegeneration(StaminaRegeneration); - AttributeSet.SetMaxStaminaRegeneration(MaxStaminaRegeneration); - - AttributeSet.SetMana(Mana); - AttributeSet.SetMaxMana(MaxMana); - AttributeSet.SetManaRegeneration(ManaRegeneration); - AttributeSet.SetMaxManaRegeneration(MaxManaRegeneration); - - AttributeSet.SetFocus(Focus); - AttributeSet.SetMaxFocus(MaxFocus); - AttributeSet.SetFocusRegeneration(FocusRegeneration); - AttributeSet.SetMaxFocusRegeneration(MaxFocusRegeneration); - - // Attack / Ability - AttributeSet.SetAttackDamage(AttackDamage); - AttributeSet.SetMaxAttackDamage(MaxAttackDamage); - AttributeSet.SetAbilityPower(AbilityPower); - AttributeSet.SetMaxAbilityPower(MaxAbilityPower); - - // Armor / Resistance - AttributeSet.SetArmor(Armor); - AttributeSet.SetMaxArmor(MaxArmor); - AttributeSet.SetResistance(Resistance); - AttributeSet.SetMaxResistance(MaxResistance); - - // Attack Speed - AttributeSet.SetAttackSpeed(AttackSpeed); - AttributeSet.SetMaxAttackSpeed(MaxAttackSpeed); - - // Skill Cooldown - AttributeSet.SetSkillCooldown(SkillCooldown); - AttributeSet.SetMaxSkillCooldown(MaxSkillCooldown); - - // Critical - AttributeSet.SetCriticalChance(CriticalChance); - AttributeSet.SetMaxCriticalChance(MaxCriticalChance); - AttributeSet.SetCriticalDamage(CriticalDamage); - AttributeSet.SetMaxCriticalDamage(MaxCriticalDamage); - - // Movement Speed - AttributeSet.SetMovementSpeed(MovementSpeed); - AttributeSet.SetMaxMovementSpeed(MaxMovementSpeed); - - // Damage - AttributeSet.SetDamage(Damage); -} - diff --git a/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.h b/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.h deleted file mode 100644 index fd0e05d3..00000000 --- a/Source/ProjectMJ/TG/Struct/MJCharacterAttributeSaveDataStruct.h +++ /dev/null @@ -1,153 +0,0 @@ - #pragma once - -#include "CoreMinimal.h" -#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" -#include "MJCharacterAttributeSaveDataStruct.generated.h" - -USTRUCT(BlueprintType) -struct FMJCharacterAttributeSaveData -{ - GENERATED_BODY() - - // TODO 필요한 Attribute 만 담기 - - // Stat | Level - UPROPERTY(BlueprintReadWrite, SaveGame) - float Level; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxLevel; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float Experience; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxExperience; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float DropExperience; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxDropExperience; - - // Stat | Resource - UPROPERTY(BlueprintReadWrite, SaveGame) - float Health; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxHealth; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float HealthRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxHealthRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float Stamina; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxStamina; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float StaminaRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxStaminaRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float Mana; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxMana; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float ManaRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxManaRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float Focus; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxFocus; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float FocusRegeneration; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxFocusRegeneration; - - // Attack / Ability - UPROPERTY(BlueprintReadWrite, SaveGame) - float AttackDamage; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxAttackDamage; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float AbilityPower; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxAbilityPower; - - // Armor / Resistance - UPROPERTY(BlueprintReadWrite, SaveGame) - float Armor; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxArmor; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float Resistance; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxResistance; - - // Attack Speed - UPROPERTY(BlueprintReadWrite, SaveGame) - float AttackSpeed; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxAttackSpeed; - - // Skill Cooldown - UPROPERTY(BlueprintReadWrite, SaveGame) - float SkillCooldown; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxSkillCooldown; - - // Critical - UPROPERTY(BlueprintReadWrite, SaveGame) - float CriticalChance; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCriticalChance; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float CriticalDamage; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCriticalDamage; - - // MovementSpeed - UPROPERTY(BlueprintReadWrite, SaveGame) - float MovementSpeed; - - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxMovementSpeed; - - // Damage - UPROPERTY(BlueprintReadWrite, SaveGame) - float Damage; - - // AttributeSet으로부터 값을 받아 저장 - FMJCharacterAttributeSaveData& operator=(const UMJCharacterAttributeSet& AttributeSet); - - // AttributeSet에 값을 덮어쓰기 - void ApplyToAttributeSet(UMJCharacterAttributeSet& AttributeSet) const; - - -}; \ No newline at end of file diff --git a/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.cpp b/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.cpp deleted file mode 100644 index 5a222991..00000000 --- a/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "MJCharacterSkillAttributeSaveData.h" - -// Operator overload -FMJCharacterSkillAttributeSaveData& FMJCharacterSkillAttributeSaveData::operator=(const UMJCharacterSkillAttributeSet& AttributeSet) -{ - // Level - SkillLevel = AttributeSet.GetSkillLevel(); - MaxSkillLevel = AttributeSet.GetMaxSkillLevel(); - - // Cost - CostStamina = AttributeSet.GetCostStamina(); - MaxCostStamina = AttributeSet.GetMaxCostStamina(); - CostMana = AttributeSet.GetCostMana(); - MaxCostMana = AttributeSet.GetMaxCostMana(); - CostFocus = AttributeSet.GetCostFocus(); - MaxCostFocus = AttributeSet.GetMaxCostFocus(); - - // Damage / Scaling - BaseDamage = AttributeSet.GetBaseDamage(); - MaxBaseDamage = AttributeSet.GetMaxBaseDamage(); - Healing = AttributeSet.GetHealing(); - MaxHealing = AttributeSet.GetMaxHealing(); - LifeSteal = AttributeSet.GetLifeSteal(); - MaxLifeSteal = AttributeSet.GetMaxLifeSteal(); - AttackDamageScaling = AttributeSet.GetAttackDamageScaling(); - MaxAttackDamageScaling = AttributeSet.GetMaxAttackDamageScaling(); - AbilityPowerScaling = AttributeSet.GetAbilityPowerScaling(); - MaxAbilityPowerScaling = AttributeSet.GetMaxAbilityPowerScaling(); - - // Range - SkillRadius = AttributeSet.GetSkillRadius(); - MaxSkillRadius = AttributeSet.GetMaxSkillRadius(); - SkillRange = AttributeSet.GetSkillRange(); - MaxSkillRange = AttributeSet.GetMaxSkillRange(); - - // Time - Cooldown = AttributeSet.GetCooldown(); - MaxCooldown = AttributeSet.GetMaxCooldown(); - SkillAttackRate = AttributeSet.GetSkillAttackRate(); - MaxSkillAttackRate = AttributeSet.GetMaxSkillAttackRate(); - CastTime = AttributeSet.GetCastTime(); - MaxCastTime = AttributeSet.GetMaxCastTime(); - PreDelay = AttributeSet.GetPreDelay(); - MaxPreDelay = AttributeSet.GetMaxPreDelay(); - PostDelay = AttributeSet.GetPostDelay(); - MaxPostDelay = AttributeSet.GetMaxPostDelay(); - EffectDuration = AttributeSet.GetEffectDuration(); - MaxEffectDuration = AttributeSet.GetMaxEffectDuration(); - - // Status effect - StatusEffectChance = AttributeSet.GetStatusEffectChance(); - MaxStatusEffectChance = AttributeSet.GetMaxStatusEffectChance(); - StatusEffectDuration = AttributeSet.GetStatusEffectDuration(); - MaxStatusEffectDuration = AttributeSet.GetMaxStatusEffectDuration(); - - // Projectile - ProjectileSpeed = AttributeSet.GetProjectileSpeed(); - MaxProjectileSpeed = AttributeSet.GetMaxProjectileSpeed(); - ProjectileCount = AttributeSet.GetProjectileCount(); - MaxProjectileCount = AttributeSet.GetMaxProjectileCount(); - - return *this; -} - -void FMJCharacterSkillAttributeSaveData::ApplyToAttributeSet(UMJCharacterSkillAttributeSet& AttributeSet) const -{ - // Level - AttributeSet.SetSkillLevel(SkillLevel); - AttributeSet.SetMaxSkillLevel(MaxSkillLevel); - - // Cost - AttributeSet.SetCostStamina(CostStamina); - AttributeSet.SetMaxCostStamina(MaxCostStamina); - AttributeSet.SetCostMana(CostMana); - AttributeSet.SetMaxCostMana(MaxCostMana); - AttributeSet.SetCostFocus(CostFocus); - AttributeSet.SetMaxCostFocus(MaxCostFocus); - - // Damage / Scaling - AttributeSet.SetBaseDamage(BaseDamage); - AttributeSet.SetMaxBaseDamage(MaxBaseDamage); - AttributeSet.SetHealing(Healing); - AttributeSet.SetMaxHealing(MaxHealing); - AttributeSet.SetLifeSteal(LifeSteal); - AttributeSet.SetMaxLifeSteal(MaxLifeSteal); - AttributeSet.SetAttackDamageScaling(AttackDamageScaling); - AttributeSet.SetMaxAttackDamageScaling(MaxAttackDamageScaling); - AttributeSet.SetAbilityPowerScaling(AbilityPowerScaling); - AttributeSet.SetMaxAbilityPowerScaling(MaxAbilityPowerScaling); - - // Range - AttributeSet.SetSkillRadius(SkillRadius); - AttributeSet.SetMaxSkillRadius(MaxSkillRadius); - AttributeSet.SetSkillRange(SkillRange); - AttributeSet.SetMaxSkillRange(MaxSkillRange); - - // Time - AttributeSet.SetCooldown(Cooldown); - AttributeSet.SetMaxCooldown(MaxCooldown); - AttributeSet.SetSkillAttackRate(SkillAttackRate); - AttributeSet.SetMaxSkillAttackRate(MaxSkillAttackRate); - AttributeSet.SetCastTime(CastTime); - AttributeSet.SetMaxCastTime(MaxCastTime); - AttributeSet.SetPreDelay(PreDelay); - AttributeSet.SetMaxPreDelay(MaxPreDelay); - AttributeSet.SetPostDelay(PostDelay); - AttributeSet.SetMaxPostDelay(MaxPostDelay); - AttributeSet.SetEffectDuration(EffectDuration); - AttributeSet.SetMaxEffectDuration(MaxEffectDuration); - - // Status effect - AttributeSet.SetStatusEffectChance(StatusEffectChance); - AttributeSet.SetMaxStatusEffectChance(MaxStatusEffectChance); - AttributeSet.SetStatusEffectDuration(StatusEffectDuration); - AttributeSet.SetMaxStatusEffectDuration(MaxStatusEffectDuration); - - // Projectile - AttributeSet.SetProjectileSpeed(ProjectileSpeed); - AttributeSet.SetMaxProjectileSpeed(MaxProjectileSpeed); - AttributeSet.SetProjectileCount(ProjectileCount); - AttributeSet.SetMaxProjectileCount(MaxProjectileCount); -} diff --git a/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.h b/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.h deleted file mode 100644 index 6e977874..00000000 --- a/Source/ProjectMJ/TG/Struct/MJCharacterSkillAttributeSaveData.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "AbilitySystem/Attributes/MJCharacterSkillAttributeSet.h" -#include "MJCharacterSkillAttributeSaveData.generated.h" - -USTRUCT(BlueprintType) -struct FMJCharacterSkillAttributeSaveData -{ - GENERATED_BODY() - - // Level - UPROPERTY(BlueprintReadWrite, SaveGame) - float SkillLevel; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxSkillLevel; - - // Cost - UPROPERTY(BlueprintReadWrite, SaveGame) - float CostStamina; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCostStamina; - UPROPERTY(BlueprintReadWrite, SaveGame) - float CostMana; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCostMana; - UPROPERTY(BlueprintReadWrite, SaveGame) - float CostFocus; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCostFocus; - - // Damage / Scaling - UPROPERTY(BlueprintReadWrite, SaveGame) - float BaseDamage; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxBaseDamage; - UPROPERTY(BlueprintReadWrite, SaveGame) - float Healing; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxHealing; - UPROPERTY(BlueprintReadWrite, SaveGame) - float LifeSteal; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxLifeSteal; - UPROPERTY(BlueprintReadWrite, SaveGame) - float AttackDamageScaling; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxAttackDamageScaling; - UPROPERTY(BlueprintReadWrite, SaveGame) - float AbilityPowerScaling; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxAbilityPowerScaling; - - // Range - UPROPERTY(BlueprintReadWrite, SaveGame) - float SkillRadius; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxSkillRadius; - UPROPERTY(BlueprintReadWrite, SaveGame) - float SkillRange; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxSkillRange; - - // Time - UPROPERTY(BlueprintReadWrite, SaveGame) - float Cooldown; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCooldown; - UPROPERTY(BlueprintReadWrite, SaveGame) - float SkillAttackRate; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxSkillAttackRate; - UPROPERTY(BlueprintReadWrite, SaveGame) - float CastTime; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxCastTime; - UPROPERTY(BlueprintReadWrite, SaveGame) - float PreDelay; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxPreDelay; - UPROPERTY(BlueprintReadWrite, SaveGame) - float PostDelay; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxPostDelay; - UPROPERTY(BlueprintReadWrite, SaveGame) - float EffectDuration; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxEffectDuration; - - // Status effect - UPROPERTY(BlueprintReadWrite, SaveGame) - float StatusEffectChance; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxStatusEffectChance; - UPROPERTY(BlueprintReadWrite, SaveGame) - float StatusEffectDuration; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxStatusEffectDuration; - - // Projectile - UPROPERTY(BlueprintReadWrite, SaveGame) - float ProjectileSpeed; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxProjectileSpeed; - UPROPERTY(BlueprintReadWrite, SaveGame) - float ProjectileCount; - UPROPERTY(BlueprintReadWrite, SaveGame) - float MaxProjectileCount; - - // Operator overload - FMJCharacterSkillAttributeSaveData& operator=(const UMJCharacterSkillAttributeSet& AttributeSet); - - // Apply saved data - void ApplyToAttributeSet(UMJCharacterSkillAttributeSet& AttributeSet) const; -}; diff --git a/Source/ProjectMJ/TG/Struct/MJDataTableMapNameTG.h b/Source/ProjectMJ/TG/Struct/MJDataTableMapNameTG.h deleted file mode 100644 index d3c308a6..00000000 --- a/Source/ProjectMJ/TG/Struct/MJDataTableMapNameTG.h +++ /dev/null @@ -1,27 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Engine/DataTable.h" -#include "MJDataTableMapNameTG.generated.h" - -/** - * Class Description: 테스트용 Datatable Row - * Author: 차태관 - * Created Date: 2025-06-15 - * Last Modified By: 차태관 - * Last Modified Date: 2025-06-15 - */ -USTRUCT(BlueprintType) -struct FMJDataTableMapNameTG : public FTableRowBase -{ -public: - GENERATED_BODY() - - - UPROPERTY(EditAnywhere, BlueprintReadWrite) - FString MapName; - - -}; \ No newline at end of file diff --git a/Source/ProjectMJ/TG/Struct/MJDungeonGraphStruct.h b/Source/ProjectMJ/TG/Struct/MJDungeonGraphStruct.h index 5298f30e..18218de4 100644 --- a/Source/ProjectMJ/TG/Struct/MJDungeonGraphStruct.h +++ b/Source/ProjectMJ/TG/Struct/MJDungeonGraphStruct.h @@ -3,16 +3,24 @@ #include "CoreMinimal.h" #include "MJDungeonGraphStruct.generated.h" -UENUM() -enum class ENodeType : uint8 +UENUM(BlueprintType) +enum class EMJNodeType : uint8 { Battle UMETA(DisplayName = "Battle"), Reward UMETA(DisplayName = "Reward"), Boss UMETA(DisplayName = "Boss") }; +UENUM(BlueprintType) +enum class EMJAISpawnType : uint8 +{ + Static UMETA(DisplayName = "Static"), + RandomPoints UMETA(DisplayName = "RandomPoints"), + Wave UMETA(DisplayName = "Wave") +}; + USTRUCT(BlueprintType) -struct FDungeonNode +struct FMJDungeonNode { GENERATED_BODY() @@ -23,7 +31,10 @@ struct FDungeonNode int32 AssignedMapID; UPROPERTY(BlueprintReadOnly) - ENodeType NodeType; + EMJNodeType NodeType; + + UPROPERTY(BlueprintReadOnly) + EMJAISpawnType AISpawnType; UPROPERTY(BlueprintReadOnly) FVector2D UICoordinate; @@ -31,30 +42,45 @@ struct FDungeonNode UPROPERTY(BlueprintReadOnly) TArray ConnectedNodeIDs; - static FString NodeTypeToString(ENodeType Type) + static FString NodeTypeToString(EMJNodeType Type) { switch (Type) { - case ENodeType::Battle: + case EMJNodeType::Battle: return TEXT("Battle"); - case ENodeType::Reward: + case EMJNodeType::Reward: return TEXT("Reward"); - case ENodeType::Boss: + case EMJNodeType::Boss: return TEXT("Boss"); default: return TEXT("Unknown"); } } + + static FString AISpawnTypeToString(EMJAISpawnType Type) + { + switch (Type) + { + case EMJAISpawnType::Static: + return TEXT("Static"); + case EMJAISpawnType::Wave: + return TEXT("Wave"); + case EMJAISpawnType::RandomPoints: + return TEXT("RandomPoints"); + default: + return TEXT("Unknown"); + } + } }; USTRUCT(BlueprintType) -struct FDungeonGraph +struct FMJDungeonGraph { GENERATED_BODY() UPROPERTY(BlueprintReadOnly) - TArray Nodes; + TArray Nodes; UPROPERTY(BlueprintReadOnly) TArray BezierPoints; diff --git a/Source/ProjectMJ/TG/Struct/MJDungeonSessionDataStruct.h b/Source/ProjectMJ/TG/Struct/MJDungeonSessionDataStruct.h index 1e0a4c75..f7bf7883 100644 --- a/Source/ProjectMJ/TG/Struct/MJDungeonSessionDataStruct.h +++ b/Source/ProjectMJ/TG/Struct/MJDungeonSessionDataStruct.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "MJDungeonGraphStruct.h" #include "MJDungeonSessionDataStruct.generated.h" UENUM(BlueprintType) @@ -29,15 +30,17 @@ struct FMJDungeonSessionData { GENERATED_BODY() - FMJDungeonSessionData(EMJDungeonContext InContext, uint8 InNodeNum, const FString& InMapName) + FMJDungeonSessionData(EMJDungeonContext InContext, uint8 InNodeNum, EMJAISpawnType InAISpawnType, const FString& InMapName) : DungeonContext(InContext) , DungeonNodeNum(InNodeNum) + , AISpawnType(InAISpawnType) , MapName(InMapName) {} FMJDungeonSessionData() : DungeonContext(EMJDungeonContext::InActive) , DungeonNodeNum(0) + , AISpawnType(EMJAISpawnType::Static) , MapName(TEXT("")) {} @@ -45,10 +48,13 @@ struct FMJDungeonSessionData EMJDungeonContext DungeonContext = EMJDungeonContext::InActive; UPROPERTY(BlueprintReadOnly) - uint8 DungeonNodeNum; + uint8 DungeonNodeNum = 0; UPROPERTY(BlueprintReadOnly) - FString MapName; + EMJAISpawnType AISpawnType = EMJAISpawnType::Static; + + UPROPERTY(BlueprintReadOnly) + FString MapName = TEXT(""); UPROPERTY(BlueprintReadOnly) TArray SpawnInfos; diff --git a/Source/ProjectMJ/TG/Struct/MJPlayerSessionDataStruct.h b/Source/ProjectMJ/TG/Struct/MJPlayerSessionDataStruct.h index 7a22da6b..3385d9dd 100644 --- a/Source/ProjectMJ/TG/Struct/MJPlayerSessionDataStruct.h +++ b/Source/ProjectMJ/TG/Struct/MJPlayerSessionDataStruct.h @@ -1,10 +1,10 @@ #pragma once #include "CoreMinimal.h" -#include "MJCharacterAttributeSaveDataStruct.h" -#include "MJCharacterSkillAttributeSaveData.h" +#include "Character/Component/MJSkillComponentBase.h" #include "MJPlayerSessionDataStruct.generated.h" + UENUM(BlueprintType) enum class EMJGameplayContext : uint8 { @@ -19,16 +19,47 @@ struct FMJPlayerSessionData { GENERATED_BODY() - UPROPERTY(BlueprintReadOnly) - EMJGameplayContext GameplayContext; + UPROPERTY() + FString PlayerName; + + UPROPERTY() + int32 PlayerLevel = 1; - UPROPERTY(BlueprintReadOnly) - FMJCharacterAttributeSaveData CharacterAttribute; + UPROPERTY() + float PlayerExp = 0.0f; + + UPROPERTY() + int8 SaveGameSlotNum = -1; UPROPERTY(BlueprintReadOnly) - FMJCharacterSkillAttributeSaveData CharacterSkillAttribute; + EMJGameplayContext GameplayContext; UPROPERTY(BlueprintReadOnly) uint8 CurrentDungeonMapNum; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap CurrentOwnedSkillMap; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap CurrentEquippedSkillMap; + + + void SetCurrentOwnedSKillMap(TMap Input) + { + CurrentOwnedSkillMap.Empty(); + for (auto Iter : Input) + { + CurrentOwnedSkillMap.Add(Iter); + } + } + + void SetCurrentEquippedSkillMap(TMap Input) + { + CurrentEquippedSkillMap.Empty(); + for (auto Iter : Input) + { + CurrentEquippedSkillMap.Add(Iter); + } + } }; diff --git a/Source/ProjectMJ/TG/Struct/MJSaveGame.cpp b/Source/ProjectMJ/TG/Struct/MJSaveGame.cpp index f723211a..6cde1022 100644 --- a/Source/ProjectMJ/TG/Struct/MJSaveGame.cpp +++ b/Source/ProjectMJ/TG/Struct/MJSaveGame.cpp @@ -3,14 +3,8 @@ #include "MJSaveGame.h" -#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" UMJSaveGame::UMJSaveGame() { } - -FMJCharacterAttributeSaveData& UMJSaveGame::GetAttributeSaveData() -{ - return AttributeSaveData; -} diff --git a/Source/ProjectMJ/TG/Struct/MJSaveGame.h b/Source/ProjectMJ/TG/Struct/MJSaveGame.h index 21e1b8ea..e483d0ad 100644 --- a/Source/ProjectMJ/TG/Struct/MJSaveGame.h +++ b/Source/ProjectMJ/TG/Struct/MJSaveGame.h @@ -3,12 +3,10 @@ #pragma once #include "CoreMinimal.h" -#include "MJCharacterAttributeSaveDataStruct.h" #include "GameFramework/SaveGame.h" +#include "Character/Component/MJSkillComponentBase.h" #include "MJSaveGame.generated.h" -struct FGameplayAttributeData; -class UMJCharacterAttributeSet; /** * Class Description: 게임을 저장할 데이터들을 모아놓은 USaveGame 클래스 * Author: 차태관 @@ -20,20 +18,49 @@ UCLASS() class PROJECTMJ_API UMJSaveGame : public USaveGame { GENERATED_BODY() + public: UMJSaveGame(); - - FMJCharacterAttributeSaveData& GetAttributeSaveData(); - -protected: + UPROPERTY(SaveGame) + int8 SlotNum = -1; UPROPERTY(BlueprintReadOnly, SaveGame) FString PlayerName; + + UPROPERTY(BlueprintReadOnly, SaveGame) + int32 PlayerLevel = 1; UPROPERTY(BlueprintReadOnly, SaveGame) - FMJCharacterAttributeSaveData AttributeSaveData; + int32 PlayerExp; + + UPROPERTY(BlueprintReadOnly, SaveGame) + FDateTime RecentPlayedDateTime; + UPROPERTY(BlueprintReadOnly, SaveGame) + FDateTime SaveGameCreatedDateTime; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap CurrentOwnedSkillMap; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") + TMap CurrentEquippedSkillMap; + void SetCurrentOwnedSKillMap(TMap Input) + { + CurrentOwnedSkillMap.Empty(); + for (auto Iter : Input) + { + CurrentOwnedSkillMap.Add(Iter); + } + } + void SetCurrentEquippedSkillMap(TMap Input) + { + CurrentEquippedSkillMap.Empty(); + for (auto Iter : Input) + { + CurrentEquippedSkillMap.Add(Iter); + } + } }; diff --git a/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.cpp b/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.cpp index 5fbfb79d..2bdb12a0 100644 --- a/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.cpp +++ b/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.cpp @@ -3,8 +3,7 @@ #include "MJDungeonGenerationSubSystem.h" #include -#include "ProjectMJ.h" -#include "TG/MJGameInstanceTG.h" +#include "TG/MJGameInstance.h" UMJDungeonGenerationSubSystem::UMJDungeonGenerationSubSystem() @@ -15,14 +14,24 @@ UMJDungeonGenerationSubSystem::UMJDungeonGenerationSubSystem() void UMJDungeonGenerationSubSystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); - GenerateDungeonGraph(); + + // do + // { + // GenerateDungeonGraph(); + // } + // while (!CheckHasIterableGraph()); + } -bool UMJDungeonGenerationSubSystem::GenerateDungeonGraph() +void UMJDungeonGenerationSubSystem::GenerateDungeonGraph() { - UMJGameInstanceTG* MJGI = Cast(GetGameInstance()); - + UMJGameInstance* MJGI = Cast(GetGameInstance()); + check(MJGI); + + // Initialize Data + DungeonGraph = FMJDungeonGraph(); + MJGI->GetDungeonSessionDataRef().Empty(); FVector2D StartPoint; FVector2D EndPoint; @@ -81,35 +90,46 @@ bool UMJDungeonGenerationSubSystem::GenerateDungeonGraph() { // Boss - FDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,ENodeType::Boss, CandidatePoint); + FMJDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,EMJNodeType::Boss, EMJAISpawnType::Static, CandidatePoint); DungeonGraph.Nodes.Add(NewNode); DungeonGraph.BossNodeID = CurrentNotAssignedNodeNum; - MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum,FString(TEXT("Boss")))); + MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum, EMJAISpawnType::Static, FString(TEXT("Boss")))); CurrentNotAssignedNodeNum++; } else if (Prob > 0.0f && Prob < 80.f) { - // Battle 1~5 Map Assign + // Battle 1~5 Map Assign + // AISpawn Wave Map : 01 02 + // AISpawn Static Map : 03 04 05 float RandNum = FMath::RandRange(1,5); + + EMJAISpawnType AISpawnType = EMJAISpawnType::Static; + + // Wave + if (RandNum == 1 || RandNum == 2) + { + AISpawnType = EMJAISpawnType::Wave; + + } - FDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,RandNum,ENodeType::Battle, CandidatePoint); + FMJDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,RandNum,EMJNodeType::Battle, AISpawnType, CandidatePoint); DungeonGraph.Nodes.Add(NewNode); - MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum,FString(TEXT("Battle")))); + MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum, AISpawnType, FString(TEXT("Battle")))); CurrentNotAssignedNodeNum++; } else if (Prob >= 80.f && Prob < 90.f) { // Reward - FDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,ENodeType::Reward, CandidatePoint); + FMJDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,EMJNodeType::Reward, EMJAISpawnType::Static, CandidatePoint); DungeonGraph.Nodes.Add(NewNode); - MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum,FString(TEXT("Reward")))); + MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum, EMJAISpawnType::Static, FString(TEXT("Reward")))); CurrentNotAssignedNodeNum++; } @@ -120,7 +140,7 @@ bool UMJDungeonGenerationSubSystem::GenerateDungeonGraph() for (auto iter : DungeonGraph.Nodes) { - if (iter.NodeType == ENodeType::Boss) + if (iter.NodeType == EMJNodeType::Boss) { HasBossRoom = true; break; @@ -132,38 +152,25 @@ bool UMJDungeonGenerationSubSystem::GenerateDungeonGraph() continue; } - FDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,ENodeType::Boss, CandidatePoint); + FMJDungeonNode NewNode = MakeNewNode(CurrentNotAssignedNodeNum,0,EMJNodeType::Boss, EMJAISpawnType::Static, CandidatePoint); DungeonGraph.Nodes.Add(NewNode); DungeonGraph.BossNodeID = CurrentNotAssignedNodeNum; - MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum,FString(TEXT("Boss")))); + MJGI->GetDungeonSessionDataRef().Add(FMJDungeonSessionData(EMJDungeonContext::InActive, CurrentNotAssignedNodeNum, EMJAISpawnType::Static, FString(TEXT("Boss")))); CurrentNotAssignedNodeNum++; } - } + } + - // @fixme : maybe cause infinite loop ? - // temporaily, start at random node - // uint8 tmp; - // while (true) - // { - // tmp = FMath::RandRange(0,CurrentNotAssignedNodeNum - 1); - // - // if (DungeonGraph.Nodes[tmp].NodeType == ENodeType::Battle) - // { - // DungeonGraph.StartNodeID = tmp; - // SavedMapNodeNum = tmp; - // break; - // } - // } // Choose most far node from BossNode float MaxDist = FLT_MIN; uint8 MaxDistNodeNum = -1; for (auto& iter : DungeonGraph.Nodes) { - if (iter.NodeType != ENodeType::Battle) + if (iter.NodeType != EMJNodeType::Battle) continue; if (MaxDist < FVector2D::Distance(DungeonGraph.Nodes[iter.NodeID].UICoordinate, DungeonGraph.Nodes[DungeonGraph.BossNodeID].UICoordinate)) @@ -178,71 +185,33 @@ bool UMJDungeonGenerationSubSystem::GenerateDungeonGraph() if (MJGI) { + MJGI->GetPlayerSessionDataRef().CurrentDungeonMapNum = DungeonGraph.StartNodeID; } -#pragma region Debug - - // for (int i = 0 ; i < CurrentNotAssignedNodeNum ; i++) - // { - // MJ_LOG(LogTG, Warning, TEXT("Node Num = %d, Node Assigned Num = %d, NodeType = %s"), DungeonGraph.Nodes[i].NodeID,DungeonGraph.Nodes[i].AssignedMapID, *FDungeonNode::NodeTypeToString(DungeonGraph.Nodes[i].NodeType)); - // } - // - // - // for (int i = 0 ; i < CurrentNotAssignedNodeNum ; i++) - // { - // const FDungeonNode& Node = DungeonGraph.Nodes[i]; - // bool bOutOfBounds = - // Node.UICoordinate.X < 0.f || Node.UICoordinate.X > ViewportSize.X || - // Node.UICoordinate.Y < 0.f || Node.UICoordinate.Y > ViewportSize.Y; - // - // if (bOutOfBounds) - // { - // MJ_LOG(LogTG, Error, TEXT(" OUT OF BOUNDS → Node ID: %d | Pos: X=%.1f, Y=%.1f (Viewport: %.1f x %.1f)"), - // Node.NodeID, - // Node.UICoordinate.X, - // Node.UICoordinate.Y, - // ViewportSize.X, - // ViewportSize.Y); - // } - // else - // { - // MJ_LOG(LogTG, Log, TEXT("Node ID: %d | Pos: X=%.1f, Y=%.1f"), - // Node.NodeID, - // Node.UICoordinate.X, - // Node.UICoordinate.Y); - // } - // } - // for (int i = 0 ; i < CurrentNotAssignedNodeNum ; i++) - // { - // const FDungeonNode& Node = DungeonGraph.Nodes[i]; - // - // MJ_LOG(LogTG, Warning, TEXT("Node ID: %d | Type: %s | Pos: X=%.1f, Y=%.1f"), - // Node.NodeID, - // *FDungeonNode::NodeTypeToString(Node.NodeType), - // Node.UICoordinate.X, - // Node.UICoordinate.Y); - // } -#pragma endregion - ConnectNodesByMST(FVector2D::Distance(FVector2D(0.0f,0.0f), ViewportSize)); ConnectNodesByDistance(400.f,4); - - return true; + + if (!CheckHasIterableGraph()) + { + return GenerateDungeonGraph(); + } } -FDungeonNode UMJDungeonGenerationSubSystem::MakeNewNode(uint8 NodeNum, uint8 AssignedMapID, ENodeType NodeType, FVector2D UICoordinate) + +FMJDungeonNode UMJDungeonGenerationSubSystem::MakeNewNode(uint8 NodeNum, uint8 AssignedMapID, EMJNodeType NodeType,EMJAISpawnType AISpawnType, FVector2D UICoordinate) { - FDungeonNode BuffNode; + FMJDungeonNode NewNode; - BuffNode.NodeID = NodeNum; - BuffNode.AssignedMapID = AssignedMapID; - BuffNode.NodeType = NodeType; - BuffNode.UICoordinate = UICoordinate; + NewNode.NodeID = NodeNum; + NewNode.AssignedMapID = AssignedMapID; + NewNode.NodeType = NodeType; + NewNode.AISpawnType = AISpawnType; + NewNode.UICoordinate = UICoordinate; - return BuffNode; + return NewNode; } void UMJDungeonGenerationSubSystem::ConnectNodesByDistance(float MaxDistance, int MaxEdgePerNode) @@ -251,7 +220,7 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByDistance(float MaxDistance, in for (int i = 0 ; i < NodeCount ; i++) { - FDungeonNode& CurrentNode = DungeonGraph.Nodes[i]; + FMJDungeonNode& CurrentNode = DungeonGraph.Nodes[i]; TArray> CandidateNodes; @@ -259,7 +228,7 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByDistance(float MaxDistance, in { if (i == j) continue; - FDungeonNode& IterNode = DungeonGraph.Nodes[j]; + FMJDungeonNode& IterNode = DungeonGraph.Nodes[j]; float DistanceBetweenNodes = FVector2D::Distance(CurrentNode.UICoordinate, IterNode.UICoordinate); @@ -289,21 +258,6 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByDistance(float MaxDistance, in } } } - - // for debug - // for (const FDungeonNode& Node : DungeonGraph.Nodes) - // { - // FString ConnectedStr; - // for (int32 ID : Node.ConnectedNodeIDs) - // { - // ConnectedStr += FString::Printf(TEXT("%d "), ID); - // } - // - // MJ_LOG(LogTG, Warning, TEXT("Node %d (%s) → [%s]"), - // Node.NodeID, - // *FDungeonNode::NodeTypeToString(Node.NodeType), - // *ConnectedStr); - // } } @@ -313,7 +267,6 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByMST(float MaxDistance) if ( NodeCount <= 1 ) return; - // make tuple for candidateEdges TArray> AllCandidateEdges; for (int i = 0 ; i< NodeCount ; i++) { @@ -327,13 +280,11 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByMST(float MaxDistance) } } - // 2. sort by ascend AllCandidateEdges.Sort([](const TTuple& A, const TTuple& B) { return A.Get<2>() < B.Get<2>(); }); - // 3. create Parent Array for union find & lamda functions TArray Parent; Parent.Init(-1,NodeCount); @@ -354,7 +305,6 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByMST(float MaxDistance) return true; }; - // 4. choose edge based on union find for (const auto& iter : AllCandidateEdges) { uint8 NodeA = iter.Get<0>(); @@ -366,7 +316,43 @@ void UMJDungeonGenerationSubSystem::ConnectNodesByMST(float MaxDistance) DungeonGraph.Nodes[NodeB].ConnectedNodeIDs.Add(NodeA); } } - // 5. All the nodes and edges are visualized in UMG Widget +} + +bool UMJDungeonGenerationSubSystem::CheckHasIterableGraph() +{ + const uint8 NodeCount = DungeonGraph.Nodes.Num(); + const uint8 BossID = DungeonGraph.BossNodeID; + const uint8 StartID = DungeonGraph.StartNodeID; + + TArray Visited; + Visited.Init(false, NodeCount); + + DFS(StartID, BossID, /* OutArray */Visited); + + for (uint8 NodeID = 0; NodeID < NodeCount; ++NodeID) + { + if (NodeID == BossID) + continue; + + if (!Visited[NodeID]) + return false; + } + + return true; +} + +void UMJDungeonGenerationSubSystem::DFS(uint8 CurrentNode, const uint8 BossID, TArray& Visited) +{ + Visited[CurrentNode] = true; + + for (uint8 Next : DungeonGraph.Nodes[CurrentNode].ConnectedNodeIDs) + { + if (Next == BossID) + continue; + + if (!Visited[Next]) + DFS(Next, BossID, Visited); + } } bool UMJDungeonGenerationSubSystem::CheckHasRoute(uint8 CurrentNodeNum, uint8 DestNodeNum) diff --git a/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.h b/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.h index 4305ae0f..637dbd57 100644 --- a/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.h +++ b/Source/ProjectMJ/TG/SubSystem/MJDungeonGenerationSubSystem.h @@ -17,54 +17,75 @@ UCLASS() class PROJECTMJ_API UMJDungeonGenerationSubSystem : public UGameInstanceSubsystem { - GENERATED_BODY() + GENERATED_BODY() public: - - UMJDungeonGenerationSubSystem(); - - virtual void Initialize(FSubsystemCollectionBase& Collection) override; - - UFUNCTION(BlueprintCallable) - bool GenerateDungeonGraph(); - - UFUNCTION(BlueprintCallable) - const FDungeonGraph& GetDungeonGraph() const { return DungeonGraph; } - - UFUNCTION(BlueprintCallable, Category="Dungeon") - uint8 GetMaxNodeNum() const; - - UFUNCTION(BlueprintCallable, Category="Dungeon") - void SetMaxNodeNum(uint8 NewMaxNodeNum); + + UMJDungeonGenerationSubSystem(); + + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + + + UFUNCTION(BlueprintCallable) + void GenerateDungeonGraph(); + + UFUNCTION(BlueprintCallable) + bool CheckHasRoute(uint8 CurrentNodeNum, uint8 DestNodeNum); + + UFUNCTION(BlueprintCallable) + void GetDungeonGraphOut(FMJDungeonGraph& OutGraph) const { OutGraph = DungeonGraph;} + + FMJDungeonGraph* GetDungeonGraph() { return &DungeonGraph; } + + UFUNCTION(BlueprintCallable, Category="Dungeon") + uint8 GetMaxNodeNum() const; + + UFUNCTION(BlueprintCallable, Category="Dungeon") + void SetMaxNodeNum(uint8 NewMaxNodeNum); protected: - - // GeneratingDungeonSystem Section - - UPROPERTY(BlueprintReadOnly) - FDungeonGraph DungeonGraph; - - UPROPERTY(BlueprintReadOnly) - uint8 MaxNodeNum; - - UFUNCTION() - FDungeonNode MakeNewNode(uint8 NodeNum, uint8 AssignedMapID, ENodeType NodeType, FVector2D UICoordinate); - - UFUNCTION() - void ConnectNodesByDistance(float MaxDistance, int MaxEdgePerNode); - - UFUNCTION() - void ConnectNodesByMST(float MaxDistance); - - UFUNCTION(BlueprintCallable) - bool CheckHasRoute(uint8 CurrentNodeNum, uint8 DestNodeNum); - - UFUNCTION() - FVector2D GetCubicBezier(float t, const FVector2D Point); - - UFUNCTION() - FVector2D GetQuadBezier(float t, const FVector2D StartPoint, const FVector2D EndPoint, const FVector2D ControlPoint); - - - + + // GeneratingDungeonSystem Section + + UPROPERTY(BlueprintReadOnly) + FMJDungeonGraph DungeonGraph; + + UPROPERTY(BlueprintReadOnly) + uint8 MaxNodeNum; + + + + + UFUNCTION() + FMJDungeonNode MakeNewNode(uint8 NodeNum, uint8 AssignedMapID, EMJNodeType NodeType, EMJAISpawnType AISpawnType, FVector2D UICoordinate); + + + + UFUNCTION() + void ConnectNodesByDistance(float MaxDistance, int MaxEdgePerNode); + + UFUNCTION() + void ConnectNodesByMST(float MaxDistance); + + UFUNCTION() + bool CheckHasIterableGraph(); + + // DFS Section + + UFUNCTION() + void DFS(uint8 CurrentNode, const uint8 BossID, TArray& Visited); + + + + + + + UFUNCTION() + FVector2D GetCubicBezier(float t, const FVector2D Point); + + UFUNCTION() + FVector2D GetQuadBezier(float t, const FVector2D StartPoint, const FVector2D EndPoint, const FVector2D ControlPoint); + + + }; diff --git a/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.cpp b/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.cpp deleted file mode 100644 index 3ffd6212..00000000 --- a/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "MJHttpDownloadManager.h" -#include "HttpModule.h" -#include "ProjectMJ.h" -#include "Interfaces/IHttpResponse.h" - -UMJHttpDownloadManager::UMJHttpDownloadManager() -{ - -} - -void UMJHttpDownloadManager::FetchGoogleSheetData() -{ - // HTTP Module 가져오기 - FHttpModule* Http = &FHttpModule::Get(); - - // HTTP Request 생성 - TSharedRef Request = Http->CreateRequest(); - - // Google Sheets API URL 설정 - FString GoogleSheetID = TEXT("1WDXClQfgWQ1tyuY-ieqs5fxcnd_F1sm9Qm7FHW-FbNo"); - FString GoogleSheetRange = TEXT("Data"); // 시트 이름 & 범위 - FString ApiKey = TEXT("AIzaSyD2yl3uaq1kzT7YM6hfna62I_Fuw5KLsf8"); - FString Url = FString::Printf(TEXT("https://sheets.googleapis.com/v4/spreadsheets/%s/values/%s?key=%s"), *GoogleSheetID, *GoogleSheetRange, *ApiKey); - - Request->SetURL(Url); - Request->SetVerb("GET"); - Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - - // Request 완료 시 콜백 설정 - Request->OnProcessRequestComplete().BindUObject(this, &UMJHttpDownloadManager::OnResponseReceived); - - // Request 실행 - Request->ProcessRequest(); - - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager Fetching Google Sheet Data")); - -} - -void UMJHttpDownloadManager::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) -{ - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager OnResponseReceived start")); - - if (bWasSuccessful && Response.IsValid()) - { - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager OnResponseReceived valid")); - - FString ResponseString = Response->GetContentAsString(); - MJ_LOG(LogTemp, Warning, TEXT("==== Raw JSON START ====")); - for (int32 i = 0; i < ResponseString.Len(); i += 512) - { - FString Chunk = ResponseString.Mid(i, 512); - MJ_LOG(LogTemp, Warning, TEXT("%s"), *Chunk); - } - MJ_LOG(LogTemp, Warning, TEXT("==== Raw JSON END ====")); - - - - // JSON 파싱 - TSharedPtr JsonObject; - TSharedRef> Reader = TJsonReaderFactory<>::Create(ResponseString); - - if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid()) - { - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager OnResponseReceived deserialized")); - const TArray>* Rows; - - if (JsonObject->TryGetArrayField(TEXT("values"), Rows)) - { - - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager getarrayfield")); - - for (const TSharedPtr& RowValue : *Rows) - { - const TArray>& Row = RowValue->AsArray(); - if (Row.Num() == 2) - { - FString Key = Row[0]->AsString(); - float Value = FCString::Atof(*Row[1]->AsString()); - - DataMap.Add(Key, Value); - - MJ_LOG(LogTGJSON, Log, TEXT("UMJHttpDownloadManager DataMap added: %s - %f"), *Key, Value); - } - } - - // 데이터 로그 출력 - for (const TPair& Pair : DataMap) - { - MJ_LOG(LogTGJSON, Log, TEXT("%s: %f"), *Pair.Key, Pair.Value); - } - - OnDataFetched.Broadcast(); - } - } - else - { - MJ_LOG(LogTGJSON, Error, TEXT("Failed to deserialize JSON response")); - } - } - else - { - MJ_LOG(LogTGJSON, Error, TEXT("Failed to fetch Google Sheet data")); - } - -} diff --git a/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.h b/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.h deleted file mode 100644 index e9728ae1..00000000 --- a/Source/ProjectMJ/TG/SubSystem/MJHttpDownloadManager.h +++ /dev/null @@ -1,43 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Interfaces/IHttpRequest.h" -#include "MJHttpDownloadManager.generated.h" - -/** - * Class Description: 구글 시트 파싱해서 JSON 으로 가져오는 HttpDownloader - * Author: 차태관 - * Created Date: 25-06-15 - * Last Modified By: 차태관 - * Last Modified Date: 25-06-15 - */ -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDataFetched); - -UCLASS() -class PROJECTMJ_API UMJHttpDownloadManager : public UObject -{ - GENERATED_BODY() - - -public: - - UMJHttpDownloadManager(); - - UFUNCTION(BlueprintCallable, Category = HTTP) - void FetchGoogleSheetData(); - - - UPROPERTY() - TMap DataMap; - - FOnDataFetched OnDataFetched; - -protected: - - void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); - - - -}; \ No newline at end of file diff --git a/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.cpp b/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.cpp index 50095274..14a43a23 100644 --- a/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.cpp +++ b/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.cpp @@ -4,140 +4,189 @@ #include "MJSaveGameSubsystem.h" #include "MoviePlayer.h" -#include "ProjectMJ.h" -#include "AbilitySystem/MJAbilitySystemComponent.h" -#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" #include "Blueprint/UserWidget.h" -#include "Character/MJPlayerCharacter.h" #include "Kismet/GameplayStatics.h" +#include "TG/MJGameInstance.h" #include "TG/Struct/MJSaveGame.h" UMJSaveGameSubsystem::UMJSaveGameSubsystem() { - static ConstructorHelpers::FClassFinder LoadingScreenRef(TEXT("/Game/TG/WBP_LoadingScreen.WBP_LoadingScreen_C")); + static ConstructorHelpers::FClassFinder LoadingScreenRef(TEXT("/Game/TG/TestWBP/WBP_LoadingScreen.WBP_LoadingScreen_C")); if (LoadingScreenRef.Succeeded()) { LoadingScreen = LoadingScreenRef.Class; } + MaxSaveSlotNum = 8; } void UMJSaveGameSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); - FCoreUObjectDelegates::PreLoadMap.AddUObject(this, &UMJSaveGameSubsystem::BeginLoadingScreen); FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &UMJSaveGameSubsystem::EndLoadingScreen); +} +void UMJSaveGameSubsystem::Deinitialize() +{ + Super::Deinitialize(); + + //SaveGameToCurrentSlotNum(); } -void UMJSaveGameSubsystem::CreateSaveGame() +void UMJSaveGameSubsystem::BeginLoadingScreen(const FString& MapName) { - SaveGameData = CastChecked(UGameplayStatics::CreateSaveGameObject(UMJSaveGame::StaticClass())); - if (SaveGameData) - { - //SaveGameToSlot(nullptr); - } + + FLoadingScreenAttributes LoadingScreenAttr; + + UUserWidget* Buffer = CreateWidget(GetGameInstance(), LoadingScreen); + + LoadingScreenAttr.WidgetLoadingScreen = Buffer->TakeWidget(); + LoadingScreenAttr.bAutoCompleteWhenLoadingCompletes = false; + LoadingScreenAttr.MinimumLoadingScreenDisplayTime = 2.0f; + + GetMoviePlayer()->SetupLoadingScreen(LoadingScreenAttr); } -void UMJSaveGameSubsystem::LoadSaveGame(AMJPlayerCharacter* Player) +void UMJSaveGameSubsystem::EndLoadingScreen(UWorld* InLoadedWorld) { - if (!Player) return; - if (!UGameplayStatics::DoesSaveGameExist(SaveSlotName, UserIndex)) - { - MJ_LOG(LogTG, Log, TEXT("No save found. Saving current character state as default.")); +} - SaveGameData = CastChecked(UGameplayStatics::CreateSaveGameObject(UMJSaveGame::StaticClass())); - SaveGameToSlot(Player); // Save as Default - return; - } - - SaveGameData = Cast(UGameplayStatics::LoadGameFromSlot(SaveSlotName, UserIndex)); - if (SaveGameData) +UMJSaveGame* UMJSaveGameSubsystem::GetSaveGameData() +{ + return SaveGameData; +} + +void UMJSaveGameSubsystem::SaveGameToCurrentSlotNum() +{ + + UMJGameInstance* MJGI = Cast(GetGameInstance()); + if (MJGI) { - if (Player) + const uint8 SlotNum = MJGI->GetPlayerSessionDataRef().SaveGameSlotNum; + + if (MJGI->GetPlayerSessionDataRef().SaveGameSlotNum == INDEX_NONE) { - UMJAbilitySystemComponent* MJASC = Cast(Player->GetAbilitySystemComponent()); - if (MJASC) - { - UMJCharacterAttributeSet* MJCAS = const_cast(MJASC->GetSet()); - if (MJCAS) - { - //SaveGameData->GetAttributeSaveData().ApplyTo(MJASC); - } - } - - MJ_LOG(LogTG,Log,TEXT("loaded health : %f"), MJASC->GetNumericAttribute(UMJCharacterAttributeSet::GetHealthAttribute())); + return; + } + + const FString SlotName = FString::Printf(TEXT("Slot_%d"), SlotNum); + + UMJSaveGame* SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); + + if (!SaveGame) + { + SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UMJSaveGame::StaticClass())); + SaveGame->SlotNum = SlotNum; } + + SaveGame->PlayerName = MJGI->GetPlayerSessionDataRef().PlayerName; + SaveGame->PlayerLevel = MJGI->GetPlayerSessionDataRef().PlayerLevel; + SaveGame->PlayerExp = MJGI->GetPlayerSessionDataRef().PlayerExp; + SaveGame->SlotNum = MJGI->GetPlayerSessionDataRef().SaveGameSlotNum; + SaveGame->RecentPlayedDateTime = FDateTime::Now(); + + SaveGame->SetCurrentEquippedSkillMap(MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap); + SaveGame->SetCurrentOwnedSKillMap(MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap); + + + UGameplayStatics::SaveGameToSlot(SaveGame, SlotName, 0); } + + } -void UMJSaveGameSubsystem::SaveGameToSlot(AMJPlayerCharacter* Player) +void UMJSaveGameSubsystem::SaveGameToSelectedSlotNum(const uint8 InputSlotNum) { - if (SaveGameData) + // using only in SavePointActor + UMJGameInstance* MJGI = Cast(GetGameInstance()); + if (MJGI) { - if (Player) - { - UMJAbilitySystemComponent* MJASC = Cast(Player->GetAbilitySystemComponent()); + const FString SlotName = FString::Printf(TEXT("Slot_%d"), InputSlotNum); - if (MJASC) - { - - UMJCharacterAttributeSet* MJCAS = const_cast(MJASC->GetSet()); - if (MJCAS) - { - //SaveGameData->GetAttributeSaveData() = FCharacterAttributeSaveData::FromAttributeSet(MJCAS); - } - } - - MJ_LOG(LogTG,Log,TEXT("saved health : %f"), SaveGameData->GetAttributeSaveData().Health); + UMJSaveGame* SaveGame; + + if (UGameplayStatics::DoesSaveGameExist(SlotName,0)) + { + SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); } else { - bool bSaved = false; - MJ_LOG(LogTG, Log, TEXT("Character Info Save success: %s"), bSaved ? TEXT("true") : TEXT("false")); + SaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UMJSaveGame::StaticClass())); } - - bool bSaved = UGameplayStatics::SaveGameToSlot(SaveGameData, SaveSlotName, UserIndex); - MJ_LOG(LogTG, Log, TEXT("Save success: %s"), bSaved ? TEXT("true") : TEXT("false")); - + + if (SaveGame) + { + SaveGame->PlayerName = MJGI->GetPlayerSessionDataRef().PlayerName; + SaveGame->PlayerLevel = MJGI->GetPlayerSessionDataRef().PlayerLevel; + SaveGame->PlayerExp = MJGI->GetPlayerSessionDataRef().PlayerExp; + SaveGame->SlotNum = InputSlotNum; + SaveGame->RecentPlayedDateTime = FDateTime::Now(); + SaveGame->SaveGameCreatedDateTime = FDateTime::Now(); + + SaveGame->SetCurrentEquippedSkillMap(MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap); + SaveGame->SetCurrentOwnedSKillMap(MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap); + } + + UGameplayStatics::SaveGameToSlot(SaveGame, SlotName, 0); } } - -void UMJSaveGameSubsystem::BeginLoadingScreen(const FString& MapName) +bool UMJSaveGameSubsystem::LoadGameFromSlotNum(int8 SlotNum) { - FLoadingScreenAttributes LoadingScreenAttr; - - UUserWidget* Buffer = CreateWidget(GetGameInstance(), LoadingScreen); - - LoadingScreenAttr.WidgetLoadingScreen = Buffer->TakeWidget(); - LoadingScreenAttr.bAutoCompleteWhenLoadingCompletes = false; - LoadingScreenAttr.MinimumLoadingScreenDisplayTime = 2.0f; - - GetMoviePlayer()->SetupLoadingScreen(LoadingScreenAttr); -} + UMJGameInstance* MJGI = Cast(GetGameInstance()); + if (MJGI) + { + const FString SlotName = FString::Printf(TEXT("Slot_%d"), SlotNum); + if (UGameplayStatics::DoesSaveGameExist(SlotName, 0)) + { + UMJSaveGame* SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); + + MJGI->GetPlayerSessionDataRef().PlayerName = SaveGame->PlayerName; + MJGI->GetPlayerSessionDataRef().PlayerLevel = SaveGame->PlayerLevel; + MJGI->GetPlayerSessionDataRef().PlayerExp = SaveGame->PlayerExp; + MJGI->GetPlayerSessionDataRef().SaveGameSlotNum = SaveGame->SlotNum; + MJGI->GetPlayerSessionDataRef().SetCurrentEquippedSkillMap(SaveGame->CurrentEquippedSkillMap); + MJGI->GetPlayerSessionDataRef().SetCurrentOwnedSKillMap(SaveGame->CurrentOwnedSkillMap); + + -void UMJSaveGameSubsystem::EndLoadingScreen(UWorld* InLoadedWorld) -{ + return true; + } + } + return false; } - -UMJSaveGame* UMJSaveGameSubsystem::GetSaveGameData() +const uint8 UMJSaveGameSubsystem::GetCurrentSavedSlotNum() { - return SaveGameData; -} + uint8 Result = 0; + + for (int i = 0; i < MaxSaveSlotNum; ++i) + { + const FString SlotName = FString::Printf(TEXT("Slot_%d"), i); + + if (UGameplayStatics::DoesSaveGameExist(SlotName, 0)) + { + UMJSaveGame* SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); + if (SaveGame) + { + ++Result; + } + } + } -FString UMJSaveGameSubsystem::GetSaveSlotName() const -{ - return SaveSlotName; + return Result; } -int32 UMJSaveGameSubsystem::GetUserIndex() const +const bool UMJSaveGameSubsystem::IsSlotFull() { - return UserIndex; + if (GetCurrentSavedSlotNum() == MaxSaveSlotNum) + { + return true; + } + return false; } diff --git a/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.h b/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.h index d0175881..a94de886 100644 --- a/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.h +++ b/Source/ProjectMJ/TG/SubSystem/MJSaveGameSubsystem.h @@ -13,6 +13,9 @@ * Last Modified By: Cha Tae Gwan * Last Modified Date: 2025-06-20 */ + +class UMJSaveGame; + UCLASS() class PROJECTMJ_API UMJSaveGameSubsystem : public UGameInstanceSubsystem { @@ -23,43 +26,44 @@ class PROJECTMJ_API UMJSaveGameSubsystem : public UGameInstanceSubsystem UMJSaveGameSubsystem(); virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; UFUNCTION(BlueprintCallable) UMJSaveGame* GetSaveGameData(); - UFUNCTION(BlueprintCallable) - FString GetSaveSlotName() const; + UFUNCTION() + void SaveGameToCurrentSlotNum(); - UFUNCTION(BlueprintCallable) - int32 GetUserIndex() const; + UFUNCTION() + void SaveGameToSelectedSlotNum(const uint8 InputSlotNum); - UFUNCTION(BlueprintCallable) - void CreateSaveGame(); - - UFUNCTION(BlueprintCallable) - void LoadSaveGame(AMJPlayerCharacter* Player); + UFUNCTION() + bool LoadGameFromSlotNum(int8 SlotNum); - UFUNCTION(BlueprintCallable) - void SaveGameToSlot(AMJPlayerCharacter* Player); + UFUNCTION() + const uint8 GetCurrentSavedSlotNum(); + + const uint8 GetMaxSaveSlotNum() {return MaxSaveSlotNum; } + const bool IsSlotFull(); - // Loading Screen Section + // Loading Screen Section UFUNCTION() virtual void BeginLoadingScreen(const FString& MapName); UFUNCTION() virtual void EndLoadingScreen(UWorld* InLoadedWorld); - UPROPERTY(VisibleAnywhere) - TSubclassOf LoadingScreen; protected: + UPROPERTY(VisibleAnywhere) + TSubclassOf LoadingScreen; + UPROPERTY(VisibleAnywhere) TObjectPtr SaveGameData; - - const FString SaveSlotName = TEXT("DefaultSaveGameSlot"); - const int32 UserIndex = 0; + UPROPERTY(VisibleAnywhere) + uint8 MaxSaveSlotNum; }; diff --git a/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.cpp b/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.cpp new file mode 100644 index 00000000..310848df --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.cpp @@ -0,0 +1,69 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJBossHpBarWidget.h" + +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Components/ProgressBar.h" +#include "Kismet/GameplayStatics.h" +#include "TG/AI/MJAIBossCharacterTG.h" + +void UMJBossHpBarWidget::NativeConstruct() +{ + Super::NativeConstruct(); +} + +void UMJBossHpBarWidget::BindToAttributes() +{ + AActor* BossRef = UGameplayStatics::GetActorOfClass(GetWorld(),AMJAIBossCharacterTG::StaticClass()); + + if (BossRef) + { + AMJAIBossCharacterTG* MJBossRef = Cast(BossRef); + + if (MJBossRef) + { + UMJAbilitySystemComponent* MJBossASC = Cast(MJBossRef->GetAbilitySystemComponent()); + + const UMJCharacterAttributeSet* MJBossASet = MJBossASC->GetSet(); + if (MJBossASC) + { + if (MJBossASC) + { + + CurrentHealth = MJBossASC->GetNumericAttribute(UMJCharacterAttributeSet::GetHealthAttribute()); + CurrentMaxHealth = MJBossASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxHealthAttribute()); + + MJBossASC->GetGameplayAttributeValueChangeDelegate(MJBossASet->GetHealthAttribute()).AddUObject(this,&UMJBossHpBarWidget::OnHealthChanged); + MJBossASC->GetGameplayAttributeValueChangeDelegate(MJBossASet->GetMaxHealthAttribute()).AddUObject(this,&UMJBossHpBarWidget::OnMaxHealthChanged); + + } + } + } + } +} + +void UMJBossHpBarWidget::OnHealthChanged(const FOnAttributeChangeData& Data) +{ + if (Data.NewValue >= 0.0f) + { + CurrentHealth = FMath::Clamp(Data.NewValue, 0.0f, CurrentMaxHealth); + HpProgressBar->SetPercent(CurrentHealth / CurrentMaxHealth); + + if (FMath::IsNearlyZero(CurrentHealth)) + { + SetVisibility(ESlateVisibility::Hidden); + } + } +} + +void UMJBossHpBarWidget::OnMaxHealthChanged(const FOnAttributeChangeData& Data) +{ + if (Data.NewValue >= 0.0f) + { + CurrentMaxHealth = Data.NewValue; + HpProgressBar->SetPercent(CurrentHealth/ CurrentMaxHealth); + } +} + diff --git a/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.h b/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.h new file mode 100644 index 00000000..6d27caf1 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJBossHpBarWidget.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJBossHpBarWidget.generated.h" + +struct FOnAttributeChangeData; +class UProgressBar; +/** + * Class Description: BossHpBarWidget + * Author: Cha Tae Gwan + * Created Date: 2025-07-19 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-19 + */ +UCLASS() +class PROJECTMJ_API UMJBossHpBarWidget : public UUserWidget +{ + GENERATED_BODY() +public: + + UFUNCTION() + void BindToAttributes(); + +protected: + + virtual void NativeConstruct() override; + + void OnHealthChanged(const FOnAttributeChangeData& Data); + + void OnMaxHealthChanged(const FOnAttributeChangeData& Data); + + UPROPERTY(Meta = (BindWidget)) + TObjectPtr HpProgressBar; + + UPROPERTY() + float CurrentHealth; + + UPROPERTY() + float CurrentMaxHealth; + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.cpp b/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.cpp new file mode 100644 index 00000000..c7ef6031 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.cpp @@ -0,0 +1,26 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJChildMenuBaseWidget.h" + +void UMJChildMenuBaseWidget::SetParentWidget(UUserWidget* NewParentWidget) +{ + if (IsValid(NewParentWidget)) + { + ParentWidget = NewParentWidget; + } +} + +UUserWidget* UMJChildMenuBaseWidget::GetParentWidget() +{ + return ParentWidget.Get(); +} + +void UMJChildMenuBaseWidget::BackToParentWidget() +{ + if (ParentWidget.IsValid()) + { + SetVisibility(ESlateVisibility::Hidden); + ParentWidget->SetVisibility(ESlateVisibility::Visible); + } +} diff --git a/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.h b/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.h new file mode 100644 index 00000000..726f2b8c --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJChildMenuBaseWidget.h @@ -0,0 +1,36 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJChildMenuBaseWidget.generated.h" + +/** + * Class Description: UMJChildMenuBaseWidget Widget ( + * Author: Cha Tae Gwan + * Created Date: 2025-07-21 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-21 + */ +UCLASS() +class PROJECTMJ_API UMJChildMenuBaseWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + UFUNCTION() + void SetParentWidget(UUserWidget* NewParentWidget); + + UFUNCTION() + UUserWidget* GetParentWidget(); + + UFUNCTION(BlueprintCallable) + void BackToParentWidget(); + +protected: + + UPROPERTY() + TWeakObjectPtr ParentWidget; + +}; diff --git a/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.cpp b/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.cpp new file mode 100644 index 00000000..4d5c68a4 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.cpp @@ -0,0 +1,101 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJDungeonEndMenuWidget.h" + +#include "MJChildMenuBaseWidget.h" +#include "MJGameFlowPopUpMsgWidget.h" +#include "ProjectMJ.h" +#include "Character/MJPlayerCharacter.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/Button.h" +#include "GameMode/MJGameModeBase.h" +#include "Kismet/GameplayStatics.h" + + +void UMJDungeonEndMenuWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + + Button_GotoTown->OnClicked.AddDynamic(this,&UMJDungeonEndMenuWidget::OnClicked_GotoTown); + + Button_TryAgain->OnClicked.AddDynamic(this,&UMJDungeonEndMenuWidget::OnClicked_TryAgain); + + + UMJPlayerStatComponent* MJPlayerStatComp = (Cast(UGameplayStatics::GetPlayerCharacter(this,0)) + ->FindComponentByClass()); + if (MJPlayerStatComp) + { + MJPlayerStatComp->OnDeath.AddDynamic(this,&::UMJDungeonEndMenuWidget::OnDead); + } + + PopUpWidget = Cast(CreateWidget(this, PopUpWidgetClass)); + if (PopUpWidget) + { + PopUpWidget->SetParentWidget(this); + PopUpWidget->AddToViewport(2); + PopUpWidget->SetVisibility(ESlateVisibility::Hidden); + } + + SetVisibility(ESlateVisibility::Hidden); +} + +void UMJDungeonEndMenuWidget::OnClicked_GotoTown() +{ + if (IsValid(PopUpWidget)) + { + SetVisibility(ESlateVisibility::Hidden); + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + APlayerController* PC = UGameplayStatics::GetPlayerController(WeakThis->GetWorld(),0); + if (PC) + { + WeakThis->RemoveFromParent(); + AMJGameModeBase* MJGM = WeakThis->GetWorld()->GetAuthGameMode(); + if (MJGM) + { + MJGM->TravelToMap(MAP_TOWN); + } + } + } + }), FText::FromString(TEXT("Are you sure you want to go to town?"))); + } + + } + +} + +void UMJDungeonEndMenuWidget::OnClicked_TryAgain() +{ + if (IsValid(PopUpWidget)) + { + SetVisibility(ESlateVisibility::Hidden); + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + WeakThis->GetWorld()->ServerTravel(UGameplayStatics::GetCurrentLevelName(WeakThis->GetWorld())); + } + }), FText::FromString(TEXT("Are you sure you want to try this level again?"))); + } + + } +} + +void UMJDungeonEndMenuWidget::OnDead(AActor* InEffectCauser) +{ + SetVisibility(ESlateVisibility::Visible); +} diff --git a/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.h b/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.h new file mode 100644 index 00000000..b05ea158 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonEndMenuWidget.h @@ -0,0 +1,49 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJDungeonEndMenuWidget.generated.h" + +/** + * Class Description: DungeonEndMenu Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-22 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-22 + */ + +class UMJChildMenuBaseWidget; +class UMJGameFlowPopUpMsgWidget; +class UButton; +UCLASS() +class PROJECTMJ_API UMJDungeonEndMenuWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + virtual void NativeConstruct() override; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf PopUpWidgetClass; + + UPROPERTY() + TObjectPtr PopUpWidget; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_GotoTown; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_TryAgain; + + UFUNCTION() + void OnClicked_GotoTown(); + + UFUNCTION() + void OnClicked_TryAgain(); + + UFUNCTION() + void OnDead(AActor* InEffectCauser); + +}; diff --git a/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.cpp b/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.cpp new file mode 100644 index 00000000..fc817969 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.cpp @@ -0,0 +1,131 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJDungeonMapWidget.h" + +#include "MJDungeonNodeWidget.h" +#include "Components/BackgroundBlur.h" +#include "Components/CanvasPanel.h" +#include "Components/CanvasPanelSlot.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" +#include "Kismet/KismetMathLibrary.h" +#include "TG/SubSystem/MJDungeonGenerationSubSystem.h" + + +void UMJDungeonMapWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + //Background->SetVisibility(ESlateVisibility::Hidden); + BackgroundBlur->SetVisibility(ESlateVisibility::Hidden); + DrawAllWidget(); +} + +int32 UMJDungeonMapWidget::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, + const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, + const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const +{ + // Draw Debug Lines + return Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); +} + +void UMJDungeonMapWidget::DrawNode() +{ + const FMJDungeonGraph* DungeonGraph = GetGameInstance()->GetSubsystem()->GetDungeonGraph(); + + if (DungeonGraph) + { + for (auto& Iter : DungeonGraph->Nodes) + { + UMJDungeonNodeWidget* NewNodeWidget = Cast(CreateWidget(this, NodeWidgetClass)); + if (NewNodeWidget) + { + NewNodeWidget->NodeNum->SetText(FText::FromString(FString::FromInt(Iter.NodeID))); + NewNodeWidget->SetVisibility(ESlateVisibility::Hidden); + + switch (Iter.NodeType) + { + case EMJNodeType::Battle: + + break; + case EMJNodeType::Boss: + NewNodeWidget->SetColorAndOpacity(FLinearColor::Red); + + break; + case EMJNodeType::Reward: + NewNodeWidget->SetColorAndOpacity(FLinearColor::Green); + break; + } + + UCanvasPanelSlot* AssignedSlot = Cast(CanvasPanel->AddChild(NewNodeWidget)); + + if (AssignedSlot) + { + AssignedSlot->SetPosition(FVector2d(Iter.UICoordinate.X,Iter.UICoordinate.Y)); + + } + + } + } + } + + +} + +void UMJDungeonMapWidget::DrawEdge() +{ + const FMJDungeonGraph* DungeonGraph = GetGameInstance()->GetSubsystem()->GetDungeonGraph(); + + if (DungeonGraph) + { + for (auto& IterNode : DungeonGraph->Nodes) + { + for (auto& IterNodeConnectedNodeID : IterNode.ConnectedNodeIDs) + { + FVector2D NewPivotPoint = (IterNode.UICoordinate + DungeonGraph->Nodes[IterNodeConnectedNodeID].UICoordinate) / 2 + 50.f; + + + if (!PivotPoints.Contains(NewPivotPoint)) + { + PivotPoints.AddUnique(NewPivotPoint); + + UUserWidget* NewEdgeWidget = CreateWidget(this,EdgeWidgetClass); + if (NewEdgeWidget) + { + FVector2D DeltaVector = DungeonGraph->Nodes[IterNodeConnectedNodeID].UICoordinate - IterNode.UICoordinate; + + float Distance2D = UKismetMathLibrary::Distance2D(DungeonGraph->Nodes[IterNodeConnectedNodeID].UICoordinate, IterNode.UICoordinate); + + float Angle = FMath::RadiansToDegrees(FMath::Atan2(DeltaVector.Y, DeltaVector.X)); + + NewEdgeWidget->SetRenderTransform(FWidgetTransform(NewPivotPoint,FVector2D(1.0f,1.0f),FVector2D::ZeroVector, Angle)); + NewEdgeWidget->SetVisibility(ESlateVisibility::Hidden); + + UCanvasPanelSlot* AssignedSlot = CanvasPanel->AddChildToCanvas(NewEdgeWidget); + AssignedSlot->SetAlignment(FVector2D(0.5f,0.5f)); + AssignedSlot->SetSize(FVector2D(Distance2D,10.0f)); + + } + + } + } + } + } +} + +void UMJDungeonMapWidget::DrawAllWidget() +{ + PivotPoints.Empty(); + DrawEdge(); + DrawNode(); +} + + +void UMJDungeonMapWidget::SetAllVisibility(ESlateVisibility NewVisibility) +{ + for (auto& Iter : CanvasPanel->GetAllChildren()) + { + Iter->SetVisibility(NewVisibility); + } +} diff --git a/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.h b/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.h new file mode 100644 index 00000000..6c261d49 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonMapWidget.h @@ -0,0 +1,68 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJDungeonMapWidget.generated.h" + +class UBackgroundBlur; +class UCanvasPanel; +class UImage; +/** + * Class Description: DungeonMapWidget + * Author: Cha Tae Gwan + * Created Date: 2025_07_29 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025_07_29 + */ + + +UCLASS() +class PROJECTMJ_API UMJDungeonMapWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION() + void SetAllVisibility(ESlateVisibility NewVisibility); + // + // UPROPERTY(meta = (BindWidget)) + // TObjectPtr Background; + + + + +protected: + + virtual void NativeConstruct() override; + + virtual int32 NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; + + UFUNCTION() + void DrawNode(); + + UFUNCTION() + void DrawEdge(); + + UFUNCTION() + void DrawAllWidget(); + + + UPROPERTY(EditDefaultsOnly) + TSubclassOf NodeWidgetClass; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf EdgeWidgetClass; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr CanvasPanel; + + UPROPERTY() + TArray PivotPoints; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BackgroundBlur; + +}; diff --git a/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.cpp b/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.cpp new file mode 100644 index 00000000..b3a5a068 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.cpp @@ -0,0 +1,100 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJDungeonNodeWidget.h" + +#include "Components/Image.h" +#include "Components/TextBlock.h" +#include "Kismet/GameplayStatics.h" +#include "Kismet/KismetStringLibrary.h" +#include "Kismet/KismetTextLibrary.h" +#include "TG/MJGameInstance.h" +#include "TG/GameMode/MJGameModeDungeon.h" +#include "TG/SubSystem/MJDungeonGenerationSubSystem.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + +void UMJDungeonNodeWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + LoadedPlayerSessionData = GetGameInstance()->GetPlayerSessionDataRef(); + +} + +void UMJDungeonNodeWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + + if (LoadedPlayerSessionData.CurrentDungeonMapNum == UKismetStringLibrary::Conv_StringToInt(UKismetTextLibrary::Conv_TextToString((NodeNum->GetText())))) + { + FLinearColor NewColor(NodeImage->GetColorAndOpacity()); + NewColor.A = ((FMath::Sin(UKismetSystemLibrary::GetGameTimeInSeconds(GetWorld()) * 10) + 1.0f) * 0.5f); + NodeImage->SetColorAndOpacity(NewColor); + } + +} + +FReply UMJDungeonNodeWidget::NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, + const FPointerEvent& InMouseEvent) +{ + UMJDungeonGenerationSubSystem* DGSubSystem = GetGameInstance()->GetSubsystem(); + + if (DGSubSystem) + { + const FMJDungeonGraph* DungeonGraph = DGSubSystem->GetDungeonGraph(); + + uint8 NodeNumber; + EMJNodeType NodeType = EMJNodeType::Battle; + FMJDungeonNode Node; + FString MapName; + + if (DungeonGraph) + { + NodeNumber = UKismetStringLibrary::Conv_StringToInt(NodeNum->GetText().ToString()); + NodeType = DungeonGraph->Nodes[NodeNumber].NodeType; + Node = DungeonGraph->Nodes[NodeNumber]; + } + + if (AMJGameModeDungeon* GMDungeon = Cast(UGameplayStatics::GetGameMode(GetWorld()))) + { + if (GMDungeon) + { + uint8 CurrentPlayerMapNum = GetGameInstance()->GetPlayerSessionDataRef().CurrentDungeonMapNum; + + if (true)//GetGameInstance()->GetSubsystem()->CheckHasRoute(CurrentPlayerMapNum, NodeNumber)) + { + + switch (NodeType) + { + case EMJNodeType::Battle: + MapName = FString::Printf(TEXT("Dungeon_Chunk_Battle0%d"), Node.AssignedMapID); + break; + + case EMJNodeType::Boss: + MapName = TEXT("Dungeon_Chunk_Boss"); + break; + case EMJNodeType::Reward: + MapName = TEXT("Dungeon_Chunk_Reward"); + break; + } + + // GameSaveTiming + + GetGameInstance()->GetSubsystem()->SaveGameToCurrentSlotNum(); + + + + GMDungeon->TravelToMapByNode(MapName,UKismetStringLibrary::Conv_StringToInt(NodeNum->GetText().ToString())); + + FInputModeGameAndUI InputMode; + InputMode.SetHideCursorDuringCapture(false); + UGameplayStatics::GetPlayerController(GetWorld(),0)->SetInputMode(InputMode); + + return FReply::Handled(); + } + } + } + } + return FReply::Unhandled(); +} + diff --git a/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.h b/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.h new file mode 100644 index 00000000..864a4675 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJDungeonNodeWidget.h @@ -0,0 +1,41 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "TG/Struct/MJPlayerSessionDataStruct.h" +#include "MJDungeonNodeWidget.generated.h" + +class UImage; +class UTextBlock; +/** + * Class Description: DungeonNodeWidget + * Author: Cha Tae Gwan + * Created Date: 2025_07_29 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025_07_29 + */ +UCLASS() +class PROJECTMJ_API UMJDungeonNodeWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UPROPERTY(meta = (BindWidget), BlueprintReadWrite) + TObjectPtr NodeImage; + + UPROPERTY(meta = (BindWidget), BlueprintReadWrite) + TObjectPtr NodeNum; + + FMJPlayerSessionData LoadedPlayerSessionData; + +protected: + virtual void NativeConstruct() override; + + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; + + virtual FReply NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + +}; diff --git a/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.cpp b/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.cpp new file mode 100644 index 00000000..37a5ece8 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.cpp @@ -0,0 +1,147 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJGameFlowHUDWidget.h" + +#include "MJBossHpBarWidget.h" +#include "MJDungeonEndMenuWidget.h" +#include "MJGameFlowPopUpMsgWidget.h" +#include "MJLoadGameWidget.h" +#include "MJPauseMenuWidget.h" +#include "MJSettingsWidget.h" +#include "Components/Button.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" +#include "Kismet/GameplayStatics.h" +#include "TG/GameState/MJGameStateDungeon.h" + + +void UMJGameFlowHUDWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + PauseMenu->SetVisibility(ESlateVisibility::Hidden); + SaveGameMenu->SetVisibility(ESlateVisibility::Hidden); + DungeonEndMenu->SetVisibility(ESlateVisibility::Hidden); + BossHpBar->SetVisibility(ESlateVisibility::Hidden); + MiniMap->SetVisibility(ESlateVisibility::Visible); + TimeTextBlock->SetVisibility(ESlateVisibility::Hidden); + TimeBG->SetVisibility(ESlateVisibility::Hidden); + + CurrTime = 0.0f; + + FSlateApplication::Get().OnApplicationActivationStateChanged().AddUObject(this, &UMJGameFlowHUDWidget::OnWindowFocusChanged); + + + SettingsButton->OnClicked.AddDynamic(this,&UMJGameFlowHUDWidget::PauseGame); + + AMJGameStateDungeon* MJDungeonState = Cast(UGameplayStatics::GetGameState(GetWorld())); + if (MJDungeonState) + { + MJDungeonState->OnAIBossSpawned.AddDynamic(this,&UMJGameFlowHUDWidget::OnBossSpawned); + MJDungeonState->OnAIBossDied.AddDynamic(this, &UMJGameFlowHUDWidget::OnBossDied); + + //MiniMap->SetVisibility(ESlateVisibility::Visible); + + GetWorld()->GetTimerManager().ClearTimer(TimeWidgetTimerHandle); + + TWeakObjectPtr WeakThis = this; + GetWorld()->GetTimerManager().SetTimer(TimeWidgetTimerHandle,FTimerDelegate::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + WeakThis->CurrTime += 1.0f; + WeakThis->TimeTextBlock->SetText(FText::FromString(FString::Printf(TEXT("%2d:%02d"), FMath::FloorToInt(WeakThis->CurrTime / 60) ,FMath::FloorToInt(WeakThis->CurrTime) % 60))); + } + + }),1.0f,true,0.0f); + + TimeTextBlock->SetVisibility(ESlateVisibility::Visible); + TimeBG->SetVisibility(ESlateVisibility::Visible); + } +} + + +void UMJGameFlowHUDWidget::OnBossSpawned() +{ + BossHpBar->BindToAttributes(); + BossHpBar->SetVisibility(ESlateVisibility::Visible); + if (!BossHpFadeIn) + { + return; + } + PlayAnimation(BossHpFadeIn); +} + +void UMJGameFlowHUDWidget::OnBossDied() +{ + FWidgetAnimationDynamicEvent AnimEndDelgate; + AnimEndDelgate.BindDynamic(this,&UMJGameFlowHUDWidget::OnBossHpBarFadeInEnded); + + PlayAnimation(BossHpFadeIn,0.0f,1,EUMGSequencePlayMode::Type::Reverse); + BindToAnimationFinished(BossHpFadeIn,AnimEndDelgate); +} + +void UMJGameFlowHUDWidget::OnBossHpBarFadeInEnded() +{ + BossHpBar->SetVisibility(ESlateVisibility::Hidden); +} + +void UMJGameFlowHUDWidget::PauseGame() +{ + if (PC.IsValid()) + { + if (PC->IsPaused()) + { + UMJSettingsWidget* SettingsWidget = Cast((Cast(PauseMenu)->GetSettingsWidget())); + UMJGameFlowPopUpMsgWidget* ForceExitCautionWidget = Cast((PauseMenu->GetForceExitCautionWidget())); + + if ( SettingsWidget && ForceExitCautionWidget) + { + if (SettingsWidget->GetVisibility() == ESlateVisibility::Visible) + { + SettingsWidget->BackToParentWidget(); + } + else if (ForceExitCautionWidget->GetVisibility() == ESlateVisibility::Visible) + { + ForceExitCautionWidget->BackToParentWidget(); + } + else + { + PauseMenu->SetVisibility(ESlateVisibility::Hidden); + PC->SetPause(false); + } + } + } + else + { + PauseMenu->SetVisibility(ESlateVisibility::Visible); + PC->SetPause(true); + } + } +} + +void UMJGameFlowHUDWidget::OnWindowFocusChanged(bool bIsFocused) +{ + if (PC.IsValid()) + { + if (bIsFocused) + { + + } + else + { + PauseMenu->SetVisibility(ESlateVisibility::Visible); + PC->SetPause(true); + } + } +} + +void UMJGameFlowHUDWidget::SetPlayerController(APlayerController* InputPC) +{ + if (IsValid(InputPC)) + { + PC = InputPC; + PauseMenu->SetPlayerController(InputPC); + } +} \ No newline at end of file diff --git a/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.h b/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.h new file mode 100644 index 00000000..e9fc6bef --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJGameFlowHUDWidget.h @@ -0,0 +1,112 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJGameFlowHUDWidget.generated.h" + +class UButton; +class UImage; +class UTextBlock; +class UMJLoadGameWidget; +class UMJDungeonMapWidget; +class UMJBossHpBarWidget; +class UMJSettingsWidget; +class UMJDungeonEndMenuWidget; +class UMJGameFlowPopUpMsgWidget; +class UMJPauseMenuWidget; +/** + * Class Description: UMJGameFlowHUDWidget Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-24 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-24 + */ + +UCLASS() +class PROJECTMJ_API UMJGameFlowHUDWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION() + void PauseGame(); + + void OnWindowFocusChanged(bool bIsFocused); + + void SetPlayerController(APlayerController* InputPC); + + UFUNCTION() + void OnBossSpawned(); + + UFUNCTION() + void OnBossDied(); + + UFUNCTION() + void OnBossHpBarFadeInEnded(); + + FORCEINLINE UMJLoadGameWidget* GetSaveGameWidget() {return SaveGameMenu;} + FORCEINLINE UMJDungeonMapWidget* GetDungeonMapWidget() {return DungeonMap;} + +protected: + + + virtual void NativeConstruct() override; + + TWeakObjectPtr PC; + + UPROPERTY(meta= (BindWidget)) + TObjectPtr PauseMenu; + + UPROPERTY(meta= (BindWidget)) + TObjectPtr SaveGameMenu; + + UPROPERTY(meta= (BindWidget)) + TObjectPtr DungeonEndMenu; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr DungeonMap; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MiniMap; + + + // Boss Section + UPROPERTY(meta=(BindWidget)) + TObjectPtr BossHpBar; + + UPROPERTY(meta=(BindWidgetAnim), Transient) + TObjectPtr BossHpFadeIn; + + // Time Section + + UPROPERTY(meta = (BindWidget)) + TObjectPtr TimeTextBlock; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr TimeBG; + + UPROPERTY() + float CurrTime; + + UPROPERTY() + FTimerHandle TimeWidgetTimerHandle; + + // Button Section + + UPROPERTY(meta=(BindWidget)) + TObjectPtr InventoryButton; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr StatButton; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr QuestButton; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr SettingsButton; + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.cpp b/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.cpp new file mode 100644 index 00000000..d6947d52 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.cpp @@ -0,0 +1,45 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJGameFlowPopUpMsgWidget.h" + +#include "Components/AudioComponent.h" +#include "Components/Button.h" +#include "Components/TextBlock.h" +#include "Kismet/GameplayStatics.h" +#include "Sound/SoundCue.h" + +void UMJGameFlowPopUpMsgWidget::PopUpWithCallback(const FOnUserConfirmed InCallBackFunc, const FText InText) +{ + SetVisibility(ESlateVisibility::Visible); + CallBackFunc = InCallBackFunc; + PopUptext->SetText(InText); + UGameplayStatics::SpawnSound2D(GetWorld(),CautionSound); +} + +void UMJGameFlowPopUpMsgWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + BackButton->OnClicked.AddDynamic(this, &UMJGameFlowPopUpMsgWidget::OnClicked_BackButton); + ConfirmButton->OnClicked.AddDynamic(this, &UMJGameFlowPopUpMsgWidget::OnClicked_ConfirmButton); +} + +void UMJGameFlowPopUpMsgWidget::OnClicked_BackButton() +{ + BackToParentWidget(); +} + +void UMJGameFlowPopUpMsgWidget::OnClicked_ConfirmButton() +{ + ConfirmInternal(); +} + +void UMJGameFlowPopUpMsgWidget::ConfirmInternal() +{ + if (CallBackFunc.IsBound()) + { + CallBackFunc.Execute(); + SetVisibility(ESlateVisibility::Hidden); + } +} diff --git a/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.h b/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.h new file mode 100644 index 00000000..e36b6f39 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJGameFlowPopUpMsgWidget.h @@ -0,0 +1,61 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJChildMenuBaseWidget.h" +#include "MJGameFlowPopUpMsgWidget.generated.h" + +/** + * Class Description: UMJGameFlowPopUpMsgWidget Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-24 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-24 + */ + +class UTextBlock; +DECLARE_DELEGATE(FOnUserConfirmed) + +class UButton; +class USoundCue; +UCLASS() +class PROJECTMJ_API UMJGameFlowPopUpMsgWidget : public UMJChildMenuBaseWidget +{ + GENERATED_BODY() + +public: + + void PopUpWithCallback(const FOnUserConfirmed InCallBackFunc, const FText InText); + +protected: + virtual void NativeConstruct() override; + + UFUNCTION() + void OnClicked_BackButton(); + + UFUNCTION() + void OnClicked_ConfirmButton(); + + UFUNCTION() + void ConfirmInternal(); + + FOnUserConfirmed CallBackFunc; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PopUptext; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BackButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ConfirmButton; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SuccessSound; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr CautionSound; + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.cpp b/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.cpp new file mode 100644 index 00000000..37402b4b --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.cpp @@ -0,0 +1,147 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJLoadGameSlotWidget.h" + +#include "MJGameFlowHUDWidget.h" +#include "MJGameFlowPopUpMsgWidget.h" +#include "MJLoadGameWidget.h" +#include "ProjectMJ.h" +#include "Components/AudioComponent.h" +#include "Components/Button.h" +#include "Components/TextBlock.h" +#include "Controller/MJPlayerController.h" +#include "Kismet/GameplayStatics.h" +#include "Sound/SoundCue.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + + +void UMJLoadGameSlotWidget::NativeOnInitialized() +{ + Super::NativeOnInitialized(); + SlotNum = -1; +} + +void UMJLoadGameSlotWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + DeleteButton->OnClicked.AddDynamic(this, &UMJLoadGameSlotWidget::DeleteSelf); + + PopUpMsgWidget = Cast(CreateWidget(this,PopUpMessageWidgetClass)); + if (PopUpMsgWidget) + { + PopUpMsgWidget->AddToViewport(3); + PopUpMsgWidget->SetParentWidget(this); + PopUpMsgWidget->SetVisibility(ESlateVisibility::Hidden); + } +} + +FReply UMJLoadGameSlotWidget::NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) +{ + UMJSaveGameSubsystem* SGSS = GetGameInstance()->GetSubsystem(); + + + + + if (SGSS) + { + if (UGameplayStatics::GetCurrentLevelName(GetWorld()) == MAP_TOWN) + { + PopUpMsgWidget->SetVisibility(ESlateVisibility::Visible); + + TWeakObjectPtr WeakThis = this; + PopUpMsgWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + // Saving at SavePoint. Widget popups only in town. + if (WeakThis.IsValid()) + { + WeakThis->GetGameInstance()->GetSubsystem()->SaveGameToSelectedSlotNum(WeakThis->SlotNum); + + AMJPlayerController* MJPC = Cast(UGameplayStatics::GetPlayerController(WeakThis->GetWorld(),0)); + + if (MJPC) + { + MJPC->GetGameFlowHUD()->GetSaveGameWidget()->SetVisibility(ESlateVisibility::Hidden); + MJPC->ChangeToIMCDefault(); + } + + UGameplayStatics::SpawnSound2D(WeakThis->GetWorld(),WeakThis->FailSound); + } + + }), FText::FromString(FString::Printf(TEXT("Are you sure to SAVE at Slot_%d ?"), SlotNum) )); + } + else if (UGameplayStatics::GetCurrentLevelName(GetWorld()) == MAP_MAINMENU) + { + if (!UGameplayStatics::DoesSaveGameExist(FString::Printf(TEXT("Slot_%d"),SlotNum),0)) + { + return FReply::Handled(); + } + + bool bLoadSlotSucceeded = SGSS->LoadGameFromSlotNum(SlotNum); + + if (bLoadSlotSucceeded) + { + UGameplayStatics::SpawnSound2D(GetWorld(),FailSound); + SwitchToInGame(); + } + } + } + + return FReply::Handled(); +} + +void UMJLoadGameSlotWidget::SwitchToInGame() +{ + UGameplayStatics::OpenLevel(this,MAP_TOWN); +} + +void UMJLoadGameSlotWidget::DeleteSelf() +{ + PopUpMsgWidget->SetVisibility(ESlateVisibility::Visible); + + TWeakObjectPtr WeakThis = this; + PopUpMsgWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + // Saving at SavePoint. Widget popups only in town. + if (WeakThis.IsValid()) + { + bool bDeleted = UGameplayStatics::DeleteGameInSlot("Slot_" + FString::FromInt(WeakThis->SlotNum),0); + + if (bDeleted) + { + //RemoveFromParent(); + WeakThis->PlayerNameText->SetText(FText::FromString(TEXT("Empty"))); + WeakThis->CreatedDateText->SetText(FText::FromString(TEXT(""))); + WeakThis->RecentPlayedDateText->SetText(FText::FromString(TEXT(""))); + } + + UGameplayStatics::SpawnSound2D(WeakThis->GetWorld(),WeakThis->SuccessSound); + } + + }), FText::FromString(FString::Printf(TEXT("Are you sure to DELETE Slot_%d ?"), SlotNum) )); + + + +} + + +void UMJLoadGameSlotWidget::SetText(const FText NewSlotNumberText, const FText NewPlayerNameText, const FText NewCreatedDateText, + const FText NewRecentPlayedDateText) +{ + SlotNumberText->SetText(NewSlotNumberText); + PlayerNameText->SetText(NewPlayerNameText); + CreatedDateText->SetText(NewCreatedDateText); + RecentPlayedDateText->SetText(NewRecentPlayedDateText); +} + +void UMJLoadGameSlotWidget::SetSlotNum(int8 InputSlotNum) +{ + SlotNum = InputSlotNum; +} + +int8 UMJLoadGameSlotWidget::GetSlotNum() +{ + return SlotNum; +} + diff --git a/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.h b/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.h new file mode 100644 index 00000000..9f2a722b --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJLoadGameSlotWidget.h @@ -0,0 +1,84 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJLoadGameSlotWidget.generated.h" + +/** + * Class Description: LoadGameSlot Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-21 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-21 + */ + +class UButton; +class UHorizontalBox; +class UVerticalBox; +class UTextBlock; +class UBorder; +class USoundCue; +class UMJGameFlowPopUpMsgWidget; + +UCLASS() +class PROJECTMJ_API UMJLoadGameSlotWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UFUNCTION() + void SetText(const FText NewSlotNumberText, const FText NewPlayerNameText, const FText NewCreatedDateText,const FText NewRecentPlayedDateText); + + UFUNCTION() + void SetSlotNum(int8 InputSlotNum); + + UFUNCTION() + int8 GetSlotNum(); + +protected: + + virtual void NativeOnInitialized() override; + virtual void NativeConstruct() override; + + virtual FReply NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + + UFUNCTION() + void SwitchToInGame(); + + UFUNCTION() + void DeleteSelf(); + + UPROPERTY() + int8 SlotNum; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SlotNumberText; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr CreatedDateText; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr RecentPlayedDateText; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PlayerNameText; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr DeleteButton; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SuccessSound; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr FailSound; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf PopUpMessageWidgetClass; + + UPROPERTY() + TObjectPtr PopUpMsgWidget; + +}; diff --git a/Source/ProjectMJ/TG/UI/MJLoadGameWidget.cpp b/Source/ProjectMJ/TG/UI/MJLoadGameWidget.cpp new file mode 100644 index 00000000..0af65b26 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJLoadGameWidget.cpp @@ -0,0 +1,126 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJLoadGameWidget.h" + +#include "MJLoadGameSlotWidget.h" +#include "ProjectMJ.h" +#include "Components/Button.h" +#include "Components/Image.h" +#include "Components/ScrollBox.h" +#include "Controller/MJPlayerController.h" +#include "Kismet/GameplayStatics.h" +#include "TG/Struct/MJSaveGame.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + + +void UMJLoadGameWidget::NativeConstruct() +{ + Super::NativeConstruct(); + MaxSlotNum = GetGameInstance()->GetSubsystem()->GetMaxSaveSlotNum(); + + BackButton->OnClicked.AddDynamic(this, &UMJLoadGameWidget::OnClicked_Back); + + SavedGameSlotScrollBox->ClearChildren(); + + BackGround->SetVisibility(ESlateVisibility::Visible); + + + InitializeSlot(); +} + +void UMJLoadGameWidget::InitializeSlot() +{ + for (int i = 0; i < MaxSlotNum; ++i) + { + const FString SlotName = FString::Printf(TEXT("Slot_%d"), i); + + if (UGameplayStatics::DoesSaveGameExist(SlotName, 0)) + { + UMJSaveGame* SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); + if (SaveGame) + { + UMJLoadGameSlotWidget* NewSlotWidget = CreateWidget(this, LoadGameSlotWidgetClass); + if (NewSlotWidget) + { + NewSlotWidget->SetSlotNum(SaveGame->SlotNum); + NewSlotWidget->SetText(FText::FromString(FString::FromInt(SaveGame->SlotNum)), + FText::FromString(SaveGame->PlayerName), + FText::FromString(SaveGame->SaveGameCreatedDateTime.ToString(TEXT("%Y/%m/%d %H:%M"))), + FText::FromString(SaveGame->RecentPlayedDateTime.ToString(TEXT("%Y/%m/%d %H:%M"))) + ); + + NewSlotWidget->SetPadding(FMargin(20.0f,20.f,20.f,20.f)); + SavedGameSlotScrollBox->AddChild(NewSlotWidget); + } + } + } + else + { + UMJLoadGameSlotWidget* NewSlotWidget = CreateWidget(this, LoadGameSlotWidgetClass); + + if (NewSlotWidget) + { + NewSlotWidget->SetSlotNum(i); + NewSlotWidget->SetText(FText::FromString(FString::FromInt(i)), + FText::FromString(TEXT("Empty")), + FText(), + FText() + ); + + NewSlotWidget->SetPadding(FMargin(20.0f,20.f,20.f,20.f)); + SavedGameSlotScrollBox->AddChild(NewSlotWidget); + } + } + } +} + + +void UMJLoadGameWidget::UpdateSlot() +{ + for (int i = 0; i < MaxSlotNum; ++i) + { + const FString SlotName = FString::Printf(TEXT("Slot_%d"), i); + + if (UGameplayStatics::DoesSaveGameExist(SlotName, 0)) + { + UMJSaveGame* SaveGame = Cast(UGameplayStatics::LoadGameFromSlot(SlotName, 0)); + if (SaveGame) + { + UWidget* ChildWidget = SavedGameSlotScrollBox->GetChildAt(i); + + UMJLoadGameSlotWidget* ChildSlotWidget = Cast(ChildWidget); + + if (ChildSlotWidget) + { + ChildSlotWidget->SetSlotNum(SaveGame->SlotNum); + ChildSlotWidget->SetText(FText::FromString(FString::FromInt(SaveGame->SlotNum)), + FText::FromString(SaveGame->PlayerName), + FText::FromString(SaveGame->SaveGameCreatedDateTime.ToString(TEXT("%Y/%m/%d %H:%M"))), + FText::FromString(SaveGame->RecentPlayedDateTime.ToString(TEXT("%Y/%m/%d %H:%M"))) + ); + + ChildSlotWidget->SetPadding(FMargin(20.0f,20.f,20.f,20.f)); + } + } + } + } +} + +void UMJLoadGameWidget::OnClicked_Back() +{ + if (UGameplayStatics::GetCurrentLevelName(GetWorld()) == MAP_TOWN) + { + SetVisibility(ESlateVisibility::Hidden); + AMJPlayerController* MJPC = Cast(UGameplayStatics::GetPlayerController(GetWorld(),0)); + if (MJPC) + { + MJPC->ChangeToIMCDefault(); + } + } + else + { + BackToParentWidget(); + } +} + diff --git a/Source/ProjectMJ/TG/UI/MJLoadGameWidget.h b/Source/ProjectMJ/TG/UI/MJLoadGameWidget.h new file mode 100644 index 00000000..86d6ba8c --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJLoadGameWidget.h @@ -0,0 +1,57 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJChildMenuBaseWidget.h" +#include "MJLoadGameWidget.generated.h" + +/** + * Class Description: LoadGame Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-21 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-21 + */ + +class UMJGameFlowPopUpMsgWidget; +class UImage; +class UScrollBox; +class UMJLoadGameSlotWidget; +class UButton; + +UCLASS() +class PROJECTMJ_API UMJLoadGameWidget : public UMJChildMenuBaseWidget +{ + GENERATED_BODY() + +public: + void UpdateSlot(); + +protected: + + virtual void NativeConstruct() override; + + void InitializeSlot(); + + UFUNCTION() + void OnClicked_Back(); + + UPROPERTY(EditDefaultsOnly) + TSubclassOf LoadGameSlotWidgetClass; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SavedGameSlotScrollBox; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BackButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BackGround; + + UPROPERTY() + uint8 MaxSlotNum; + + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJMainMenuWidget.cpp b/Source/ProjectMJ/TG/UI/MJMainMenuWidget.cpp new file mode 100644 index 00000000..59aac231 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJMainMenuWidget.cpp @@ -0,0 +1,117 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TG/UI/MJMainMenuWidget.h" + +#include "MJGameFlowPopUpMsgWidget.h" +#include "MJSettingsWidget.h" +#include "Components/Button.h" +#include "Kismet/GameplayStatics.h" +#include "Kismet/KismetSystemLibrary.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + + +void UMJMainMenuWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + Button_NewGame->OnClicked.AddDynamic(this, &ThisClass::OnClicked_NewGame); + Button_LoadGame->OnClicked.AddDynamic(this, &ThisClass::OnClicked_LoadGame); + Button_Settings->OnClicked.AddDynamic(this, &ThisClass::OnClicked_Settings); + Button_Quit->OnClicked.AddDynamic(this, &ThisClass::OnClicked_Quit); + + APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(),0); + if (PC) + { + FInputModeGameAndUI InputMode; + InputMode.SetWidgetToFocus(this->TakeWidget()); + InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::LockInFullscreen); + InputMode.SetHideCursorDuringCapture(false); + + PC->SetInputMode(InputMode); + PC->SetShowMouseCursor(true); + + NewGamePopUpWidget = Cast(CreateWidget(PC, NewGamePopUpWidgetClass)); + if (NewGamePopUpWidget) + { + NewGamePopUpWidget->AddToViewport(2); + NewGamePopUpWidget->SetParentWidget(this); + NewGamePopUpWidget->SetVisibility(ESlateVisibility::Hidden); + } + + LoadGameWidget = Cast(CreateWidget(PC,LoadGameWidgetClass)); + if (LoadGameWidget) + { + LoadGameWidget->AddToViewport(2); + LoadGameWidget->SetParentWidget(this); + LoadGameWidget->SetVisibility(ESlateVisibility::Hidden); + } + + SettingsWidget = Cast(CreateWidget(PC,SettingsWidgetClass)); + if (SettingsWidget) + { + SettingsWidget->AddToViewport(2); + SettingsWidget->SetParentWidget(this); + SettingsWidget->SetVisibility(ESlateVisibility::Hidden); + } + + PopUpMsgWidget = Cast(CreateWidget(PC,PopUpMsgWidgetClass)); + if (PopUpMsgWidget) + { + PopUpMsgWidget->AddToViewport(2); + PopUpMsgWidget->SetParentWidget(this); + PopUpMsgWidget->SetVisibility(ESlateVisibility::Hidden); + } + } +} + +void UMJMainMenuWidget::OnClicked_NewGame() +{ + if (GetGameInstance()->GetSubsystem()->IsSlotFull()) + { + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpMsgWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + WeakThis->PopUpMsgWidget->BackToParentWidget(); + } + }), FText::FromString(TEXT("All slots are full! Please delete at least one slot."))); + } + } + else + { + SetVisibility(ESlateVisibility::Hidden); + NewGamePopUpWidget->SetVisibility(ESlateVisibility::Visible); + + } +} + +void UMJMainMenuWidget::OnClicked_LoadGame() +{ + SetVisibility(ESlateVisibility::Hidden); + + if (LoadGameWidget) + { + LoadGameWidget->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJMainMenuWidget::OnClicked_Settings() +{ + SetVisibility(ESlateVisibility::Hidden); + + if (SettingsWidget) + { + SettingsWidget->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJMainMenuWidget::OnClicked_Quit() +{ + UKismetSystemLibrary::QuitGame(GetWorld(),nullptr,EQuitPreference::Quit,false); +} diff --git a/Source/ProjectMJ/TG/UI/MJMainMenuWidget.h b/Source/ProjectMJ/TG/UI/MJMainMenuWidget.h new file mode 100644 index 00000000..47802230 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJMainMenuWidget.h @@ -0,0 +1,78 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJMainMenuWidget.generated.h" + +class UMJChildMenuBaseWidget; +class UButton; +/** + * Class Description: MainMenu Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-14 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-14 + */ +UCLASS() +class PROJECTMJ_API UMJMainMenuWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + + virtual void NativeConstruct() override; + + UPROPERTY(EditDefaultsOnly, Category = UI) + TSubclassOf NewGamePopUpWidgetClass; + + UPROPERTY(BlueprintReadWrite) + TObjectPtr NewGamePopUpWidget; + + UPROPERTY(EditDefaultsOnly, Category = UI) + TSubclassOf LoadGameWidgetClass; + + UPROPERTY(BlueprintReadWrite) + TObjectPtr LoadGameWidget; + + UPROPERTY(EditDefaultsOnly, Category = UI) + TSubclassOf SettingsWidgetClass; + + UPROPERTY(BlueprintReadWrite) + TObjectPtr SettingsWidget; + + UPROPERTY(EditDefaultsOnly, Category = UI) + TSubclassOf PopUpMsgWidgetClass; + + UPROPERTY() + TObjectPtr PopUpMsgWidget; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_NewGame; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_LoadGame; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_Settings; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_Quit; + + UFUNCTION() + void OnClicked_NewGame(); + + UFUNCTION() + void OnClicked_LoadGame(); + + UFUNCTION() + void OnClicked_Settings(); + + UFUNCTION() + void OnClicked_Quit(); + + + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.cpp b/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.cpp new file mode 100644 index 00000000..1f39c4b7 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.cpp @@ -0,0 +1,116 @@ +// ThenOneDayStudio + + +#include "TG/UI/MJNewGamePopUpWidget.h" + +#include "ProjectMJ.h" +#include "Components/AudioComponent.h" +#include "Components/Button.h" +#include "Components/EditableTextBox.h" +#include "Kismet/GameplayStatics.h" +#include "Sound/SoundCue.h" +#include "TG/MJGameInstance.h" +#include "TG/Struct/MJSaveGame.h" + + +void UMJNewGamePopUpWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + BackButton->OnClicked.AddDynamic(this, &UMJNewGamePopUpWidget::OnClicked_BackButton); + ConfirmButton->OnClicked.AddDynamic(this, &UMJNewGamePopUpWidget::OnClicked_ConfirmButton); + + NextSlotNum = -1; + +} + +void UMJNewGamePopUpWidget::OnClicked_BackButton() +{ + BackToParentWidget(); +} + +void UMJNewGamePopUpWidget::OnClicked_ConfirmButton() +{ + if (InputPlayerNameTextBox->GetText().IsEmpty()) + { + InputPlayerNameTextBox->SetHintText(FText::FromString(TEXT("Please Enter Valid Player Name!!"))); + + UGameplayStatics::SpawnSound2D(GetWorld(),FailSound); + return; + } + + bool bHasValidSlot = (NextSlotNum = GetEmptySlotNum()) != -1; + + if (bHasValidSlot) + { + UMJSaveGame* NewSave = Cast(UGameplayStatics::CreateSaveGameObject(UMJSaveGame::StaticClass())); + NewSave->SlotNum = NextSlotNum; + NewSave->PlayerName = InputPlayerNameTextBox->GetText().ToString(); + NewSave->SaveGameCreatedDateTime = FDateTime::Now(); + NewSave->RecentPlayedDateTime = FDateTime::Now(); + + const FString SlotName = FString::Printf(TEXT("Slot_%d"), NextSlotNum); + + UGameplayStatics::SaveGameToSlot(NewSave, SlotName, 0); + } + else + { + InputPlayerNameTextBox->SetHintText(FText::FromString(TEXT("### Has No Valid Slot!! ###"))); + } + + // Switch to InGame When sound play end + // UAudioComponent* AudioComp = UGameplayStatics::SpawnSound2D(GetWorld(),SuccessSound); + // if (AudioComp) + // { + // AudioComp->OnAudioFinished.AddDynamic(this, &UMJNewGamePopUpWidget::SwitchToInGame); + // } + SwitchToInGame(); +} + +void UMJNewGamePopUpWidget::SwitchToInGame() +{ + UMJGameInstance* MJGI = GetGameInstance(); + if (MJGI) + { + MJGI->GetPlayerSessionDataRef().PlayerName = InputPlayerNameTextBox->GetText().ToString(); + MJGI->GetPlayerSessionDataRef().SaveGameSlotNum = NextSlotNum; + + // 처음 생성하는 세이브 파일일 경우 원하는 기본 스킬들을 가지고 있을 수 있음 + // - 맨 처음 세이브 파일을 생성했을 때만 단 한번 기본 스킬들을 건네줘야 하기 때문에 여기서 저장하는게 좋을듯. + // 아래 코드를 그대로 사용하고 태그만 바꿔주면 됩니다. + MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Instant.PoisonSlash"))); + MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Instant")),FGameplayTag::RequestGameplayTag(FName("Skill.Instant.PoisonSlash"))); + + MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Charge.AlphaStrike"))); + MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Charge")),FGameplayTag::RequestGameplayTag(FName("Skill.Charge.AlphaStrike"))); + + MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Normal.MeleeAttack"))); + MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Normal")),FGameplayTag::RequestGameplayTag(FName("Skill.Normal.MeleeAttack"))); + + MJGI->GetPlayerSessionDataRef().CurrentOwnedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Passive.DamageUp"))); + MJGI->GetPlayerSessionDataRef().CurrentEquippedSkillMap.Add(FGameplayTag::RequestGameplayTag(FName("Skill.Passive")),FGameplayTag::RequestGameplayTag(FName("Skill.Passive.DamageUp"))); + } + + if (APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0)) + { + FInputModeGameAndUI InputModeGameAndUI; + InputModeGameAndUI.SetHideCursorDuringCapture(false); + PC->SetInputMode(InputModeGameAndUI); + } + // OpenLevel + + UGameplayStatics::OpenLevel(this,MAP_TOWN); +} + +int8 UMJNewGamePopUpWidget::GetEmptySlotNum() +{ + for (int8 i = 0; i < INT8_MAX; ++i) + { + const FString SlotName = FString::Printf(TEXT("Slot_%d"), i); + if (!UGameplayStatics::DoesSaveGameExist(SlotName, 0)) + { + return i; + } + } + return -1; +} diff --git a/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.h b/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.h new file mode 100644 index 00000000..0c5dbe96 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJNewGamePopUpWidget.h @@ -0,0 +1,62 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJChildMenuBaseWidget.h" +#include "MJNewGamePopUpWidget.generated.h" + +/** + * Class Description: LoadGamePopUp Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-21 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-21 + */ + +class UEditableTextBox; +class UButton; +class USoundCue; +UCLASS() +class PROJECTMJ_API UMJNewGamePopUpWidget : public UMJChildMenuBaseWidget +{ + GENERATED_BODY() + +protected: + virtual void NativeConstruct() override; + + UFUNCTION() + void OnClicked_BackButton(); + + UFUNCTION() + void OnClicked_ConfirmButton(); + + UFUNCTION() + void SwitchToInGame(); + + UFUNCTION() + int8 GetEmptySlotNum(); + + UPROPERTY() + int8 NextSlotNum; + + + UPROPERTY(meta = (BindWidget)) + TObjectPtr BackButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ConfirmButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr InputPlayerNameTextBox; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SuccessSound; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr FailSound; + + + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.cpp b/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.cpp new file mode 100644 index 00000000..e8b4dfec --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.cpp @@ -0,0 +1,185 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TG/UI/MJPauseMenuWidget.h" + +#include "MJGameFlowPopUpMsgWidget.h" +#include "ProjectMJ.h" +#include "Components/Button.h" +#include "Components/Spacer.h" +#include "Kismet/GameplayStatics.h" +#include "Player/MJPlayerState.h" +#include "TG/GameMode/MJGameModeDungeon.h" +#include "TG/GameMode/MJGameModeTown.h" +#include "TG/SubSystem/MJSaveGameSubsystem.h" + +void UMJPauseMenuWidget::NativeOnInitialized() +{ + Super::NativeOnInitialized(); +} + +void UMJPauseMenuWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + Button_Resume->OnClicked.AddDynamic(this,&UMJPauseMenuWidget::OnClicked_Resume); + Button_Settings->OnClicked.AddDynamic(this,&UMJPauseMenuWidget::OnClicked_Settings); + Button_MainMenu->OnClicked.AddDynamic(this,&UMJPauseMenuWidget::OnClicked_MainMenu); + Button_GotoTown->OnClicked.AddDynamic(this,&UMJPauseMenuWidget::OnClicked_GotoTown); + Button_QuitGame->OnClicked.AddDynamic(this, &UMJPauseMenuWidget::OnClicked_QuitGame); + + if (PC.IsValid()) + { + SettingsWidget = Cast(CreateWidget(this, SettingsWidgetClass)); + if (SettingsWidget) + { + SettingsWidget->SetParentWidget(this); + SettingsWidget->AddToViewport(2); + SettingsWidget->SetVisibility(ESlateVisibility::Hidden); + } + + PopUpMsgWidget = Cast(CreateWidget(this,PopUpMsgnWidgetClass)); + if (PopUpMsgWidget) + { + PopUpMsgWidget->SetParentWidget(this); + PopUpMsgWidget->AddToViewport(2); + PopUpMsgWidget->SetVisibility(ESlateVisibility::Hidden); + } + + if (AMJGameModeTown* GMTown = GetWorld()->GetAuthGameMode()) + { + Button_GotoTown->SetVisibility(ESlateVisibility::Collapsed); + } + else if (AMJGameModeDungeon* GMDungeon = GetWorld()->GetAuthGameMode()) + { + Button_GotoTown->SetVisibility(ESlateVisibility::Visible); + } + } +} + +void UMJPauseMenuWidget::OnClicked_Resume() +{ + if (PC.IsValid()) + { + SetVisibility(ESlateVisibility::Hidden); + + if (PC->IsPaused()) + { + PC->SetPause(false); + } + } +} + +void UMJPauseMenuWidget::OnClicked_SaveGame() +{ + if (PC.IsValid()) + { + AMJPlayerState* PS = PC->GetPlayerState(); + if (PS) + { + PS->SaveToInstancedPlayerSessionData(); + GetGameInstance()->GetSubsystem()->SaveGameToCurrentSlotNum(); + } + } +} + +void UMJPauseMenuWidget::OnClicked_Settings() +{ + if (SettingsWidget) + { + SetVisibility(ESlateVisibility::Hidden); + SettingsWidget->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJPauseMenuWidget::OnClicked_MainMenu() +{ + if (PC.IsValid()) + { + + SetVisibility(ESlateVisibility::Hidden); + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpMsgWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + AMJGameModeBase* MJGM = WeakThis->GetWorld()->GetAuthGameMode(); + if (MJGM) + { + MJGM->TravelToMap(TEXT("TG_MainMenu")); + } + } + }), FText::FromString(TEXT("All unsaved changes will be lost. Are you sure you want to go main menu?"))); + } + } +} + +void UMJPauseMenuWidget::OnClicked_GotoTown() +{ + if (PC.IsValid()) + { + + SetVisibility(ESlateVisibility::Hidden); + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpMsgWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + AMJGameModeBase* MJGM = WeakThis->GetWorld()->GetAuthGameMode(); + if (MJGM) + { + MJGM->TravelToMap(MAP_TOWN); + } + } + }), FText::FromString(TEXT("All unsaved changes will be lost. Are you sure you want to go back to town?"))); + } + + } +} + +void UMJPauseMenuWidget::OnClicked_QuitGame() +{ + if (IsValid(PopUpMsgWidget)) + { + SetVisibility(ESlateVisibility::Hidden); + UMJGameFlowPopUpMsgWidget* CastedWidget = Cast(PopUpMsgWidget); + + if (CastedWidget) + { + TWeakObjectPtr WeakThis = this; + CastedWidget->PopUpWithCallback(FOnUserConfirmed::CreateLambda([WeakThis] + { + if (WeakThis.IsValid()) + { + UKismetSystemLibrary::QuitGame(WeakThis->GetWorld(),nullptr,EQuitPreference::Quit,false); + } + }), FText::FromString(TEXT("All unsaved changes will be lost. Are you sure you want to quit the game?"))); + } + } +} + +UUserWidget* UMJPauseMenuWidget::GetSettingsWidget() +{ + return SettingsWidget; +} + +UUserWidget* UMJPauseMenuWidget::GetForceExitCautionWidget() +{ + return PopUpMsgWidget; +} + +void UMJPauseMenuWidget::SetPlayerController(APlayerController* InputPC) +{ + if (IsValid(InputPC)) + { + PC = InputPC; + } +} diff --git a/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.h b/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.h new file mode 100644 index 00000000..915c8cd1 --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJPauseMenuWidget.h @@ -0,0 +1,89 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJChildMenuBaseWidget.h" +#include "MJPauseMenuWidget.generated.h" + +class USpacer; +class UButton; + +/** + * Class Description: PauseMenu Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-14 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-14 + */ +UCLASS() +class PROJECTMJ_API UMJPauseMenuWidget : public UMJChildMenuBaseWidget +{ + GENERATED_BODY() +public: + + UFUNCTION() + UUserWidget* GetSettingsWidget(); + + UFUNCTION() + UUserWidget* GetForceExitCautionWidget(); + + void SetPlayerController(APlayerController* PC); + + +protected: + virtual void NativeOnInitialized() override; + + virtual void NativeConstruct() override; + + // WeakPtr of MJPC + TWeakObjectPtr PC; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf SettingsWidgetClass; + + UPROPERTY() + TObjectPtr SettingsWidget; + + UPROPERTY(EditDefaultsOnly) + TSubclassOf PopUpMsgnWidgetClass; + + UPROPERTY() + TObjectPtr PopUpMsgWidget; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_Resume; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_Settings; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_GotoTown; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_MainMenu; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_QuitGame; + + UFUNCTION() + void OnClicked_Resume(); + + UFUNCTION() + void OnClicked_SaveGame(); + + UFUNCTION() + void OnClicked_Settings(); + + UFUNCTION() + void OnClicked_GotoTown(); + + UFUNCTION() + void OnClicked_MainMenu(); + + UFUNCTION() + void OnClicked_QuitGame(); + + + +}; diff --git a/Source/ProjectMJ/TG/UI/MJSettingsWidget.cpp b/Source/ProjectMJ/TG/UI/MJSettingsWidget.cpp new file mode 100644 index 00000000..b034613c --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJSettingsWidget.cpp @@ -0,0 +1,140 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TG/UI/MJSettingsWidget.h" + +#include "Components/Button.h" +#include "Components/ComboBoxString.h" +#include "Components/Slider.h" +#include "GameFramework/GameUserSettings.h" +#include "Kismet/GameplayStatics.h" + +void UMJSettingsWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + ComboBox_WindowMode->OnSelectionChanged.AddDynamic(this, &UMJSettingsWidget::ComboBox_WindowModeChanged); + ComboBox_Resolution->OnSelectionChanged.AddDynamic(this, &UMJSettingsWidget::ComboBox_ResolutionChanged); + ComboBox_Graphics->OnSelectionChanged.AddDynamic(this, &UMJSettingsWidget::ComboBox_GraphicsChanged); + Button_Back->OnClicked.AddDynamic(this, &UMJSettingsWidget::OnClicked_Back); + MasterVolumeSlider->OnValueChanged.AddDynamic(this, &UMJSettingsWidget::MasterVolumeValueChanged); + MusicVolumeSlider->OnValueChanged.AddDynamic(this,&UMJSettingsWidget::MusicVolumeValueChanged); + SFXVolumeSlider->OnValueChanged.AddDynamic(this,&UMJSettingsWidget::UMJSettingsWidget::SFXVolumeValueChanged); +} + +void UMJSettingsWidget::ComboBox_WindowModeChanged(FString SelectedItem, ESelectInfo::Type SelectionType) +{ + if (SelectionType == ESelectInfo::Type::OnMouseClick) + { + UGameUserSettings* CurrentGameUserSettings = GEngine->GetGameUserSettings(); + if (CurrentGameUserSettings) + { + if (SelectedItem == "Windowed") + { + CurrentGameUserSettings->SetFullscreenMode(EWindowMode::Type::Windowed); + // Have to change Resolution cuz it might not show it`s windowed UI when changed at same as screen resolution. + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1280,720)); + } + else if (SelectedItem == "FullScreen") + { + CurrentGameUserSettings->SetFullscreenMode(EWindowMode::Type::Fullscreen); + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1920,1080)); + } + else if (SelectedItem == "WindowedFullScreen") + { + CurrentGameUserSettings->SetFullscreenMode(EWindowMode::Type::WindowedFullscreen); + } + + CurrentGameUserSettings->ApplySettings(false); + CurrentGameUserSettings->SaveSettings(); + } + } +} + +void UMJSettingsWidget::ComboBox_ResolutionChanged(FString SelectedItem, ESelectInfo::Type SelectionType) +{ + if (SelectionType == ESelectInfo::Type::OnMouseClick) + { + UGameUserSettings* CurrentGameUserSettings = GEngine->GetGameUserSettings(); + if (CurrentGameUserSettings) + { + if (SelectedItem == "1280 X 720") + { + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1280,720)); + } + else if (SelectedItem == "1600 X 900") + { + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1600,900)); + } + else if (SelectedItem == "1920 X 1080") + { + CurrentGameUserSettings->SetScreenResolution(FIntPoint(1920,1080)); + } + + CurrentGameUserSettings->ApplySettings(false); + CurrentGameUserSettings->SaveSettings(); + } + } +} + +void UMJSettingsWidget::ComboBox_GraphicsChanged(FString SelectedItem, ESelectInfo::Type SelectionType) +{ + if (SelectionType == ESelectInfo::Type::OnMouseClick) + { + UGameUserSettings* CurrentGameUserSettings = GEngine->GetGameUserSettings(); + if (CurrentGameUserSettings) + { + // Sets all other settings based on an overall value + // @param Value 0:low, 1:medium, 2:high, 3:epic, 4:cinematic (gets clamped if needed) + if (SelectedItem == "Low") + { + CurrentGameUserSettings->SetOverallScalabilityLevel(0); + } + else if (SelectedItem == "Medium") + { + CurrentGameUserSettings->SetOverallScalabilityLevel(1); + } + else if (SelectedItem == "High") + { + CurrentGameUserSettings->SetOverallScalabilityLevel(2); + } + else if (SelectedItem == "Epic") + { + CurrentGameUserSettings->SetOverallScalabilityLevel(3); + } + else if (SelectedItem == "Cinematic") + { + CurrentGameUserSettings->SetOverallScalabilityLevel(4); + } + + CurrentGameUserSettings->ApplySettings(false); + CurrentGameUserSettings->SaveSettings(); + } + } +} + +void UMJSettingsWidget::OnClicked_Back() +{ + BackToParentWidget(); +} + +void UMJSettingsWidget::MasterVolumeValueChanged(float NewValue) +{ + // Range 0 ~ 2.0 + UGameplayStatics::SetSoundMixClassOverride(GetWorld(),SoundMixModifier,MasterSoundClass, NewValue, 1.0f, 0.0f, true); + UGameplayStatics::PushSoundMixModifier(GetWorld(), SoundMixModifier); +} + +void UMJSettingsWidget::MusicVolumeValueChanged(float NewValue) +{ + // Range 0 ~ 2.0 + UGameplayStatics::SetSoundMixClassOverride(GetWorld(),SoundMixModifier,MusicSoundClass, NewValue, 1.0f, 0.0f); + UGameplayStatics::PushSoundMixModifier(GetWorld(), SoundMixModifier); +} + +void UMJSettingsWidget::SFXVolumeValueChanged(float NewValue) +{ + // Range 0 ~ 2.0 + UGameplayStatics::SetSoundMixClassOverride(GetWorld(),SoundMixModifier,SFXSoundClass, NewValue, 1.0f, 0.0f); + UGameplayStatics::PushSoundMixModifier(GetWorld(), SoundMixModifier); +} diff --git a/Source/ProjectMJ/TG/UI/MJSettingsWidget.h b/Source/ProjectMJ/TG/UI/MJSettingsWidget.h new file mode 100644 index 00000000..a1aaf5ae --- /dev/null +++ b/Source/ProjectMJ/TG/UI/MJSettingsWidget.h @@ -0,0 +1,91 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJChildMenuBaseWidget.h" +#include "MJSettingsWidget.generated.h" + +class USlider; +class UButton; +class UComboBoxString; + +/** + * Class Description: Settings Widget + * Author: Cha Tae Gwan + * Created Date: 2025-07-14 + * Last Modified By: Cha Tae Gwan + * Last Modified Date: 2025-07-14 + */ +UCLASS() +class PROJECTMJ_API UMJSettingsWidget : public UMJChildMenuBaseWidget +{ + GENERATED_BODY() + +protected: + + virtual void NativeConstruct() override; + + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ComboBox_WindowMode; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ComboBox_Resolution; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ComboBox_Graphics; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button_Back; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MasterVolumeSlider; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MusicVolumeSlider; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SFXVolumeSlider; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr MasterSoundClass; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr MusicSoundClass; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SFXSoundClass; + + UPROPERTY(EditDefaultsOnly) + TObjectPtr SoundMixModifier; + + + UFUNCTION() + void ComboBox_WindowModeChanged(FString SelectedItem, ESelectInfo::Type SelectionType); + + UFUNCTION() + void ComboBox_ResolutionChanged(FString SelectedItem, ESelectInfo::Type SelectionType); + + UFUNCTION() + void ComboBox_GraphicsChanged(FString SelectedItem, ESelectInfo::Type SelectionType); + + UFUNCTION() + void OnClicked_Back(); + + UFUNCTION() + void MasterVolumeValueChanged(float NewValue); + + UFUNCTION() + void MusicVolumeValueChanged(float NewValue); + + UFUNCTION() + void SFXVolumeValueChanged(float NewValue); + + + + + + + +}; diff --git a/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.cpp b/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.cpp new file mode 100644 index 00000000..f5459280 --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.cpp @@ -0,0 +1,64 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Bar/MJEnemyHPBar.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Components/ProgressBar.h" + +void UMJEnemyHPBar::BindToAttributes(UMJAbilitySystemComponent* ASC, UMJCharacterAttributeSet* AttributeSet) +{ + if (!ASC || !AttributeSet) + { + return; + } + + MaxHP = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxHealthAttribute()); + CurrentHP = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetHealthAttribute()); + // 데이터가 실제로 변할 때마다, GAS가 자동 호출 + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetHealthAttribute()).AddUObject(this, &UMJEnemyHPBar::OnHealthChanged); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxHealthAttribute()).AddUObject(this, &UMJEnemyHPBar::OnMaxHealthChanged); + + InitializeWidget(); +} + +void UMJEnemyHPBar::InitializeWidget() +{ + // 초기화 + CurrentPercent = (MaxHP > 0.f) ? CurrentHP / MaxHP : 0.f; + TargetPercent = (MaxHP > 0.f) ? CurrentHP / MaxHP : 0.f; + if (HPBar) + { + HPBar->SetPercent(CurrentPercent); + } +} + +void UMJEnemyHPBar::OnHealthChanged(const FOnAttributeChangeData& Data) +{ + CurrentHP = Data.NewValue; + TargetPercent = (MaxHP > 0.f) ? CurrentHP / MaxHP : 0.f; +} + +void UMJEnemyHPBar::OnMaxHealthChanged(const FOnAttributeChangeData& Data) +{ + MaxHP = Data.NewValue; + TargetPercent = (MaxHP > 0.f) ? CurrentHP / MaxHP : 0.f; +} + +void UMJEnemyHPBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + if (HPBar->GetPercent() >= 0.f) // 어차피 죽으면 tick도 멈출거 같긴 한데 일단 안전하게 넣어봅니다 + { + Super::NativeTick(MyGeometry, InDeltaTime); + + if (FMath::Abs(CurrentPercent- TargetPercent) > KINDA_SMALL_NUMBER) + { + CurrentPercent = FMath::FInterpTo(CurrentPercent, TargetPercent, InDeltaTime, LerpSpeed); + CurrentPercent = CurrentPercent < 0.f ? -0.0001f : CurrentPercent; + if (HPBar) + { + HPBar->SetPercent(CurrentPercent); + } + } + } +} diff --git a/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.h b/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.h new file mode 100644 index 00000000..1af920e1 --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJEnemyHPBar.h @@ -0,0 +1,43 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystemComponent.h" +#include "Blueprint/UserWidget.h" +#include "MJEnemyHPBar.generated.h" + +class UMJCharacterAttributeSet; +class UMJAbilitySystemComponent; +class UProgressBar; +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJEnemyHPBar : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr HPBar; + + UPROPERTY() + float MaxHP; + + UPROPERTY() + float CurrentHP; + + float TargetPercent ; + float CurrentPercent ; + float LerpSpeed = 2.5f; // 보간 속도 + +public: + UFUNCTION() + void BindToAttributes(UMJAbilitySystemComponent* ASC, UMJCharacterAttributeSet* AttributeSet); + void InitializeWidget(); + + void OnHealthChanged(const FOnAttributeChangeData& Data); + void OnMaxHealthChanged(const FOnAttributeChangeData& Data); + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; +}; diff --git a/Source/ProjectMJ/UI/Bar/MJExperienceWidget.cpp b/Source/ProjectMJ/UI/Bar/MJExperienceWidget.cpp new file mode 100644 index 00000000..448ebd87 --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJExperienceWidget.cpp @@ -0,0 +1,73 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Bar/MJExperienceWidget.h" + +#include "NiagaraCommon.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "Character/MJPlayerCharacter.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/ProgressBar.h" +#include "Components/TextBlock.h" +#include "Kismet/GameplayStatics.h" + +void UMJExperienceWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + if (AMJPlayerCharacter* Player = Cast(UGameplayStatics::GetPlayerCharacter(this, 0))) + { + if (UMJPlayerStatComponent* StatComponent = Player->FindComponentByClass()) + { + StatComponent->OnExperienceChanged.AddDynamic(this, &UMJExperienceWidget::OnExperienceChanged); + InitializeWidget(StatComponent->GetNumerator(), StatComponent->GetDenominator()); + } + } +} + +void UMJExperienceWidget::InitializeWidget(float Current, float ExpForNextLevel) +{ + CurrentExp = Current; + MaxExp = ExpForNextLevel; + + CurrentPercent= (MaxExp > 0.f) ? CurrentExp / MaxExp : 0.f; + TargetPercent = (MaxExp > 0.f) ? CurrentExp / MaxExp : 0.f; + + if (ExpBar) + { + ExpBar->SetPercent(CurrentPercent); + } + if (Percent) + { + float percent = CurrentPercent * 100.f; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.1f%% ( %.0f / %.0f )"),percent, CurrentExp , MaxExp))); + } +} + +void UMJExperienceWidget::OnExperienceChanged(float Current, float ExpForNextLevel) +{ + CurrentExp = Current; + MaxExp = ExpForNextLevel; + + TargetPercent = (MaxExp > 0.f) ? CurrentExp / MaxExp : 0.f; + + if (Percent) + { + float percent = TargetPercent * 100.f; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.1f%% ( %.0f / %.0f )"),percent, CurrentExp , MaxExp))); + } +} + +void UMJExperienceWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + + if (FMath::Abs(CurrentPercent- TargetPercent) > KINDA_SMALL_NUMBER) + { + CurrentPercent = FMath::FInterpTo(CurrentPercent, TargetPercent, InDeltaTime, LerpSpeed); + if (ExpBar) + { + ExpBar->SetPercent(CurrentPercent); + } + } +} \ No newline at end of file diff --git a/Source/ProjectMJ/UI/Bar/MJExperienceWidget.h b/Source/ProjectMJ/UI/Bar/MJExperienceWidget.h new file mode 100644 index 00000000..76bd0a9a --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJExperienceWidget.h @@ -0,0 +1,51 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "AbilitySystemComponent.h" +#include "MJExperienceWidget.generated.h" + +class UTextBlock; +/** + * Class Description: HUD 경험치 바 + * Author: 이지수 + * Created Date: 2025.06.26 + * Last Modified By: + * Last Modified Date: + */ +class UProgressBar; +UCLASS() +class PROJECTMJ_API UMJExperienceWidget : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr ExpBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Percent; + + // 스르륵 게이지가 줄도록 하기 위한 변수 + float TargetPercent ; + float CurrentPercent ; + float LerpSpeed = 2.5f; // 보간 속도 + + float MaxExp; + float CurrentExp; + + +public: + UFUNCTION() + virtual void NativeConstruct() override; + + UFUNCTION() + void InitializeWidget(float Current, float ExpForNextLevel); + + UFUNCTION() + void OnExperienceChanged(float Current, float ExpForNextLevel); + + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; +}; diff --git a/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.cpp b/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.cpp new file mode 100644 index 00000000..11926faa --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.cpp @@ -0,0 +1,84 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Bar/MJHealthBarWidget.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Components/ProgressBar.h" +#include "Components/TextBlock.h" + +void UMJHealthBarWidget::BindToAttributes(UMJAbilitySystemComponent* ASC, UMJCharacterAttributeSet* AttributeSet) +{ + if (!ASC || !AttributeSet) + { + return; + } + + // Max는 base & current 같다. + MaxHealth = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxHealthAttribute()); + CurrentHealth = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetHealthAttribute()); + // 데이터가 실제로 변할 때마다, GAS가 자동 호출 + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetHealthAttribute()).AddUObject(this,&UMJHealthBarWidget::OnHealthChanged); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxHealthAttribute()).AddUObject(this,&UMJHealthBarWidget::OnMaxHealthChanged); + InitializeWidget(); +} + +void UMJHealthBarWidget::InitializeWidget() +{ + float per = (MaxHealth > 0.f) ? (CurrentHealth / MaxHealth) : 0.f; + // CurrentPercent = (MaxHealth > 0.f) ? (CurrentHealth / MaxHealth) : 0.f; + // CurrentPercent 대신 per를 쓰면 게임 시작할때 체력이 빠르게 차오르는 꼴을 볼 수 있다 + // 이유 : CurrentPercent = 0 이라서 NativeTick에서 0->TargetPercent까지 차오르는 거임 + // 이게 더 예뻐보여서 이렇게 했는데, 처음부터 꽉 찬 상태로 시작하고 싶으면 currentPercent 쓰면 된다. + + TargetPercent = (MaxHealth > 0.f) ? CurrentHealth / MaxHealth : 0.f; + if (HealthBar) + { + HealthBar->SetPercent(per); + } + if (Percent) + { + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentHealth, MaxHealth))); + } +} + +void UMJHealthBarWidget::OnHealthChanged(const FOnAttributeChangeData& Data) +{ + CurrentHealth = Data.NewValue; // 바뀐 데이터로 갱신할 수 있는? 근데 이게 있으면 시작할 땐 currentHealth가 0이 되넴 + TargetPercent = (MaxHealth > 0.f) ? CurrentHealth / MaxHealth : 0.f; // 바 퍼센트 갱신 + + if (Percent) + { + CurrentHealth = CurrentHealth < 0.f ? 0.f : CurrentHealth; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentHealth, MaxHealth))); + } +} + +void UMJHealthBarWidget::OnMaxHealthChanged(const FOnAttributeChangeData& Data) +{ + MaxHealth = Data.NewValue; + TargetPercent = (MaxHealth > 0.f) ? CurrentHealth / MaxHealth : 0.f; + + if (Percent) + { + CurrentHealth = CurrentHealth < 0.f ? 0.f : CurrentHealth; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentHealth, MaxHealth))); + } +} + +void UMJHealthBarWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + if (HealthBar->GetPercent() >= 0.f) // 더이상 tick이 돌지않도록 가라로.. + { + Super::NativeTick(MyGeometry, InDeltaTime); + if (FMath::Abs(CurrentPercent- TargetPercent) > KINDA_SMALL_NUMBER) + { + CurrentPercent = FMath::FInterpTo(CurrentPercent, TargetPercent, InDeltaTime, LerpSpeed); + CurrentPercent = CurrentPercent < 0.f ? -0.0001f : CurrentPercent; + if (HealthBar) + { + HealthBar->SetPercent(CurrentPercent); + } + } + } +} diff --git a/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.h b/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.h new file mode 100644 index 00000000..c65f16aa --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJHealthBarWidget.h @@ -0,0 +1,48 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "AbilitySystemComponent.h" +#include "MJHealthBarWidget.generated.h" + +/** +* Class Description: HUD 체력바 + * Author: 이지수 + * Created Date: 2025.06.26 + * Last Modified By: + * Last Modified Date: + */ + +class UTextBlock; +class UProgressBar; + +UCLASS() +class PROJECTMJ_API UMJHealthBarWidget : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr HealthBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Percent; + + float TargetPercent; + float CurrentPercent; + float LerpSpeed = 2.5f; // 보간 속도 + + float MaxHealth; + float CurrentHealth; + +public: + UFUNCTION() + void BindToAttributes(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet); + void InitializeWidget(); + + void OnHealthChanged(const FOnAttributeChangeData& Data); + void OnMaxHealthChanged(const FOnAttributeChangeData& Data); + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; +}; diff --git a/Source/ProjectMJ/UI/Bar/MJManaBarWidget.cpp b/Source/ProjectMJ/UI/Bar/MJManaBarWidget.cpp new file mode 100644 index 00000000..450aef39 --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJManaBarWidget.cpp @@ -0,0 +1,82 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Bar/MJManaBarWidget.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Components/ProgressBar.h" +#include "Components/TextBlock.h" + +void UMJManaBarWidget::BindToAttributes(class UMJAbilitySystemComponent* ASC, + class UMJCharacterAttributeSet* AttributeSet) +{ + if (!ASC || !AttributeSet) + { + return; + } + + MaxMana = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxManaAttribute()); + CurrentMana= ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetManaAttribute()); + // 데이터가 실제로 변할 때마다, GAS가 자동 호출 + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetManaAttribute()).AddUObject(this,&UMJManaBarWidget::OnManaChanged); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxManaAttribute()).AddUObject(this,&UMJManaBarWidget::OnMaxManaChanged); + InitializeWidget(); +} + +void UMJManaBarWidget::InitializeWidget() +{ + float per = (MaxMana > 0.f) ? CurrentMana / MaxMana : 0.f; + TargetPercent = (MaxMana > 0.f) ? CurrentMana / MaxMana : 0.f; + if (ManaBar) + { + ManaBar->SetPercent(per); + } + if (Percent) + { + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentMana, MaxMana))); + } +} + +void UMJManaBarWidget::OnManaChanged(const FOnAttributeChangeData& Data) +{ + CurrentMana = Data.NewValue; + + TargetPercent = (MaxMana > 0.f) ? CurrentMana / MaxMana : 0.f; + + if (Percent) + { + CurrentMana = CurrentMana < 0.f ? 0.f : CurrentMana; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentMana, MaxMana))); + } +} + +void UMJManaBarWidget::OnMaxManaChanged(const FOnAttributeChangeData& Data) +{ + MaxMana = Data.NewValue; + + TargetPercent = (MaxMana > 0.f) ? CurrentMana / MaxMana : 0.f; + + if (Percent) + { + CurrentMana = CurrentMana < 0.f ? 0.f : CurrentMana; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentMana, MaxMana))); + } +} + +void UMJManaBarWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + if (ManaBar->GetPercent() >= 0.f) + { + Super::NativeTick(MyGeometry, InDeltaTime); + + if (FMath::Abs(CurrentPercent- TargetPercent) > KINDA_SMALL_NUMBER) + { + CurrentPercent = FMath::FInterpTo(CurrentPercent, TargetPercent, InDeltaTime, LerpSpeed); + CurrentPercent = CurrentPercent < 0.f ? -0.0001f : CurrentPercent; + if (ManaBar) + { + ManaBar->SetPercent(CurrentPercent); + } + } + } +} diff --git a/Source/ProjectMJ/UI/Bar/MJManaBarWidget.h b/Source/ProjectMJ/UI/Bar/MJManaBarWidget.h new file mode 100644 index 00000000..06f02e8d --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJManaBarWidget.h @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "AbilitySystemComponent.h" +#include "MJManaBarWidget.generated.h" + +class UTextBlock; +/** +* Class Description: HUD 마나바 + * Author: 이지수 + * Created Date: 2025.06.26 + * Last Modified By: + * Last Modified Date: + */ +class UProgressBar; + +UCLASS() +class PROJECTMJ_API UMJManaBarWidget : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr ManaBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Percent; + + float TargetPercent; + float CurrentPercent; + float LerpSpeed = 2.5f; + + float MaxMana; + float CurrentMana; + +public: + UFUNCTION() + void BindToAttributes(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet); + void InitializeWidget(); + void OnManaChanged(const FOnAttributeChangeData& Data); + void OnMaxManaChanged(const FOnAttributeChangeData& Data); + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; +}; + diff --git a/Source/ProjectMJ/UI/Bar/MJStaminaBar.cpp b/Source/ProjectMJ/UI/Bar/MJStaminaBar.cpp new file mode 100644 index 00000000..b4bfdc8a --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJStaminaBar.cpp @@ -0,0 +1,85 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Bar/MJStaminaBar.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Components/ProgressBar.h" +#include "Components/TextBlock.h" +#include "Math/UnitConversion.h" + + +void UMJStaminaBar::BindToAttributes(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet) +{ + if (!ASC || !AttributeSet) + { + return; + } + + MaxStamina = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxStaminaAttribute()); + CurrentStamina = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetStaminaAttribute()); + // 데이터가 실제로 변할 때마다, GAS가 자동 호출 + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetStaminaAttribute()).AddUObject(this,&UMJStaminaBar::OnStaminaChanged); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxStaminaAttribute()).AddUObject(this,&UMJStaminaBar::OnMaxStaminaChanged); + + InitializeWidget(); +} + +void UMJStaminaBar::InitializeWidget() +{ + // 아래 두줄 추가한 이유 : 무작정 1.f로 해놓으면 나중에 게임 다시 켰을 때 체력이 자동회복되어있을까봐 + float per = (MaxStamina > 0.f) ? CurrentStamina / MaxStamina : 0.f; + TargetPercent = (MaxStamina > 0.f) ? CurrentStamina / MaxStamina : 0.f; + if (StaminaBar) + { + StaminaBar->SetPercent(per); + } + if (Percent) + { + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentStamina, MaxStamina))); + } +} + +void UMJStaminaBar::OnStaminaChanged(const FOnAttributeChangeData& Data) +{ + CurrentStamina = Data.NewValue; + + TargetPercent = (MaxStamina > 0.f) ? CurrentStamina / MaxStamina : 0.f; + + if (Percent) + { + CurrentStamina = CurrentStamina < 0.f ? 0.f : CurrentStamina; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentStamina, MaxStamina))); + } +} + +void UMJStaminaBar::OnMaxStaminaChanged(const FOnAttributeChangeData& Data) +{ + MaxStamina = Data.NewValue; + + TargetPercent = (MaxStamina > 0.f) ? CurrentStamina / MaxStamina : 0.f; + + if (Percent) + { + CurrentStamina = CurrentStamina < 0.f ? 0.f : CurrentStamina; + Percent->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), CurrentStamina, MaxStamina))); + } +} + +void UMJStaminaBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + if (StaminaBar->GetPercent() >= 0.f) + { + Super::NativeTick(MyGeometry, InDeltaTime); + + if (FMath::Abs(CurrentPercent- TargetPercent) > KINDA_SMALL_NUMBER) + { + CurrentPercent = FMath::FInterpTo(CurrentPercent, TargetPercent, InDeltaTime, LerpSpeed); + CurrentPercent = CurrentPercent < 0.f ? -0.0001f : CurrentPercent; + if (StaminaBar) + { + StaminaBar->SetPercent(CurrentPercent); + } + } + } +} diff --git a/Source/ProjectMJ/UI/Bar/MJStaminaBar.h b/Source/ProjectMJ/UI/Bar/MJStaminaBar.h new file mode 100644 index 00000000..5d73d9c4 --- /dev/null +++ b/Source/ProjectMJ/UI/Bar/MJStaminaBar.h @@ -0,0 +1,42 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "AbilitySystemComponent.h" +#include "MJStaminaBar.generated.h" + +/** + * + */ +class UTextBlock; +class UProgressBar; +UCLASS() +class PROJECTMJ_API UMJStaminaBar : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr StaminaBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Percent; + + float TargetPercent; + float CurrentPercent; + float LerpSpeed = 2.5f; + + float MaxStamina; + float CurrentStamina; + + +public: + UFUNCTION() + void BindToAttributes(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet); + void InitializeWidget(); + void OnStaminaChanged(const FOnAttributeChangeData& Data); + void OnMaxStaminaChanged(const FOnAttributeChangeData& Data); + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; +}; diff --git a/Source/ProjectMJ/UI/Component/MJHealthBarComponent.cpp b/Source/ProjectMJ/UI/Component/MJHealthBarComponent.cpp new file mode 100644 index 00000000..18d2807d --- /dev/null +++ b/Source/ProjectMJ/UI/Component/MJHealthBarComponent.cpp @@ -0,0 +1,27 @@ +// ThenOneDayStudio + + +#include "UI/Component/MJHealthBarComponent.h" + +#include "UI/Bar/MJEnemyHPBar.h" + +UMJHealthBarComponent::UMJHealthBarComponent() +{ + static ConstructorHelpers::FClassFinder HealthBarWidgetRef(TEXT("/Game/UI/WBP/World/Bar/WBP_EnemyHPBar.WBP_EnemyHPBar_C")); + + if (HealthBarWidgetRef.Class) + { + SetWidgetClass(HealthBarWidgetRef.Class); + } + + SetHPBarWidget(); +} + +void UMJHealthBarComponent::SetHPBarWidget() +{ + SetRelativeLocation(FVector(0.0f, 0.0f, 220.0f)); + SetWidgetSpace(EWidgetSpace::Screen); + SetDrawSize(FVector2D(100.0f,10.0f)); + SetCollisionEnabled(ECollisionEnabled::NoCollision); + SetVisibility(false); +} \ No newline at end of file diff --git a/Source/ProjectMJ/UI/Component/MJHealthBarComponent.h b/Source/ProjectMJ/UI/Component/MJHealthBarComponent.h new file mode 100644 index 00000000..c8ae4789 --- /dev/null +++ b/Source/ProjectMJ/UI/Component/MJHealthBarComponent.h @@ -0,0 +1,21 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Components/WidgetComponent.h" +#include "MJHealthBarComponent.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJHealthBarComponent : public UWidgetComponent +{ + GENERATED_BODY() + + UMJHealthBarComponent(); + +public: + void SetHPBarWidget(); +}; diff --git a/Source/ProjectMJ/UI/Component/MJInteractComponent.cpp b/Source/ProjectMJ/UI/Component/MJInteractComponent.cpp new file mode 100644 index 00000000..f723c43f --- /dev/null +++ b/Source/ProjectMJ/UI/Component/MJInteractComponent.cpp @@ -0,0 +1,159 @@ +// ThenOneDayStudio + +#include "UI/Component/MJInteractComponent.h" + +#include "Dialogue/MJDialogueChoiceWidget.h" +#include "Dialogue/MJDialogueComponent.h" +#include "Dialogue/MJDialogueWidget.h" +#include "TG/Actor/MJSavePointActor.h" +#include "UI/Store/MJStoreComponent.h" +#include "UI/World/MJInteractionComponent.h" + +UMJInteractComponent::UMJInteractComponent() +{ + static ConstructorHelpers::FObjectFinder MetarialRef(TEXT("/Game/UI/WBP/HUD/Inventory/woodcover.woodcover")); + if (MetarialRef.Succeeded()) + { + OverlayMaterial = MetarialRef.Object; + } + + WidgetComponent = CreateDefaultSubobject(TEXT("InteractionWidget")); +} + +void UMJInteractComponent::OnBeginInteract() +{ + if (UMeshComponent* Mesh = GetOwner()->FindComponentByClass()) + { + Mesh->SetOverlayMaterial(OverlayMaterial); + if (WidgetComponent) + { + WidgetComponent->AttachToComponent(Mesh,FAttachmentTransformRules::KeepRelativeTransform); + WidgetComponent->Active("F"); + } + } + + EvaluateType(); + + if (StoreComponent) + { + StoreComponent->BindButtons(); + } + +} + +void UMJInteractComponent::OnEndInteract() +{ + if (UMeshComponent* Mesh = GetOwner()->FindComponentByClass()) + { + Mesh->SetOverlayMaterial(nullptr); + WidgetComponent->Deactive(); + } +} + +void UMJInteractComponent::EvaluateType() +{ + CurrentType = EMJInteractionType::None; + + for (UActorComponent* Comp : GetOwner()->GetComponents()) + { + if (Cast(Comp)) + { + StoreComponent = Cast(Comp); + CurrentType = EMJInteractionType::Store; + return; + } + + if (Cast(Comp)) + { + DialogueComponent = Cast(Comp); + CurrentType = EMJInteractionType::Dialogue; + return; + } + // CTG : add Interact Logic 25.08.05 + if (GetOwner()->IsA(AMJInteractableActor::StaticClass())) + { + CurrentType = EMJInteractionType::Interactable; + return; + } + } +} + + +void UMJInteractComponent::StartInteraction() +{ + // execute logic + if (CurrentType == EMJInteractionType::Dialogue) + { + DialogueComponent->FloatLine(); + return; + } + else if (CurrentType == EMJInteractionType::Store) + { + StoreComponent->FloatLine(); + StoreComponent->GetDialogueWidget()->GetDialogueChoiceWidget()->SetVisibility(ESlateVisibility::Visible); + return; + } + else if (CurrentType == EMJInteractionType::Interactable) + { + if (AMJInteractableActor* Interactable = Cast(GetOwner())) + { + Interactable->Execute(); + } + } +} + +void UMJInteractComponent::ProceedInteraction() +{ + // if have any procedure, called. + if (CurrentType == EMJInteractionType::Dialogue) + { + DialogueComponent->TurnOver(); + + if (DialogueComponent->IsDialogueEnd()) + { + DialogueComponent->SetIndex(0); + OndialogueEnd.Broadcast(); // 컨트롤러에서 다이어로그 꺼지는 함수 실행될거임 + } + } + + if (CurrentType == EMJInteractionType::Store) + { + if (StoreComponent->bIsFirstIndex()) + { + StoreComponent->SkipTyping(); + if (StoreComponent->GetIsOpened()) // 열려있으니까 + { + OnstoreClose.Broadcast(); // 닫을거임 + StoreComponent->SetbIsOpened(false); + StoreComponent->SetIndex(0); + return; + } + return; + } + + StoreComponent->TurnOver(); + + if (StoreComponent->IsDialogueEnd()) + { + if (StoreComponent->GetIsStoreRoot()) + { + StoreComponent->DialogueEnd();// 이 함수안에서 IsOpened true가 되 + StoreComponent->UpdateStore(); + OnstoreOpen.Broadcast(); //컨트롤러에서 다이어로그는 꺼지고, 스토어는 열리는 ui 함수가 호출될거임 + StoreComponent->SetbIsStoreRoot(false); + } + else + { + StoreComponent->SetIndex(0); + OndialogueEnd.Broadcast(); + } + return; + } + + + } +} + + + + diff --git a/Source/ProjectMJ/UI/Component/MJInteractComponent.h b/Source/ProjectMJ/UI/Component/MJInteractComponent.h new file mode 100644 index 00000000..8d8fabff --- /dev/null +++ b/Source/ProjectMJ/UI/Component/MJInteractComponent.h @@ -0,0 +1,70 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "MJInteractComponent.generated.h" +/** + * Class Description: 상호작용이 필요한 액터에게 심어주는 컴포넌트 예 NPC - Dialogue, Store 등 + * Author: 이지수 + * Created Date: 2025_07-23 + * Last Modified By: Lee JuHyeon + * Last Modified Date: Add Using Func from BP + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDialogueEnd); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStoreOpen); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStoreClose); + +UENUM(BlueprintType) +enum class EMJInteractionType : uint8 +{ + None, + Dialogue, + Store, + Interactable +}; + +class UMJInteractionComponent; +class UMJDialogueComponent; +class UMJStoreComponent; +class UInputAction; + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJInteractComponent : public UActorComponent +{ + GENERATED_BODY() + +protected: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Component") + TObjectPtr WidgetComponent; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Overlay") + TObjectPtr OverlayMaterial; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Component") + TObjectPtr DialogueComponent; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Component") + TObjectPtr StoreComponent; + + +public: + UMJInteractComponent(); + + void OnBeginInteract(); + void OnEndInteract(); + void EvaluateType(); + void StartInteraction(); + void ProceedInteraction(); + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly) + EMJInteractionType CurrentType; + + FOnDialogueEnd OndialogueEnd; + FOnStoreOpen OnstoreOpen; + FOnStoreClose OnstoreClose; + + UMJStoreComponent* GetStoreComponent() {return StoreComponent;} +}; \ No newline at end of file diff --git a/Source/ProjectMJ/UI/Inventory/ItemDataRow.h b/Source/ProjectMJ/UI/Inventory/ItemDataRow.h new file mode 100644 index 00000000..4a55f0aa --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/ItemDataRow.h @@ -0,0 +1,73 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "GameplayTagContainer.h" +#include "ItemDataRow.generated.h" +class AMJItemBase; +/** + * Class Description: Item DataTable Base + * Author: 이지수 + * Created Date: ? + * Last Modified By: 이지수 + * Last Modified Date: 2025.07.15 / Add + * Last Modified By: 김민진 + * Last Modified Date: 2025.07.29 / Add ItemClass + */ +UENUM(BlueprintType) +enum class EItemType : uint8 +{ + None UMETA(DisplayName = "None"), + Weapon UMETA(DisplayName = "Weapon"), + Armor UMETA(DisplayName = "Armor"), + Consumable UMETA(DisplayName = "Consumable"), + Material UMETA(DisplayName = "Material"), + Quest UMETA(DisplayName = "Quest") +}; + +UENUM(BlueprintType) +enum class EItemRarity : uint8 +{ + Common UMETA(DisplayName = "Common"), + Uncommon UMETA(DisplayName = "Uncommon"), + Rare UMETA(DisplayName = "Rare"), + Epic UMETA(DisplayName = "Epic"), + Legendary UMETA(DisplayName = "Legendary") +}; + +USTRUCT(BlueprintType) +struct FItemDataRow : public FTableRowBase // 안바뀌는값 // 아이템 고유 정보 +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Item")) + FGameplayTag ItemTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Monster")) + FGameplayTag DropMonsterTag; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText ItemID; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + EItemType ItemType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + EItemRarity Rarity; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UTexture2D* Icon; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText Description; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 Price; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool IsMerchandise; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ItemClass; + +}; diff --git a/Source/ProjectMJ/UI/Inventory/MJDragWidget.cpp b/Source/ProjectMJ/UI/Inventory/MJDragWidget.cpp new file mode 100644 index 00000000..65187414 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJDragWidget.cpp @@ -0,0 +1,18 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Inventory/MJDragWidget.h" +#include "Components/Image.h" + +void UMJDragWidget::SetDragImage(UTexture2D* ItemTexture) +{ + { + if (DragImage && ItemTexture) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + Brush.ImageSize = FVector2D(80,80); + DragImage->SetBrush(Brush); + } + } +} diff --git a/Source/ProjectMJ/UI/Inventory/MJDragWidget.h b/Source/ProjectMJ/UI/Inventory/MJDragWidget.h new file mode 100644 index 00000000..cc48c184 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJDragWidget.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJDragWidget.generated.h" + +/** +* Class Description: 인벤토리 내 슬롯을 집으면 뜨는 드래그 위젯 + * Author: 이지수 + * Created Date: 2025. + * Last Modified By: + * Last Modified Date: + */ +class UImage; +UCLASS() +class PROJECTMJ_API UMJDragWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr DragImage; + +public: + void SetDragImage(UTexture2D* ItemTexture); + +}; diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.cpp b/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.cpp new file mode 100644 index 00000000..4096a984 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.cpp @@ -0,0 +1,170 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Inventory/MJInventoryComponent.h" +#include "ItemDataRow.h" +#include "MJInventorySlot.h" +#include "MJInventoryWidget.h" +#include "TG/MJGameInstance.h" +#include "UI/MJHUDWidget.h" +#include "UI/MJUIManagerSubsystem.h" +#include "UI/Store/MJStoreComponent.h" +#include "UI/Store/MJStoreWidget.h" + +UMJInventoryComponent::UMJInventoryComponent() +{ + ItemEmptyData.ItemTag = FGameplayTag::EmptyTag; + ItemEmptyData.ItemCount = 0; +} +void UMJInventoryComponent::PickUpItem(FGameplayTag ItemTag, int32 Count, UMJStoreWidget* StoreWidget) +{ +#pragma region InventoryRef + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->ItemDataTable) + { + UE_LOG(LogTemp,Error,TEXT("게임인스턴스를 제대로 못 받아왔나보다")); + return; + // 못 받아왔던 이유 -> GameInstance를 다른 걸 쓰고 있었음.. 프로젝트 세팅에서 수정 잘하자 + } + + UMJUIManagerSubsystem* UIManager = GI->GetSubsystem(); + if (!UIManager) + { + return; // 잘 받아오는거 확인 완료 + } + + TArray InventorySlot = UIManager->GetHUDWidget()->GetInventoryWidget()->GetInventorySlot(); + if (!UIManager->GetHUDWidget()) + { + return; + } + if (!UIManager->GetHUDWidget()->GetInventoryWidget()) + { + return; + } + for (int i = 0; i < InventorySlot.Num(); i++) + { + if (!InventorySlot[i]) + { + return; + } + } +#pragma endregion + if (Count == 0) + { + return; + } + + if (ItemInInventory.Contains(ItemTag)) + { + ItemInInventory[ItemTag].ItemCount += Count; + } + else + { + if (StoreWidget) + { + StoreWidget->UpdateInventorySlot(); + } + + FInventoryItemData NewItemData; + NewItemData.ItemTag = ItemTag; + NewItemData.ItemCount = Count; + + // for문 순회를 통해 공백을 만나면 그 위치를 반환해서 넣기 + for (int i = 0; i < InventorySlot.Num(); i++) + { + if (!InventorySlot[i]->bIsOccupied) + { + NewItemData.Position = InventorySlot[i]->SlotPosition; + InventorySlot[i]->SetIsOccupied(true); + break; + } + } + // if (NewItemData.Position == -1) + // { + // UE_LOG(LogTemp,Error,TEXT("자리가 부족합니다")); + // return; + // } + + ItemInInventory.Add(ItemTag, NewItemData); + ItemTags.Add(ItemTag); + } + UpdateSlot(ItemTag); +} + +void UMJInventoryComponent::DropItem(FGameplayTag ItemTag, int32 count) +{ + ItemInInventory[ItemTag].ItemCount -= count; + if (ItemInInventory[ItemTag].ItemCount <= 0) + { + ItemEmptyData.Position = ItemInInventory[ItemTag].Position; + UpdateSlot(ItemEmptyData.ItemTag); + ItemInInventory.Remove(ItemTag); + return; + } + + UpdateSlot(ItemTag); +} + +void UMJInventoryComponent::UpdateSlot(FGameplayTag ItemTag) +{ +#pragma region Inventory + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI || !GI->ItemDataTable) + { + UE_LOG(LogTemp,Error,TEXT("게임인스턴스를 제대로 못 받아왔나보다")); + return; + // 못 받아왔던 이유 -> GameInstance를 다른 걸 쓰고 있었음.. 프로젝트 세팅에서 수정 잘하자 + } + + UMJUIManagerSubsystem* UIManager = GI->GetSubsystem(); + if (!UIManager) + { + return; // 잘 받아오는거 확인 완료 + } + + TArray InventorySlot = UIManager->GetHUDWidget()->GetInventoryWidget()->GetInventorySlot(); + if (!UIManager->GetHUDWidget()) + { + return; + } + if (!UIManager->GetHUDWidget()->GetInventoryWidget()) + { + return; + } + for (int i = 0; i < InventorySlot.Num(); i++) + { + if (!InventorySlot[i]) + { + return; + } + } +#pragma endregion inventory + if (ItemTag == ItemEmptyData.ItemTag) + { + InventorySlot[ItemEmptyData.Position]->SetInventoryItemData({FGameplayTag::EmptyTag, 0, ItemEmptyData.Position}); + InventorySlot[ItemEmptyData.Position]->SetItemCount(0); + InventorySlot[ItemEmptyData.Position]->SetText(FText::GetEmpty()); + InventorySlot[ItemEmptyData.Position]->SetImage(nullptr); + + return; + } + + InventorySlot[ItemInInventory[ItemTag].Position]->SetInventoryItemData(ItemInInventory[ItemTag]); + InventorySlot[ItemInInventory[ItemTag].Position]->SetItemData(*GI->ItemDataTable->FindRow(ItemTag.GetTagName(),TEXT("Inventory"))); + + InventorySlot[ItemInInventory[ItemTag].Position]->SetItemCount(ItemInInventory[ItemTag].ItemCount); + InventorySlot[ItemInInventory[ItemTag].Position]->SetText(GI->ItemDataTable->FindRow(ItemTag.GetTagName(),TEXT("Inventory"))->ItemID); + InventorySlot[ItemInInventory[ItemTag].Position]->SetImage(GI->ItemDataTable->FindRow(ItemTag.GetTagName(),TEXT("Inventory"))->Icon); + + +} + +void UMJInventoryComponent::ZeroItem(FGameplayTag ItemTag) +{ + if (ItemInInventory[ItemTag].ItemCount == 0) + { + ItemTag = FGameplayTag::EmptyTag; + } + +} diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.h b/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.h new file mode 100644 index 00000000..d63ad6e5 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryComponent.h @@ -0,0 +1,70 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "GameplayTagContainer.h" +#include "MJInventoryComponent.generated.h" + +class UMJStoreComponent; +class UMJStoreWidget; +/** +* Class Description: 인벤토리 컴포넌트 / 캐릭터가 들고 있음 / 인벤토리 위젯의 아이템 정보를 갱신하는 역할 + * Author: 이지수 + * Created Date: 2025.06.26 + * Last Modified By: + * Last Modified Date: + */ +USTRUCT(BlueprintType) +struct FInventoryItemData // 변할 수 있는 데이터값 // 인벤토리 아이템의 정보 +{ + GENERATED_BODY() + + UPROPERTY() + FGameplayTag ItemTag; + + // UPROPERTY() + // FName ItemName; + + UPROPERTY() + int32 ItemCount = 0; + + UPROPERTY() + int32 Position = -1; + + bool IsEmpty() const + { + return ItemCount == 0; + } +}; + +//class UMJInventorySlot; +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJInventoryComponent : public UActorComponent +{ + GENERATED_BODY() + + UMJInventoryComponent(); + +protected: + int32 Position; + + FInventoryItemData ItemEmptyData; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") + TMap ItemInInventory; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") + TArray ItemTags; +public: + void PickUpItem(FGameplayTag ItemTag, int32 Count, UMJStoreWidget* StoreWidget = nullptr); + void DropItem(FGameplayTag ItemTag, int32 count); + void UpdateSlot(FGameplayTag ItemTag); + + void SetPosition(FGameplayTag ItemTag, int32 NewPosition) {ItemInInventory[ItemTag].Position = NewPosition;}; + + void ZeroItem(FGameplayTag ItemTag); + TMap GetItemInInventory() {return ItemInInventory;} + TArray GetItemTags() {return ItemTags;} +}; diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryDragDropOperation.h b/Source/ProjectMJ/UI/Inventory/MJInventoryDragDropOperation.h new file mode 100644 index 00000000..6a3918ba --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryDragDropOperation.h @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJInventoryComponent.h" +#include "Blueprint/DragDropOperation.h" +#include "ItemDataRow.h" +#include "MJInventoryDragDropOperation.generated.h" +/** + * + */ +class UMJInventorySlot; +UCLASS() +class PROJECTMJ_API UMJInventoryDragDropOperation : public UDragDropOperation +{ + GENERATED_BODY() + +public: + UPROPERTY() + UMJInventorySlot* SourceSlot; + + UPROPERTY() + FInventoryItemData InventoryItemData; + + UPROPERTY() + FItemDataRow ItemData; +}; diff --git a/Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.cpp b/Source/ProjectMJ/UI/Inventory/MJInventoryInterface.cpp similarity index 51% rename from Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.cpp rename to Source/ProjectMJ/UI/Inventory/MJInventoryInterface.cpp index 2cd6adb2..10787e64 100644 --- a/Source/ProjectMJ/TG/Interface/MJBossEventManagerTG.cpp +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryInterface.cpp @@ -1,6 +1,6 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "TG/Interface/MJBossEventManagerTG.h" +#include "UI/Inventory/MJInventoryInterface.h" -// Add default functionality here for any IMJBossEventManagerTG functions that are not pure virtual. +// Add default functionality here for any IMJInventoryInterface functions that are not pure virtual. diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryInterface.h b/Source/ProjectMJ/UI/Inventory/MJInventoryInterface.h new file mode 100644 index 00000000..7486cb78 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryInterface.h @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "MJInventoryInterface.generated.h" + +// This class does not need to be modified. +class UMJInventoryComponent; + +UINTERFACE(MinimalAPI) +class UMJInventoryInterface : public UInterface +{ + GENERATED_BODY() +}; + +/** + * + */ +class PROJECTMJ_API IMJInventoryInterface +{ + GENERATED_BODY() + + // Add interface functions to this class. This is the class that will be inherited to implement this interface. +public: + virtual UMJInventoryComponent* GetInventoryComponent() = 0; +}; diff --git a/Source/ProjectMJ/UI/Inventory/MJInventorySlot.cpp b/Source/ProjectMJ/UI/Inventory/MJInventorySlot.cpp new file mode 100644 index 00000000..58ea3e8b --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventorySlot.cpp @@ -0,0 +1,197 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Inventory/MJInventorySlot.h" +#include "MJDragWidget.h" +#include "MJInventoryDragDropOperation.h" +#include "Components/Border.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" +#include "MJInventoryInterface.h" +#include "ProjectMJ.h" + +void UMJInventorySlot::NativeConstruct() +{ + Super::NativeConstruct(); + + Texture = nullptr; + InventoryData = { FGameplayTag::EmptyTag, 0, SlotPosition }; // 공백아이템 + + DefaultBorderColor = {0.0f,0.0f,0.0f,0.2f}; + ClickedBorderColor = {0.0f,0.0f,0.0f,0.1f}; + Border->SetBrushColor(DefaultBorderColor); + + // Tooltip = CreateWidget(this,TooltipWidgetClass); +} + +void UMJInventorySlot::SetImage(UTexture2D* ItemTexture) +{ + //this->Texture = ItemTexture; + if (Image) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + Brush.ImageSize = FVector2D(40,40); + Image->SetBrush(Brush); + Image->SetOpacity(1.0); // 이미지 들어가기전에 자꾸 인벤배경색(반투명)을 가려서 에디터에서 0으로 해놓은거 먹으면 밝혀주는 용도 + } + if (!ItemTexture) + { + Image->SetOpacity(0.0); + } +} + +void UMJInventorySlot::SetText(FText newtext) +{ + if (Text) + { + Text->SetText(newtext); + } +} + +FReply UMJInventorySlot::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) +{ + if (!InventoryData.IsEmpty()) + { + if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) + { + if (Border) + { + Border->SetBrushColor(ClickedBorderColor); + } + return FReply::Handled().DetectDrag(TakeWidget(), EKeys::LeftMouseButton); + } + } + + return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent); +} + +FReply UMJInventorySlot::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) +{ + if (Border) + { + Border->SetBrushColor(DefaultBorderColor); + } + return Super::NativeOnMouseButtonUp(InGeometry, InMouseEvent); +} + +void UMJInventorySlot::NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, + UDragDropOperation*& OutOperation) // Jisoo : 드래그 시 작동하는 함수 +{ + UMJInventoryDragDropOperation* DragOperation = NewObject(); + DragOperation->SourceSlot = this; + DragOperation->InventoryItemData = InventoryData; + DragOperation->ItemData = ItemData; + + UMJDragWidget* DragWidget = CreateWidget(GetWorld(),DragWidgetClass); + if (DragWidget) + { + DragWidget->SetDragImage(DragOperation->ItemData.Icon); + DragOperation->DefaultDragVisual = DragWidget; + } + + OutOperation = DragOperation; // jisoo : 내가 만든 드래그 시스템의 포인터를 할당하는 것. +} + +bool UMJInventorySlot::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, + UDragDropOperation* InOperation) +{ + ScreenPos = InDragDropEvent.GetScreenSpacePosition(); + + MJ_LOG(LogMJ, Error, TEXT("In %f %f"),ScreenPos.X,ScreenPos.Y ); + UMJInventoryDragDropOperation* DragOperation = Cast(InOperation); + + if (!DragOperation) + { + return false; + } + + if (DragOperation && DragOperation->SourceSlot && DragOperation->SourceSlot != this) + { + if (!InventoryData.IsEmpty()) + { + FInventoryItemData Temp = InventoryData; + FItemDataRow Temp2 = ItemData; + + // 드래그 받는 슬롯의 정보를 갱신 + InventoryData = DragOperation->InventoryItemData; + ItemData = DragOperation->ItemData; + + // 드래그 눌렸던 슬롯의 정보를 갱신 + DragOperation->SourceSlot->SetInventoryItemData(Temp); + DragOperation->SourceSlot->SetItemData(Temp2); + + //바뀐 정보를 바탕으로 UI 갱신 + SetImage(ItemData.Icon); + SetText(ItemData.ItemID); + SetItemCount(InventoryData.ItemCount); + DragOperation->SourceSlot->SetImage(Temp2.Icon); + DragOperation->SourceSlot->SetText(Temp2.ItemID); + DragOperation->SourceSlot->SetItemCount(Temp.ItemCount); + } + else // 공백칸과의 교환일 시 + { + InventoryData = DragOperation->InventoryItemData; + ItemData = DragOperation->ItemData; + + DragOperation->SourceSlot->SetInventoryItemData({ FGameplayTag::EmptyTag, 0, SlotPosition }); + + SetImage(ItemData.Icon); + SetText(ItemData.ItemID); + SetItemCount(InventoryData.ItemCount); + bIsOccupied = true; + + DragOperation->SourceSlot->SetImage(nullptr); + DragOperation->SourceSlot->SetText(FText::GetEmpty()); + DragOperation->SourceSlot->ItemCount->SetText(FText::GetEmpty()); + DragOperation->SourceSlot->bIsOccupied = false; + } + InventoryData.Position = SlotPosition; + DragOperation->SourceSlot->InventoryData.Position = DragOperation->SourceSlot->SlotPosition; + + IMJInventoryInterface* Char = Cast(GetOwningPlayerPawn()); + if (Char) + { + Char->GetInventoryComponent()->SetPosition(InventoryData.ItemTag,InventoryData.Position); + UE_LOG(LogTemp, Log, TEXT("SlotPosition : %d"), InventoryData.Position); + } + Border->SetBrushColor(DefaultBorderColor); + return true; + } + Border->SetBrushColor(DefaultBorderColor); + + return false; +} + +void UMJInventorySlot::NativeOnDragEnter(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, + UDragDropOperation* InOperation) +{ + Super::NativeOnDragEnter(InGeometry, InDragDropEvent, InOperation); + + Border->SetBrushColor(ClickedBorderColor); +} + +void UMJInventorySlot::NativeOnDragLeave(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) +{ + Super::NativeOnDragLeave(InDragDropEvent, InOperation); + + Border->SetBrushColor(DefaultBorderColor); +} + +void UMJInventorySlot::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) +{ + if (!InventoryData.IsEmpty()) // 공백칸 무시용 + { + OnMouseEntered.Broadcast(this); + } +} + +void UMJInventorySlot::NativeOnMouseLeave(const FPointerEvent& InMouseEvent) +{ + if (!InventoryData.IsEmpty()) + { + OnMouseLeaved.Broadcast(this); + } +} + + diff --git a/Source/ProjectMJ/UI/Inventory/MJInventorySlot.h b/Source/ProjectMJ/UI/Inventory/MJInventorySlot.h new file mode 100644 index 00000000..e4caff0b --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventorySlot.h @@ -0,0 +1,114 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJInventoryComponent.h" +#include "ItemDataRow.h" +#include "Components/TextBlock.h" +#include "Blueprint/UserWidget.h" +#include "MJInventorySlot.generated.h" + +/** +* Class Description: 인벤토리 슬롯 위젯 + * Author: 이지수 + * Created Date: 2025.08.10 + * Last Modified By: + * Last Modified Date: + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMouseEnterEvent, UMJInventorySlot*, Slot); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMouseLeaveEvent, UMJInventorySlot*, Slot); +class UBorder; +class UTextBlock; +class UImage; +class UMJDragWidget; +class UMJInventoryTooltip; + +UCLASS() +class PROJECTMJ_API UMJInventorySlot : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Border; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Image; + + UPROPERTY() + TObjectPtr Text; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ItemCount; + + TObjectPtr Texture; + + // visual + FLinearColor DefaultBorderColor; + FLinearColor ClickedBorderColor; + + // for drag&drop + FInventoryItemData InventoryData; + FItemDataRow ItemData; + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + TSubclassOf DragWidgetClass; + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + TSubclassOf TooltipWidgetClass; + + UPROPERTY() + TObjectPtr Tooltip = nullptr; + + FVector2D ScreenPos; +public: + // Delegate + UPROPERTY(BlueprintAssignable, Category = "EventDispatchers") + FOnMouseEnterEvent OnMouseEntered; + + UPROPERTY(BlueprintAssignable, Category = "EventDispatchers") + FOnMouseLeaveEvent OnMouseLeaved; + + // + int32 SlotPosition; + bool bIsOccupied = false; + virtual void NativeConstruct() override; + + void SetImage(UTexture2D* ItemTexture); + void SetText(FText text); + void SetItemCount(int count) + { + ItemCount->SetText(FText::AsNumber(count)); + if (count == 0) + { + ItemCount->SetText(FText::GetEmpty()); + } + }; + void SetIsOccupied(bool b) {bIsOccupied = b;} + + void SetInventoryItemData(FInventoryItemData data){InventoryData = data;} + void SetItemData(FItemDataRow data){ItemData = data;} + + UImage* GetImage() {return Image;} + UTextBlock* GetText() {return Text;} + UTexture2D* GetItemTexture() {return Texture;} + int32 GetSlotPosition() {return SlotPosition;} + FVector2D GetScreenPos() {return ScreenPos;} + + FItemDataRow GetItemData() {return ItemData;}; + + virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual void NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation) override; + virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override; + virtual void NativeOnDragEnter(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override; + virtual void NativeOnDragLeave(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override; + + // virtual FReply NativeOnMouseMove(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual void NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent) override; + +}; + diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.cpp b/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.cpp new file mode 100644 index 00000000..bda31748 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.cpp @@ -0,0 +1,13 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Inventory/MJInventoryTooltip.h" +#include "Components/SizeBox.h" + +// void UMJInventoryTooltip::NativeConstruct() +// { +// Super::NativeConstruct(); +// +// SizeBox->SetHeightOverride(500.f); +// SizeBox->SetWidthOverride(400.f); +// } diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.h b/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.h new file mode 100644 index 00000000..c0613f5c --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryTooltip.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Components/TextBlock.h" +#include "MJInventoryTooltip.generated.h" + +/** +* Class Description: 인벤토리 슬롯에 가져다대면 뜨는 아이템 정보 툴팁 + * Author: 이지수 + * Created Date: 2025. + * Last Modified By: + * Last Modified Date: + */ +class USizeBox; +class UTextBlock; +UCLASS() +class PROJECTMJ_API UMJInventoryTooltip : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Description; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ItemName; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SizeBox; +public: + //virtual void NativeConstruct() override; + + void SetDescription(FText Text) {Description->SetText(Text);} + void SetItemName(FText Text){ItemName->SetText(Text);} +}; diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.cpp b/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.cpp new file mode 100644 index 00000000..268c6e64 --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.cpp @@ -0,0 +1,115 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Inventory/MJInventoryWidget.h" +#include "MJInventorySlot.h" +#include "MJInventoryTooltip.h" +#include "Blueprint/WidgetLayoutLibrary.h" +#include "Components/CanvasPanelSlot.h" +#include "Components/GridPanel.h" + +void UMJInventoryWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + InventorySlots.Empty(); + + if (!GridPanel ||!InventorySlotClass) + { + return; + } + + Tooltip = CreateWidget(this,TooltipWidgetClass); + + // 격자 비율 조정 (안하면 격자 크기 다 다름) + GridPanel->SetColumnFill(0, 1.0f); + GridPanel->SetRowFill(0, 1.0f); + + for (int i = 1; i < Row; ++i) + { + for (int j = 1; j < Col; ++j) + { + GridPanel->SetRowFill(i, 1.0f); + GridPanel->SetColumnFill(j, 1.0f); + } + } + // + + // 격자 Row * Col개 만큼 생성 후 슬롯 배치 + for (int i = 0; i < Row; ++i) + { + for (int j = 0; j < Col; ++j) + { + UMJInventorySlot* NewSlot = CreateWidget(this, InventorySlotClass); + GridPanel->AddChildToGrid(NewSlot, i, j); + InventorySlots.Add(NewSlot); + } + } + for (int i = 0; i < InventorySlots.Num(); i++) + { + InventorySlots[i]->SlotPosition = i; + InventorySlots[i]->SetPadding(FMargin(5.f)); + } + // + + // Delegate + for (UMJInventorySlot* InvSlot : InventorySlots) + { + if (InvSlot) + { + InvSlot->OnMouseEntered.AddDynamic(this,&UMJInventoryWidget::ShowTooltip); + InvSlot->OnMouseLeaved.AddDynamic(this,&UMJInventoryWidget::HideTooltip); + } + } + // + +} + +void UMJInventoryWidget::ShowTooltip(UMJInventorySlot* InvSlot) +{ + if (!Tooltip) + { + UE_LOG(LogTemp, Error, TEXT("Tooltip is null!")); + return; + } + if (!InvSlot) + { + UE_LOG(LogTemp, Error, TEXT("InvSlot is null!")); + return; + } + if (InvSlot->GetItemData().Description.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("Description is null!")); + return; + } + + CanvasPanel->AddChildToCanvas(Tooltip); + + if (UCanvasPanelSlot* CanvasSlot = Cast(Tooltip->Slot)) + { + CanvasSlot->SetAutoSize(true); + FVector2D MousePos; + float Scale = UWidgetLayoutLibrary::GetViewportScale(this); // DPI 스케일 값 + // scale을 구해야 마우스 좌표가 DPI 보정되어서 가져와진다 + if (GetWorld()->GetFirstPlayerController()->GetMousePosition(MousePos.X, MousePos.Y)) + { + MousePos /= Scale; + CanvasSlot->SetPosition(MousePos + Pivot); + } + else // 드롭할 때 마우스 위치를 위 함수(GetMousePosition)로 못찾아서 대신 아래 방법씀 + { + // 그냥 일단 드롭 후에는 안뜨게 할개.. + CanvasPanel->RemoveChild(Tooltip); + // FVector2D ScreenPos = InvSlot->GetScreenPos() / Scale; + // CanvasSlot->SetPosition(ScreenPos); + } + } + + Tooltip->SetDescription(InvSlot->GetItemData().Description); + Tooltip->SetItemName(InvSlot->GetItemData().ItemID); +} + +void UMJInventoryWidget::HideTooltip(UMJInventorySlot* InvSlot) +{ + CanvasPanel->RemoveChild(Tooltip); +} \ No newline at end of file diff --git a/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.h b/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.h new file mode 100644 index 00000000..d49a6c1c --- /dev/null +++ b/Source/ProjectMJ/UI/Inventory/MJInventoryWidget.h @@ -0,0 +1,69 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJInventorySlot.h" +#include "Blueprint/UserWidget.h" +#include "Components/CanvasPanel.h" +#include "MJInventoryWidget.generated.h" + +/** +* Class Description: 인벤토리 창 + * Author: 이지수 + * Created Date: 2025. + * Last Modified By: + * Last Modified Date: + */ +class UGridPanel; +class UMJInventorySlot; +UCLASS() + +class PROJECTMJ_API UMJInventoryWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr GridPanel; + + UPROPERTY(meta = (BindWidget)) + TArray> InventorySlots; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Inventory) + TSubclassOf InventorySlotClass; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr CanvasPanel; + + UPROPERTY(EditAnywhere,BlueprintReadWrite) + TSubclassOf TooltipWidgetClass; + + UPROPERTY() + TObjectPtr Tooltip; + + UPROPERTY(EditAnywhere,BlueprintReadWrite, category = Tooltip) + float Pivot; + int32 SlotIndex; + bool bIsOccupied; + + // 아래 숫자 조절로 인벤토리 칸 조절 가능함 + int32 Row = 6; + int32 Col = 6; +public: + virtual void NativeConstruct() override; + + UFUNCTION() + void ShowTooltip(UMJInventorySlot* InvSlot); + + UFUNCTION() + void HideTooltip(UMJInventorySlot* InvSlot); + + bool GetIsOccupied() {return bIsOccupied;} + void SetIsOccupied(bool b) {bIsOccupied = b;} + + void SetCanvasPanel(); + + TArray GetInventorySlot() {return InventorySlots;} + +}; diff --git a/Source/ProjectMJ/UI/MJHUDWidget.cpp b/Source/ProjectMJ/UI/MJHUDWidget.cpp new file mode 100644 index 00000000..7b7fafa4 --- /dev/null +++ b/Source/ProjectMJ/UI/MJHUDWidget.cpp @@ -0,0 +1,139 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/MJHUDWidget.h" + +#include "MJUIToggle.h" +#include "UI/Bar/MJHealthBarWidget.h" +#include "Bar/MJManaBarWidget.h" +#include "Bar/MJStaminaBar.h" +#include "Bar/MJExperienceWidget.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/Button.h" +#include "Dialogue/MJDialogueWidget.h" +#include "World/MJStatWidget.h" +#include "Inventory/MJInventoryWidget.h" +#include "Skill/MJSkillWidget.h" +#include "Store/MJStoreWidget.h" +#include "TG/UI/MJBossHpBarWidget.h" + +void UMJHUDWidget::NativeConstruct() +{ + Super::NativeConstruct(); + if (StatPanel) + { + StatPanel->SetVisibility(ESlateVisibility::Hidden); + } + + if (Inventory) + { + Inventory->SetVisibility(ESlateVisibility::Hidden); + } + + if (Store) + { + Store->SetVisibility(ESlateVisibility::Hidden); + } + + if (Dialogue) + { + Dialogue->SetVisibility(ESlateVisibility::Hidden); + } + + if (UIToggle) + { + UIToggle->GetInventoryButton()->OnClicked.AddDynamic(this, &ThisClass::ShowInventory); + UIToggle->GetStatPanelButton()->OnClicked.AddDynamic(this, &ThisClass::ShowStatPanel); + UIToggle->GetSkillWidgetButton()->OnClicked.AddDynamic(this, &ThisClass::SetSkillWidgetVisibility); + } + + if (SkillWidget) + { + SkillWidget->SetVisibility(ESlateVisibility::Hidden); + } +} + +void UMJHUDWidget::BindAtrributesToChildren(UMJAbilitySystemComponent* ASC, UMJCharacterAttributeSet* AttributeSet, UMJPlayerStatComponent* Stat) +{ + if (HealthBar) + { + HealthBar->BindToAttributes(ASC,AttributeSet); + } + // + // if (ManaBar) + // { + // ManaBar->BindToAttributes(ASC,AttributeSet); + // } + + if (StaminaBar) + { + StaminaBar->BindToAttributes(ASC,AttributeSet); + } + + if (StatPanel) + { + StatPanel->BindToAttribute(ASC,AttributeSet,Stat); + } +} + +void UMJHUDWidget::ShowStatPanel() +{ + if (StatPanel->GetVisibility() == ESlateVisibility::Visible) + { + StatPanel->SetVisibility(ESlateVisibility::Hidden); + } + else if (StatPanel->GetVisibility() == ESlateVisibility::Hidden) + { + StatPanel->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJHUDWidget::ShowInventory() +{ + if (Inventory->GetVisibility() == ESlateVisibility::Visible) + { + Inventory->SetVisibility(ESlateVisibility::Hidden); + } + else if (Inventory->GetVisibility() == ESlateVisibility::Hidden) + { + Inventory->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJHUDWidget::ShowStore() +{ + if (Store->GetVisibility() == ESlateVisibility::Visible) + { + Store->SetVisibility(ESlateVisibility::Hidden); + Store->CloseWidget(); + } + else if (Store->GetVisibility() == ESlateVisibility::Hidden) + { + Store->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJHUDWidget::SetSkillWidgetVisibility() +{ + if (SkillWidget->GetVisibility() == ESlateVisibility::Visible) + { + SkillWidget->SetVisibility(ESlateVisibility::Hidden); + } + else if (SkillWidget->GetVisibility() == ESlateVisibility::Hidden) + { + SkillWidget->SetVisibility(ESlateVisibility::Visible); + } +} + +void UMJHUDWidget::SetDialogueVisibility() +{ + if (Dialogue->GetVisibility() == ESlateVisibility::Visible) + { + Dialogue->SetVisibility(ESlateVisibility::Hidden); + } + else if (Dialogue->GetVisibility() == ESlateVisibility::Hidden) + { + Dialogue->SetVisibility(ESlateVisibility::Visible); + } +} + diff --git a/Source/ProjectMJ/UI/MJHUDWidget.h b/Source/ProjectMJ/UI/MJHUDWidget.h new file mode 100644 index 00000000..c707e624 --- /dev/null +++ b/Source/ProjectMJ/UI/MJHUDWidget.h @@ -0,0 +1,97 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJHUDWidget.generated.h" + +/** + * Class Description: HUD + * Author: 이지수 + * Created Date: 2025.06.26 + * Last Modified By: + * Last Modified Date: + */ + +class UMJEquipedSkillWidget; +class UMJSkillWidget; +class UMJUIToggle; +class UMJDialogueWidget; +class UMJBossHpBarWidget; +class UMJHealthBarWidget; +class UMJManaBarWidget; +class UMJStaminaBar; +class UMJExperienceWidget; +class UMJStatWidget; +class UMJInventoryWidget; +class UMJStoreWidget; + +UCLASS() +class PROJECTMJ_API UMJHUDWidget : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr HealthBar; + + // UPROPERTY(meta = (BindWidget)) + // TObjectPtr ManaBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StaminaBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ExpBar; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatPanel; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Inventory; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Store; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Dialogue; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr UIToggle; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillWidget; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr EquipedSkillWidget; + +public: + UFUNCTION() + virtual void NativeConstruct() override; + + UFUNCTION() + void BindAtrributesToChildren(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet, class UMJPlayerStatComponent* Stat); + + UFUNCTION() + void ShowStatPanel(); + + UFUNCTION() + void ShowInventory(); + + UFUNCTION() + void ShowStore(); + + UFUNCTION() + void SetSkillWidgetVisibility(); + + UFUNCTION() + void SetDialogueVisibility(); + + UMJInventoryWidget* GetInventoryWidget() {return Inventory;}; + UMJStoreWidget* GetStoreWidget() {return Store;}; + UMJDialogueWidget* GetDialogueWidget() {return Dialogue;}; + UMJSkillWidget* GetSkillWidget() {return SkillWidget;}; + UMJEquipedSkillWidget* GetEquipedSkillWidget() {return EquipedSkillWidget;} + +}; diff --git a/Source/ProjectMJ/UI/MJUIManagerSubsystem.cpp b/Source/ProjectMJ/UI/MJUIManagerSubsystem.cpp index 60c4b367..ef32218c 100644 --- a/Source/ProjectMJ/UI/MJUIManagerSubsystem.cpp +++ b/Source/ProjectMJ/UI/MJUIManagerSubsystem.cpp @@ -2,99 +2,83 @@ #include "UI/MJUIManagerSubsystem.h" +#include "MJHUDWidget.h" #include "Dialogue/MJDialogueWidget.h" #include "Dialogue/MJBacklogWidget.h" #include "Dialogue/MJDialogueComponent.h" +#include "Player/MJPlayerState.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "Bar/MJEnemyHPBar.h" +#include "Components/WidgetComponent.h" +#include "Controller/MJPlayerController.h" #include "UObject/ConstructorHelpers.h" +#include "Inventory/MJInventoryWidget.h" +#include "Kismet/GameplayStatics.h" +#include "TG/GameState/MJGameStateDungeon.h" +#include "TG/UI/MJBossHpBarWidget.h" +#include "TG/UI/MJGameFlowHUDWidget.h" -void UMJUIManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection) -{ - Super::Initialize(Collection); - - UE_LOG(LogTemp, Log, TEXT("으앵")); - DialogueWidgetClass = LoadClass( - nullptr, - TEXT("/Game/Dialogue/Widget/BP_MJDialogueWidget.BP_MJDialogueWidget_C")); -} -void UMJUIManagerSubsystem::Deinitialize() +UMJUIManagerSubsystem::UMJUIManagerSubsystem() { - Super::Deinitialize(); -} -void UMJUIManagerSubsystem::ShowDialogue(UMJDialogueComponent* DialogueComp) // 위젯 띄우기만 하는 함수 -{ - if (!DialogueWidgetClass) - return; - - // 위젯 생성 - if (!DialogueWidget) + static ConstructorHelpers::FClassFinder HUDWidgetRef(TEXT("/Game/UI/WBP/HUD/WBP_HUD.WBP_HUD_C")); + if (HUDWidgetRef.Succeeded()) { - DialogueWidget = CreateWidget(GetWorld(), DialogueWidgetClass); - if (!DialogueWidget) - return; - - DialogueWidget->AddToViewport(); + HUDWidgetClass = HUDWidgetRef.Class; } +} + +void UMJUIManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); - DialogueComp->StartDialogue(); // 데이터테이블을 불러오고, index를 0으로 설정 - SetDialogue(DialogueComp); // 인덱스 0일 때의 대사들이 세팅되어 타이핑된다 + // EnemyHPBarWidgetClass = LoadClass( + // nullptr, + // TEXT("/Game/UI/WBP/World/Bar/WBP_EnemyHPBar.WBP_EnemyHPBar_C")); + // + // EnemyHPBarWidget = CreateWidget(GetWorld(),EnemyHPBarWidgetClass ); } -void UMJUIManagerSubsystem::NextDialogue(UMJDialogueComponent* DialogueComp) +void UMJUIManagerSubsystem::ShowHUD(AMJPlayerState* PlayerState, AMJPlayerController* PC, UMJPlayerStatComponent* Stat) { - if (DialogueWidget) + HUDWidget = CreateWidget(PC, HUDWidgetClass); + if (HUDWidget) { - if (DialogueWidget->GetIsTyping()) - { - DialogueWidget->SkipTyping(); - return; - } + HUDWidget->AddToViewport(); - DialogueComp->NextDialogue(); // 인덱스 +1 - - SetDialogue(DialogueComp); // +1된 row를 가져와 타이핑 >> 순서 중요 - - - if (DialogueComp->IsDialogueEnd()) + if (PlayerState) { - HideDialogue(); + auto* MJASC = Cast(PlayerState->GetAbilitySystemComponent()); + HUDWidget->BindAtrributesToChildren(MJASC,PlayerState->GetCharacterAttributeSet(),Stat); } } } -void UMJUIManagerSubsystem::SetDialogue(const UMJDialogueComponent* DialogueComp) const +void UMJUIManagerSubsystem::SetDialogueVisibility() { - if (const FMJDialogueRow* Row = DialogueComp->GetCurrentRow()) - { - DialogueWidget->ShowDialogue(*Row); - DialogueWidget->StartTyping(Row->Text, 0.08f); - DialogueWidget->SetImageOpacity(Row->Speaker); - } + HUDWidget->SetDialogueVisibility(); +} - if (const FMJDialogueRow* PrevRow = DialogueComp->GetPreviousRow()) - { - if (DialogueWidget->BacklogWidget) - { - DialogueWidget->BacklogWidget->AddLine(*PrevRow); - } - } +void UMJUIManagerSubsystem::ShowStatPanel() +{ + HUDWidget->ShowStatPanel(); } -void UMJUIManagerSubsystem::HideDialogue() +void UMJUIManagerSubsystem::ShowInventory() { - if (DialogueWidget) - { - DialogueWidget->RemoveFromParent(); - DialogueWidget = nullptr; - } + HUDWidget->ShowInventory(); } -void UMJUIManagerSubsystem::ShowBacklog() +void UMJUIManagerSubsystem::ShowStore() { - DialogueWidget->ShowBacklog(); + HUDWidget->ShowStore(); } +void UMJUIManagerSubsystem::SetSkillWidgetVisibility() +{ + HUDWidget->SetSkillWidgetVisibility(); +} diff --git a/Source/ProjectMJ/UI/MJUIManagerSubsystem.h b/Source/ProjectMJ/UI/MJUIManagerSubsystem.h index 86b09a12..4a7506df 100644 --- a/Source/ProjectMJ/UI/MJUIManagerSubsystem.h +++ b/Source/ProjectMJ/UI/MJUIManagerSubsystem.h @@ -10,13 +10,17 @@ * Class Description: UI를 띄우고 내리기 위한 싱글톤 매니저 * Author: 이지수 * Created Date: 2025-06-20 - * Last Modified By: - * Last Modified Date: + * Last Modified By: 이지수 + * Last Modified Date: 2025-06-26 */ +class UWidgetComponent; +class UMJBossHpBarWidget; class UMJDialogueWidget; -class UMJBacklogWidget; class UMJDialogueComponent; +class UMJHUDWidget; +class UMJCharacterAttributeSet; +class UMJAbilitySystemComponent; UCLASS() class PROJECTMJ_API UMJUIManagerSubsystem : public UGameInstanceSubsystem @@ -24,24 +28,31 @@ class PROJECTMJ_API UMJUIManagerSubsystem : public UGameInstanceSubsystem GENERATED_BODY() public: + UMJUIManagerSubsystem(); + virtual void Initialize(FSubsystemCollectionBase& Collection) override; - virtual void Deinitialize() override; - // Dialogue Section - void ShowDialogue(UMJDialogueComponent* DialogueComp); - void NextDialogue(UMJDialogueComponent* DialogueComp); - void HideDialogue(); - - void SetDialogue(const UMJDialogueComponent* DialogueComp) const; + // HUD + void ShowHUD(class AMJPlayerState* PlayerState, class AMJPlayerController* PC, class UMJPlayerStatComponent* Stat); + UMJHUDWidget* GetHUDWidget() {return HUDWidget;} + + // Dialouge Section + void SetDialogueVisibility(); - //BackLog Section - void ShowBacklog(); + // StatPanel Section + void ShowStatPanel(); + + // Inventory Section + void ShowInventory(); + // Store Section + void ShowStore(); + + void SetSkillWidgetVisibility(); protected: UPROPERTY() - UMJDialogueWidget* DialogueWidget; - - TSubclassOf DialogueWidgetClass; + TObjectPtr HUDWidget; - bool bIsDialogueActive; + UPROPERTY() + TSubclassOf HUDWidgetClass; }; diff --git a/Source/ProjectMJ/UI/MJUIToggle.cpp b/Source/ProjectMJ/UI/MJUIToggle.cpp new file mode 100644 index 00000000..4d005b84 --- /dev/null +++ b/Source/ProjectMJ/UI/MJUIToggle.cpp @@ -0,0 +1,32 @@ +// ThenOneDayStudio + + +#include "UI/MJUIToggle.h" + +#include "Components/Border.h" +#include "Components/Button.h" +#include "Components/VerticalBox.h" + +void UMJUIToggle::NativeConstruct() +{ + Super::NativeConstruct(); + + Toggle->SetVisibility(ESlateVisibility::Visible); + Border->SetVisibility(ESlateVisibility::Hidden); + List->SetVisibility(ESlateVisibility::Hidden); + Toggle->OnClicked.AddDynamic(this, &ThisClass::UMJUIToggle::OnClickedToggle); +} + +void UMJUIToggle::OnClickedToggle() +{ + if (List->GetVisibility() == ESlateVisibility::Visible) + { + Border->SetVisibility(ESlateVisibility::Hidden); + List->SetVisibility(ESlateVisibility::Hidden); + } + else if (List->GetVisibility() == ESlateVisibility::Hidden) + { + Border->SetVisibility(ESlateVisibility::Visible); + List->SetVisibility(ESlateVisibility::Visible); + } +} diff --git a/Source/ProjectMJ/UI/MJUIToggle.h b/Source/ProjectMJ/UI/MJUIToggle.h new file mode 100644 index 00000000..198236de --- /dev/null +++ b/Source/ProjectMJ/UI/MJUIToggle.h @@ -0,0 +1,54 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJUIToggle.generated.h" + +class UBorder; +class UVerticalBox; +class UHorizontalBox; +class UButton; +/** +* Class Description: 위젯 목록 + * Author: 이지수 + * Created Date: 2025.08.11 + * Last Modified By: + * Last Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJUIToggle : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Toggle; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr InventoryButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatPanelButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillWidgetButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr List; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Border; + +public: + UFUNCTION() + virtual void NativeConstruct() override; + + UFUNCTION() + void OnClickedToggle(); + + UButton* GetInventoryButton() {return InventoryButton;}; + UButton* GetStatPanelButton() {return StatPanelButton;}; + UButton* GetSkillWidgetButton() {return SkillWidgetButton;}; +}; diff --git a/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.cpp b/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.cpp new file mode 100644 index 00000000..78a7a54f --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.cpp @@ -0,0 +1,74 @@ +// ThenOneDayStudio + + +#include "UI/Skill/MJEquipedSkillWidget.h" +#include "TG/MJGameInstance.h" +#include "GameplayTagContainer.h" +#include "Components/Image.h" +#include "DataTable/MJSkillDataRow.h" + +void UMJEquipedSkillWidget::SetInstantImage(UTexture2D* ItemTexture) +{ + if (InstantImage) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + //Brush.ImageSize = FVector2D(80,80); + InstantImage->SetBrush(Brush); + InstantImage->SetOpacity(1.0); + } +} + +void UMJEquipedSkillWidget::SetChargingImage(UTexture2D* ItemTexture) +{ + if (ChargingImage) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + //Brush.ImageSize = FVector2D(80,80); + ChargingImage->SetBrush(Brush); + ChargingImage->SetOpacity(1.0); + } +} + +void UMJEquipedSkillWidget::SetPassiveImage(UTexture2D* ItemTexture) +{ + if (PassiveImage) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + //Brush.ImageSize = FVector2D(80,80); + PassiveImage->SetBrush(Brush); + PassiveImage->SetOpacity(1.0); + } +} + +void UMJEquipedSkillWidget::SetAllImage(FGameplayTag SkillTag) +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI ||!GI->PlayerSkillDataTable) + { + return; + } + + const FMJSkillDataRow* Row = GI->PlayerSkillDataTable->FindRow(SkillTag.GetTagName(),TEXT("")); + if (!Row) + { + return; + } + + if (Row->SkillType == ESkillType::Instant) + { + SetInstantImage(Row->Icon); + } + if (Row->SkillType == ESkillType::Passive) + { + SetPassiveImage(Row->Icon); + } + if (Row->SkillType == ESkillType::Charge) + { + SetChargingImage(Row->Icon); + } +} + + diff --git a/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.h b/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.h new file mode 100644 index 00000000..9ff3732e --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJEquipedSkillWidget.h @@ -0,0 +1,35 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJEquipedSkillWidget.generated.h" + +struct FGameplayTag; +class UImage; +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJEquipedSkillWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta=(BindWidget)) + TObjectPtr InstantImage; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr ChargingImage; + + UPROPERTY(meta=(BindWidget)) + TObjectPtr PassiveImage; + +public: + void SetInstantImage(UTexture2D* ItemTexture); + void SetChargingImage(UTexture2D* ItemTexture); + void SetPassiveImage(UTexture2D* ItemTexture); + + void SetAllImage(FGameplayTag SkillTag); +}; diff --git a/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.cpp b/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.cpp new file mode 100644 index 00000000..d26c0919 --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.cpp @@ -0,0 +1,88 @@ +// ThenOneDayStudio + + +#include "UI/Skill/MJSkillSlotWidget.h" + +#include "Components/Button.h" +#include "Components/Image.h" +#include "Components/TextBlock.h" + +void UMJSkillSlotWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + HideImage->SetVisibility(ESlateVisibility::Visible); + Equip->SetIsEnabled(false); + Equip->OnClicked.AddDynamic(this, &ThisClass::OnClicked_EquipButton); +} + +void UMJSkillSlotWidget::SetSkillWidget(FGameplayTag Tag,UTexture2D* Image, FText Name, FText Description, ESkillType skilltype, int32 Level) +{ + SetSkillImage(Image); + SetSkillName(Name); + SetSkillDescription(Description); + SetSkillType(skilltype); + SetSkillLevel(Level); + + HideImage->SetVisibility(ESlateVisibility::Hidden); + Equip->SetIsEnabled(true); + SkillTag = Tag; +} + +void UMJSkillSlotWidget::SetEquipedSkillTag(FGameplayTag Tag) +{ +} + +void UMJSkillSlotWidget::SetSkillImage(UTexture2D* Image) +{ + if (SkillImage) + { + FSlateBrush Brush; + Brush.SetResourceObject(Image); + SkillImage->SetBrush(Brush); + SkillImage->SetOpacity(1.0); + } + + SkillIcon = Image; +} + +void UMJSkillSlotWidget::SetSkillName(FText Name) +{ + if (SkillName) + { + SkillName->SetText(Name); + } +} + +void UMJSkillSlotWidget::SetSkillDescription(FText Description) +{ + if (SkillDescription) + { + SkillDescription->SetText(Description); + } +} + +void UMJSkillSlotWidget::SetSkillType(ESkillType type) +{ + FText S_Type = StaticEnum()->GetDisplayNameTextByValue((int64)type); + + if (SkillType) + { + SkillType->SetText(S_Type); + } + + Type = type; +} + +void UMJSkillSlotWidget::SetSkillLevel(int32 Level) +{ + if (SkillLevel) + { + SkillLevel->SetText(FText::AsNumber(Level)); + } +} + +void UMJSkillSlotWidget::OnClicked_EquipButton() +{ + OnClickedEquipButton.Broadcast(SkillIcon,Type, SkillTag); +} diff --git a/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.h b/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.h new file mode 100644 index 00000000..17429c66 --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJSkillSlotWidget.h @@ -0,0 +1,75 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Blueprint/UserWidget.h" +#include "MJSkillSlotWidget.generated.h" + +enum class ESkillType : uint8; +class UButton; +/** + * + */ +class UTextBlock; +class UImage; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnClickedEquipButton,UTexture2D*,icon,ESkillType,type,FGameplayTag,SkillTag); +UCLASS() +class PROJECTMJ_API UMJSkillSlotWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillImage; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillName; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillDescription; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillType; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SkillLevel; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr HideImage; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Equip; + + UPROPERTY() + UTexture2D* SkillIcon; + + UPROPERTY() + ESkillType Type; + + FGameplayTag SkillTag; +public: + UFUNCTION() + virtual void NativeConstruct() override; + + UFUNCTION() + void SetSkillWidget(FGameplayTag Tag, UTexture2D* Image, FText Name, FText Description, ESkillType ype, int32 Level); + void SetEquipedSkillTag(FGameplayTag Tag); + void SetSkillImage(UTexture2D* Image); + void SetSkillName(FText Name); + void SetSkillDescription(FText Description); + void SetSkillType(ESkillType type); + void SetSkillLevel(int32 Level); + + UFUNCTION() + void OnClicked_EquipButton(); + + UButton* GetEquipButton() {return Equip;} + UTexture2D* GetSkillIcon() {return SkillIcon;} + ESkillType GetSkillType() {return Type;} + + FOnClickedEquipButton OnClickedEquipButton; + +}; \ No newline at end of file diff --git a/Source/ProjectMJ/UI/Skill/MJSkillWidget.cpp b/Source/ProjectMJ/UI/Skill/MJSkillWidget.cpp new file mode 100644 index 00000000..2944c26b --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJSkillWidget.cpp @@ -0,0 +1,63 @@ +// ThenOneDayStudio + + +#include "UI/Skill/MJSkillWidget.h" + +#include "GameplayTagContainer.h" +#include "MJSkillSlotWidget.h" +#include "Components/Button.h" +#include "Components/ScrollBox.h" +#include "DataTable/MJSkillDataRow.h" +#include "TG/MJGameInstance.h" + + +void UMJSkillWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + if (!SkillSlotClass) + { + return; + } + + for (int j = 0; j < 10; ++j) + { + UMJSkillSlotWidget* NewSlot = CreateWidget(this, SkillSlotClass); + ScrollBox->AddChild(NewSlot); + SkillSlots.Add(NewSlot); + + SkillSlots[j]->GetEquipButton()->OnClicked.AddDynamic(this, &ThisClass::UpdateEquipedSlots); + } +} + +void UMJSkillWidget::UpdateSkillSlots(FGameplayTag SkillTag,int32 Level) +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI ||!GI->PlayerSkillDataTable) + { + return; + } + + const FMJSkillDataRow* Row = GI->PlayerSkillDataTable->FindRow(SkillTag.GetTagName(),TEXT("")); + if (!Row) + { + return; + } + + int32 Index = Row->SkillIndex; + UTexture2D* Image = Row->Icon; + FText Name = Row->SkillName; + FText Description =Row->SkillDescription; + ESkillType SkillType =Row->SkillType; + //FText Type = StaticEnum()->GetDisplayNameTextByValue((int64)SkillType); + + if (Index >= 0) + { + SkillSlots[Index]->SetSkillWidget(SkillTag,Image, Name, Description, SkillType, Level); + } +} + +void UMJSkillWidget::UpdateEquipedSlots() +{ + +} diff --git a/Source/ProjectMJ/UI/Skill/MJSkillWidget.h b/Source/ProjectMJ/UI/Skill/MJSkillWidget.h new file mode 100644 index 00000000..f2fdc494 --- /dev/null +++ b/Source/ProjectMJ/UI/Skill/MJSkillWidget.h @@ -0,0 +1,45 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJSkillWidget.generated.h" + +/** +* Class Description: 스킬창 + * Author: 이지수 + * Created Date: 2025.08.11 + * Last Modified By: + * Last Modified Date: + */ + +struct FGameplayTag; +class UScrollBox; +class UMJSkillSlotWidget; + +UCLASS() +class PROJECTMJ_API UMJSkillWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + + void UpdateSkillSlots(FGameplayTag SkillTag,int32 Level); + + TArray> GetSkillSlots() {return SkillSlots;}; + + UFUNCTION() + void UpdateEquipedSlots(); +protected: + UPROPERTY(meta = (BindWidget)) + TArray> SkillSlots; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ScrollBox; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Skill) + TSubclassOf SkillSlotClass; + +}; diff --git a/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.cpp b/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.cpp new file mode 100644 index 00000000..7a0eff42 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.cpp @@ -0,0 +1,39 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Store/MJMerchandiseSlot.h" + + +void UMJMerchandiseSlot::OnClicked_PlusButton() +{ + Quantity ++; + SetQuantity(Quantity); +} + +void UMJMerchandiseSlot::OnClicked_MinusButton() +{ + if (Quantity == 0) + { + SetQuantity(Quantity); + return; + } + + Quantity --; + SetQuantity(Quantity); +} + +void UMJMerchandiseSlot::OnClicked_PlusTenButton() +{ + Quantity += 10; + SetQuantity(Quantity); +} + +void UMJMerchandiseSlot::OnClicked_MinusTenButton() +{ + Quantity -= 10; + if (Quantity <= 0) + { + Quantity = 0; + } + SetQuantity(Quantity); +} diff --git a/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.h b/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.h new file mode 100644 index 00000000..ab0765a5 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJMerchandiseSlot.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MJStoreSlotBase.h" +#include "MJMerchandiseSlot.generated.h" + +/** + * Class Description: 상점 구매창 내 슬롯 + * Author: 이지수 + * Created Date: 2025.07.13 + * Last Modified By: 2025.08.09 + * Last Modified Date: + */ + +class UMJStoreComponent; +struct FGameplayTag; +class UMJPopupWidget; +class UButton; +class UTextBlock; +class UImage; + +UCLASS() +class PROJECTMJ_API UMJMerchandiseSlot : public UMJStoreSlotBase +{ + GENERATED_BODY() + +public: + virtual void OnClicked_PlusButton() override; + virtual void OnClicked_MinusButton() override; + virtual void OnClicked_PlusTenButton() override; + virtual void OnClicked_MinusTenButton() override; + +}; diff --git a/Source/ProjectMJ/UI/Store/MJPopupWidget.cpp b/Source/ProjectMJ/UI/Store/MJPopupWidget.cpp new file mode 100644 index 00000000..64cab4a9 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJPopupWidget.cpp @@ -0,0 +1,10 @@ +// ThenOneDayStudio + + +#include "UI/Store/MJPopupWidget.h" +#include "Components/TextBlock.h" + +void UMJPopupWidget::SetNotification(FText notification) +{ + Notification->SetText(notification); +} diff --git a/Source/ProjectMJ/UI/Store/MJPopupWidget.h b/Source/ProjectMJ/UI/Store/MJPopupWidget.h new file mode 100644 index 00000000..005a0607 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJPopupWidget.h @@ -0,0 +1,39 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJPopupWidget.generated.h" + +class UTextBlock; +class UButton; + +/** +* Class Description: 구매 혹은 판매 버튼을 누르면 나오는 위젯 + * Author: 이지수 + * Created Date: 2025.08.09 + * Last Modified By: + * Last Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJPopupWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Notification; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr YesButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr NoButton; + +public: + void SetNotification(FText notification); + UButton* GetYesButton() {return YesButton;} + UButton* GetNoButton() {return NoButton;} +}; diff --git a/Source/ProjectMJ/UI/Store/MJSalesSlot.cpp b/Source/ProjectMJ/UI/Store/MJSalesSlot.cpp new file mode 100644 index 00000000..0062afc2 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJSalesSlot.cpp @@ -0,0 +1,11 @@ +// ThenOneDayStudio + + +#include "UI/Store/MJSalesSlot.h" + +#include "Components/TextBlock.h" + +void UMJSalesSlot::SetCount(int32 count) +{ + this->Count->SetText(FText::FromString(FString::Printf(TEXT(" 판매 가능 수량 : %d"), count))); +} diff --git a/Source/ProjectMJ/UI/Store/MJSalesSlot.h b/Source/ProjectMJ/UI/Store/MJSalesSlot.h new file mode 100644 index 00000000..c1d10b2f --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJSalesSlot.h @@ -0,0 +1,30 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "MJStoreSlotBase.h" +#include "Components/TextBlock.h" +#include "MJSalesSlot.generated.h" + +/** +* Class Description: 스토어 위젯의 내 아이템창 슬롯 + * Author: 이지수 + * Created Date: 2025.08.09 + * Last Modified By: + * Last Modified Date: + */ + +UCLASS() +class PROJECTMJ_API UMJSalesSlot : public UMJStoreSlotBase +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Count; + +public: + void SetCount(int32 count); + +}; diff --git a/Source/ProjectMJ/UI/Store/MJStoreComponent.cpp b/Source/ProjectMJ/UI/Store/MJStoreComponent.cpp new file mode 100644 index 00000000..38a31a9a --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreComponent.cpp @@ -0,0 +1,198 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Store/MJStoreComponent.h" +#include "MJMerchandiseSlot.h" +#include "MJSalesSlot.h" +#include "MJStoreWidget.h" +#include "Components/Button.h" +#include "Dialogue/MJDialogueChoiceWidget.h" +#include "Dialogue/MJDialogueWidget.h" +#include "TG/MJGameInstance.h" +#include "UI/MJHUDWidget.h" +#include "UI/MJUIManagerSubsystem.h" +#include "UI/Inventory/ItemDataRow.h" +#include "UI/Inventory/MJInventoryComponent.h" + + +void UMJStoreComponent::UpdateStore() +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI ||!GI->ItemDataTable) + { + return; + } + + TArray MerSlot = GI->GetSubsystem()->GetHUDWidget()->GetStoreWidget()->GetMerchandiseSlots(); + if (!GI->GetSubsystem() + || !GI->GetSubsystem()->GetHUDWidget() + || !GI->GetSubsystem()->GetHUDWidget()->GetStoreWidget() + ) + { + return; + } + + for (int i = 0; i < MerSlot.Num(); i++) + { + if (!MerSlot[i]) + { + return; + } + } + + TArray MerchandiseRow; + TArray Rownames = GI->ItemDataTable->GetRowNames(); + + for (const FName& RowName : Rownames) + { + FItemDataRow* Row = GI->ItemDataTable->FindRow(RowName, TEXT("")); + if (Row && Row->IsMerchandise) // 상품인 것들만 모아서 + { + MerchandiseRow.Add(RowName); // 새로 담아내자 + } + } + + SlotCount = FMath::Min(MerSlot.Num(), MerchandiseRow.Num()); + + for (int i = 0; i < SlotCount; i++) + { + // UI 갱신 + MerSlot[i]->SetItemTag(GI->ItemDataTable->FindRow(MerchandiseRow[i], TEXT(""))->ItemTag); + MerSlot[i]->SetImage(GI->ItemDataTable->FindRow(MerchandiseRow[i], TEXT(""))->Icon); + MerSlot[i]->SetItemName(GI->ItemDataTable->FindRow(MerchandiseRow[i], TEXT(""))->ItemID); + MerSlot[i]->SetDescription(GI->ItemDataTable->FindRow(MerchandiseRow[i], TEXT(""))->Description); + MerSlot[i]->SetPrice(GI->ItemDataTable->FindRow(MerchandiseRow[i], TEXT(""))->Price); + + // 바인딩 + MerSlot[i]->GetButton()->OnClicked.AddDynamic(this, &ThisClass::TryPurchase); + } +} + +void UMJStoreComponent::UpdateInventory(UMJInventoryComponent* InvenComp) +{ + UMJGameInstance* GI = GetWorld()->GetGameInstance(); + if (!GI ||!GI->ItemDataTable) + { + return; + } + + TArray InvenSlot = GI->GetSubsystem()->GetHUDWidget()->GetStoreWidget()->GetInventorySlots(); + if (!GI->GetSubsystem() + || !GI->GetSubsystem()->GetHUDWidget() + || !GI->GetSubsystem()->GetHUDWidget()->GetStoreWidget() + ) + { + return; + } + + for (int i = 0; i < ItemTagForStore.Num(); i++) + { + const FItemDataRow* Row= GI->ItemDataTable->FindRow(ItemTagForStore[i].GetTagName(),TEXT("Inventory")); + if (!Row) + { + InvenSlot[i]->SetVisibility(ESlateVisibility::Collapsed); + } + + const FGameplayTag ItemTag = Row->ItemTag; + const FInventoryItemData* Data = InvenComp->GetItemInInventory().Find(ItemTag); + if (!Data || Data->ItemCount <=0) + { + InvenSlot[i]->SetVisibility(ESlateVisibility::Collapsed); + return; + } + + InvenSlot[i]->SetVisibility(ESlateVisibility::SelfHitTestInvisible); + InvenSlot[i]->SetItemTag(ItemTag); + InvenSlot[i]->SetImage(Row->Icon); + InvenSlot[i]->SetItemName(Row->ItemID); + InvenSlot[i]->SetDescription(Row->Description); + InvenSlot[i]->SetPrice(Row->Price); + InvenSlot[i]->SetCount(Data->ItemCount); + InvenSlot[i]->SetMaxQuantity(Data->ItemCount); + + InvenSlot[i]->GetButton()->OnClicked.RemoveDynamic(this, &ThisClass::TrySale); + InvenSlot[i]->GetButton()->OnClicked.AddDynamic(this, &ThisClass::TrySale); + } + + + // 0이면 빈 슬롯이 되도록 +} + + +void UMJStoreComponent::TryPurchase() +{ + // 구매하시겠어요 팝업을 뜨게 함 + GetWorld()->GetGameInstance()-> + GetSubsystem()->GetHUDWidget()->GetStoreWidget()->Onclicked_PurchaseButton(); +} + +void UMJStoreComponent::TrySale() +{ + GetWorld()->GetGameInstance()-> + GetSubsystem()->GetHUDWidget()->GetStoreWidget()->Onclicked_SellButton(); +} + +void UMJStoreComponent::DialogueEnd() +{ + SetIndex(0); + bIsOpened = true; +} + +void UMJStoreComponent::SetChoiceWidgetText() +{ + GetDialogueWidget()->GetDialogueChoiceWidget()->SetTextBlock( + GetCurrentRow()->Choices[0].ChoiceText, + GetCurrentRow()->Choices[1].ChoiceText, + GetCurrentRow()->Choices[2].ChoiceText); +} + +void UMJStoreComponent::SetItemData(TArray ItemTags, int32 slotCount, UMJInventoryComponent* InvenComp) +{ + ItemTagForStore = ItemTags; + InvenSlotCount = slotCount; + UpdateInventory(InvenComp); +} + +void UMJStoreComponent::BindButtons() +{ + if (CurrentIndex == 0) + { + GetDialogueWidget()->GetDialogueChoiceWidget()->GetQuestButton()->OnClicked.AddDynamic(this, &UMJStoreComponent::ShowStory); + GetDialogueWidget()->GetDialogueChoiceWidget()->GetPointButton()->OnClicked.AddDynamic(this, &UMJStoreComponent::ShowStore); + GetDialogueWidget()->GetDialogueChoiceWidget()->GetExitButton()->OnClicked.AddDynamic(this, &UMJStoreComponent::ExitDialogue); + } + SetChoiceWidgetText(); +} + +void UMJStoreComponent::ShowStory() // Story 클릭 시 +{ + ++ CurrentIndex; + if (!IsDialogueEnd()) + { + FloatLine(); + GetDialogueWidget()->SetImageOpacity(GetCurrentRow()->Speaker); + UpdateBacklog(); + } + GetDialogueWidget()->GetDialogueChoiceWidget()->SetVisibility(ESlateVisibility::Hidden); +} + +void UMJStoreComponent::ShowStore() +{ + CurrentIndex = DialogueTable->GetRowNames().Num() - 1; + GetDialogueWidget()->SetTextBlock(GetCurrentRow()->ScriptForStore, GetCurrentRow()->Speaker); + GetDialogueWidget()->StartTyping(GetCurrentRow()->ScriptForStore,0.05); + GetDialogueWidget()->SetImageOpacity(GetCurrentRow()->Speaker); + GetDialogueWidget()->GetDialogueChoiceWidget()->SetVisibility(ESlateVisibility::Hidden); + bIsStoreRoot = true; +} + +void UMJStoreComponent::ExitDialogue() +{ + CurrentIndex = DialogueTable->GetRowNames().Num() -1; + GetDialogueWidget()->SetTextBlock(GetCurrentRow()->ScriptForExit, GetCurrentRow()->Speaker); + GetDialogueWidget()->StartTyping(GetCurrentRow()->ScriptForExit,0.05); + GetDialogueWidget()->SetImageOpacity(GetCurrentRow()->Speaker); + GetDialogueWidget()->GetDialogueChoiceWidget()->SetVisibility(ESlateVisibility::Hidden); +} + + diff --git a/Source/ProjectMJ/UI/Store/MJStoreComponent.h b/Source/ProjectMJ/UI/Store/MJStoreComponent.h new file mode 100644 index 00000000..78a19aa1 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreComponent.h @@ -0,0 +1,101 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Components/ActorComponent.h" +#include "Dialogue/MJDialogueComponent.h" +#include "MJStoreComponent.generated.h" + + +class UMJInventoryComponent; +struct FGameplayTag; +/* +* Class Description: 상점 시스템 / 상인 NPC에게 달아줘야 함 +* Author: 이지수 +* Created Date: 2025.07.13 +* Last Modified By: 지수 +* Last Modified Date: 2025.08.09 +* */ +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStoreUpdatedEvent, int32, SlotCount); + +USTRUCT(BlueprintType) +struct FStoreData +{ + GENERATED_BODY() + + UPROPERTY() + FName ItemName; + + UPROPERTY() + int32 ItemValue; + + UPROPERTY() + int ItemCount; + + bool IsSoldOut() const + { + return ItemCount == 0; + } + +}; +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class PROJECTMJ_API UMJStoreComponent : public UMJDialogueComponent +{ + GENERATED_BODY() + +protected: + int32 SlotCount; + int32 InvenSlotCount; + bool bIsOpened = false; + bool bIsStoreRoot = false; + + FGameplayTag CurrentItemTag; + int32 CurrentPrice; + int32 CurrentQuantity; + + TArray ItemTagForStore; + + UPROPERTY(BlueprintAssignable, Category = "EventDispatchers") + FOnStoreUpdatedEvent OnStoreUpdated; + +public: + void UpdateStore(); // 바인딩했던 슬롯들 정보 채우기용 + void UpdateInventory(UMJInventoryComponent* InvenComp); + + UFUNCTION() + void TryPurchase(); + + UFUNCTION() + void TrySale(); + + int32 GetSlotCount() const {return SlotCount;} + + //다이어로그 선택지 기능 추가를 위한 함수 + UFUNCTION() + void BindButtons(); + UFUNCTION() + void ShowStory(); + UFUNCTION() + void ShowStore(); + UFUNCTION() + void ExitDialogue(); + UFUNCTION() + void DialogueEnd(); + UFUNCTION() + void SetChoiceWidgetText(); + // + + // 구매 시 PopUp + + bool GetIsOpened() const {return bIsOpened;} + void SetbIsOpened(bool bValue) {bIsOpened = bValue;} + bool GetIsStoreRoot() const {return bIsStoreRoot;} + void SetbIsStoreRoot(bool bValue) {bIsStoreRoot = bValue;}; + + void SetSlotCount(int32 Value) {SlotCount = Value;} + void SetInvenSlotCount(int32 Value) {InvenSlotCount = Value;} + + void SetItemData(TArray ItemTags, int32 slotCount, UMJInventoryComponent* InvenComp); +}; diff --git a/Source/ProjectMJ/UI/Store/MJStoreSlotBase.cpp b/Source/ProjectMJ/UI/Store/MJStoreSlotBase.cpp new file mode 100644 index 00000000..f4da65ff --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreSlotBase.cpp @@ -0,0 +1,126 @@ +// ThenOneDayStudio + + +#include "UI/Store/MJStoreSlotBase.h" +#include "GameplayTagContainer.h" +#include "UI/Store/MJPopupWidget.h" +#include "Components/Button.h" +#include "Components/TextBlock.h" +#include "Components/Image.h" + +void UMJStoreSlotBase::NativeConstruct() +{ + Super::NativeConstruct(); + + Button->OnClicked.AddDynamic(this, &ThisClass::TryPurchase); + PlusButton->OnClicked.AddDynamic(this, &ThisClass::OnClicked_PlusButton); + MinusButton->OnClicked.AddDynamic(this, &ThisClass::OnClicked_MinusButton); + PlusTenButton->OnClicked.AddDynamic(this, &ThisClass::OnClicked_PlusTenButton); + MinusTenButton->OnClicked.AddDynamic(this, &ThisClass::OnClicked_MinusTenButton); +} + +void UMJStoreSlotBase::SetItemTag(FGameplayTag tag) +{ + ItemTag = tag; +} + +void UMJStoreSlotBase::SetImage(UTexture2D* ItemTexture) +{ + if (MerImage) + { + FSlateBrush Brush; + Brush.SetResourceObject(ItemTexture); + Brush.ImageSize = FVector2D(80,80); + MerImage->SetBrush(Brush); + MerImage->SetOpacity(1.0); + } +} + +void UMJStoreSlotBase::SetItemName(FText itemName) +{ + if (ItemName) + { + ItemName->SetText(itemName); + } +} + +void UMJStoreSlotBase::SetDescription(FText description) +{ + if (Description) + { + Description->SetText(description); + } +} + +void UMJStoreSlotBase::SetPrice(int price) +{ + if (PriceText) + { + PriceText->SetText(FText::FromString(FString::Printf(TEXT("%d G"), price))); + } + Price = price; +} + +void UMJStoreSlotBase::InitializeQuantity() +{ + Quantity = 0; + SetQuantity(Quantity); +} + +void UMJStoreSlotBase::SetQuantity(int32 delta) +{ + QuantityText->SetText(FText::AsNumber(delta)); + Quantity = delta; +} + +void UMJStoreSlotBase::TryPurchase() +{ + UE_LOG(LogTemp, Display, TEXT("TryPurchase BUTTON CLICKED")); + OnMerchandiseSlotEvent.Broadcast(ItemTag,Price,Quantity); +} + +void UMJStoreSlotBase::OnClicked_PlusButton() +{ + Quantity ++; + if (Quantity >= MaxQuantity) + { + Quantity = MaxQuantity; + } + SetQuantity(Quantity); +} + +void UMJStoreSlotBase::OnClicked_MinusButton() +{ + if (Quantity == 0) + { + SetQuantity(Quantity); + return; + } + + Quantity --; + + SetQuantity(Quantity); +} + +void UMJStoreSlotBase::OnClicked_PlusTenButton() +{ + Quantity += 10; + if (Quantity >= MaxQuantity) + { + Quantity = MaxQuantity; + } + SetQuantity(Quantity); +} + +void UMJStoreSlotBase::OnClicked_MinusTenButton() +{ + Quantity -= 10; + if (Quantity <= 0) + { + Quantity = 0; + // SetQuantity(Quantity); + // return; + } + SetQuantity(Quantity); +} + diff --git a/Source/ProjectMJ/UI/Store/MJStoreSlotBase.h b/Source/ProjectMJ/UI/Store/MJStoreSlotBase.h new file mode 100644 index 00000000..fc678357 --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreSlotBase.h @@ -0,0 +1,109 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Blueprint/UserWidget.h" +#include "MJStoreSlotBase.generated.h" + +/** +* Class Description: 스토어 위젯의 슬롯들 부모님 + * Author: 이지수 + * Created Date: 2025.08.09 + * Last Modified By: + * Last Modified Date: + */ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnMerchandiseSlotEvent, FGameplayTag&, ItemTag, int32, Price, int32, Quantity); + +class UMJStoreComponent; +struct FGameplayTag; +class UMJPopupWidget; +class UButton; +class UTextBlock; +class UImage; +UCLASS() +class PROJECTMJ_API UMJStoreSlotBase : public UUserWidget +{ + GENERATED_BODY() +protected: + UPROPERTY() + TObjectPtr StoreComponent; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Button; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MerImage; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr ItemName; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Description; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PriceText; + + // 개수 조절 + UPROPERTY(meta = (BindWidget)) + TObjectPtr QuantityText; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PlusButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MinusButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PlusTenButton; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr MinusTenButton; + + FGameplayTag ItemTag; + int32 Price; + int32 Quantity = 0; + + bool IsEmpty = true; + + int32 MaxQuantity = 0; + +public: + UFUNCTION() + virtual void NativeConstruct(); + + UButton* GetButton() { return Button; } + UButton* GetPlusButton() { return PlusButton; } + UButton* GetMinusButton() { return MinusButton; } + + void SetItemTag(FGameplayTag tag); + void SetImage(UTexture2D* ItemTexture); + void SetItemName(FText itemName); + void SetDescription(FText description); + void SetPrice(int32 price); + void InitializeQuantity(); + + UFUNCTION() + void SetQuantity(int32 delta); + + UFUNCTION() + virtual void TryPurchase(); + + UFUNCTION() + virtual void OnClicked_PlusButton(); + UFUNCTION() + virtual void OnClicked_MinusButton(); + UFUNCTION() + virtual void OnClicked_PlusTenButton(); + UFUNCTION() + virtual void OnClicked_MinusTenButton(); + + FOnMerchandiseSlotEvent OnMerchandiseSlotEvent; + + bool GetIsEmpty() { return IsEmpty; } + void SetIsEmpty(bool isEmpty) { IsEmpty = isEmpty; } + void SetMaxQuantity(int32 maxQuantity) { MaxQuantity = maxQuantity; } + +}; diff --git a/Source/ProjectMJ/UI/Store/MJStoreWidget.cpp b/Source/ProjectMJ/UI/Store/MJStoreWidget.cpp new file mode 100644 index 00000000..ecad00af --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreWidget.cpp @@ -0,0 +1,120 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/Store/MJStoreWidget.h" +#include "MJPopupWidget.h" +#include "MJSalesSlot.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/Button.h" +#include "Components/ScrollBox.h" +#include "Components/TextBlock.h" +#include "UI/Store/MJMerchandiseSlot.h" + +void UMJStoreWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + // 상점에 슬롯채우기 + if (!ScrollBox || !MerchandiseSlotClass) + { + UE_LOG(LogTemp,Error,TEXT("상점용 슬롯 클래스가 없다")); + return; + } + + for (int i = 0; i < 4; i++) + { + UMJMerchandiseSlot* NewSlot = CreateWidget(this, MerchandiseSlotClass); + ScrollBox->AddChild(NewSlot); + MerchandiseSlots.Add(NewSlot); + } + // + + //Delegate + PurchasePopup->SetVisibility(ESlateVisibility::Hidden); + PurchasePopup->GetYesButton()->OnClicked.AddDynamic(this,&UMJStoreWidget::OnClicked_PurchasePopupYes); + PurchasePopup->GetNoButton()->OnClicked.AddDynamic(this,&UMJStoreWidget::OnClicked_PurchasePopupNo); + + SellPopup->SetVisibility(ESlateVisibility::Hidden); + SellPopup->GetYesButton()->OnClicked.AddDynamic(this,&UMJStoreWidget::OnClicked_SellPopupYes); + SellPopup->GetNoButton()->OnClicked.AddDynamic(this,&UMJStoreWidget::OnClicked_SellPopupNo); + // +} + +void UMJStoreWidget::UpdateInventorySlot() +{ + if (!InvenScrollBox || !InventorySlotClass) + { + return; + } + + UMJSalesSlot* NewSlot = CreateWidget(this, InventorySlotClass); + InvenScrollBox->AddChild(NewSlot); + InventorySlots.Add(NewSlot); +} + +void UMJStoreWidget::SetStatComponent(UMJPlayerStatComponent* StatComp) +{ + StatCompRef = StatComp; + if (StatCompRef) + { + SetAvailableGold(StatCompRef->GetGold()); // 초기화 + StatCompRef->OnGoldChange.AddDynamic(this,&UMJStoreWidget::SetAvailableGold); + } +} + +void UMJStoreWidget::SetAvailableGold(int32 Gold) +{ + AvailableGold->SetText(FText::FromString(FString::FromInt(Gold))); +} + +void UMJStoreWidget::Onclicked_PurchaseButton() +{ + PurchasePopup->SetVisibility(ESlateVisibility::Visible); +} + +void UMJStoreWidget::OnClicked_PurchasePopupYes() +{ + PurchasePopup->SetVisibility(ESlateVisibility::Hidden); + OnClickedPurchaseYes.Broadcast(); +} + +void UMJStoreWidget::OnClicked_PurchasePopupNo() +{ + PurchasePopup->SetVisibility(ESlateVisibility::Hidden); +} + +void UMJStoreWidget::Onclicked_SellButton() +{ + SellPopup->SetVisibility(ESlateVisibility::Visible); +} + +void UMJStoreWidget::OnClicked_SellPopupYes() +{ + SellPopup->SetVisibility(ESlateVisibility::Hidden); + OnClickedSellYes.Broadcast(); +} + +void UMJStoreWidget::OnClicked_SellPopupNo() +{ + SellPopup->SetVisibility(ESlateVisibility::Hidden); +} + +void UMJStoreWidget::CloseWidget() +{ + // 팝업이 열린 상태로 상점을 닫는 경우, 나중에 다시 켰을 때 팝업이 꺼져있게 하기 위함 + PurchasePopup->SetVisibility(ESlateVisibility::Hidden); + SellPopup->SetVisibility(ESlateVisibility::Hidden); + + // 늘려놨던 개수들도 리셋 + for (int i =0; i < MerchandiseSlots.Num(); i++) + { + MerchandiseSlots[i]->InitializeQuantity(); + } + for (int i =0; i < InventorySlots.Num(); i++) + { + InventorySlots[i]->InitializeQuantity(); + } + +} + + diff --git a/Source/ProjectMJ/UI/Store/MJStoreWidget.h b/Source/ProjectMJ/UI/Store/MJStoreWidget.h new file mode 100644 index 00000000..1b9565be --- /dev/null +++ b/Source/ProjectMJ/UI/Store/MJStoreWidget.h @@ -0,0 +1,96 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "Blueprint/UserWidget.h" +#include "MJStoreWidget.generated.h" + + +class UMJSalesSlot; +class UMJPlayerStatComponent; +class UTextBlock; +/** +* Class Description: 상점창 + * Author: 이지수 + * Created Date: 2025.08.09 + * Last Modified By: + * Last Modified Date: + */ +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnClickedYes); + +class UMJPopupWidget; +class UScrollBox; +class UMJMerchandiseSlot; +UCLASS() +class PROJECTMJ_API UMJStoreWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr ScrollBox; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr InvenScrollBox; + + UPROPERTY(meta = (BindWidget)) + TArray> MerchandiseSlots; + + UPROPERTY(meta = (BindWidget)) + TArray> InventorySlots; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Store) + TSubclassOf MerchandiseSlotClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Store) + TSubclassOf InventorySlotClass; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr PurchasePopup; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr SellPopup; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr AvailableGold; + + UPROPERTY() + UMJPlayerStatComponent* StatCompRef; +public: + virtual void NativeConstruct() override; + + void UpdateInventorySlot(); + + TArray GetMerchandiseSlots() {return MerchandiseSlots;} + TArray GetInventorySlots() {return InventorySlots;} + + void SetStatComponent(UMJPlayerStatComponent* StatComp); + + UFUNCTION() + void SetAvailableGold(int32 Gold); + + // 구매 팝업 관련 + UFUNCTION() + void Onclicked_PurchaseButton(); + UFUNCTION() + void OnClicked_PurchasePopupYes(); + UFUNCTION() + void OnClicked_PurchasePopupNo(); + + + // 판매 팝업관련 + UFUNCTION() + void Onclicked_SellButton(); + UFUNCTION() + void OnClicked_SellPopupYes(); + UFUNCTION() + void OnClicked_SellPopupNo(); + + UMJPopupWidget* GetPurchasePopup() {return PurchasePopup;}; + void CloseWidget(); + + FOnClickedYes OnClickedPurchaseYes; + FOnClickedYes OnClickedSellYes; +}; diff --git a/Source/ProjectMJ/UI/World/MJDamageComponent.cpp b/Source/ProjectMJ/UI/World/MJDamageComponent.cpp new file mode 100644 index 00000000..76c67378 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJDamageComponent.cpp @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJDamageComponent.h" + +#include "UI/World/MJDamageWidget.h" + +UMJDamageComponent::UMJDamageComponent() +{ + static ConstructorHelpers::FClassFinder DamageWidgetRef(TEXT("/Game/UI/WBP/World/WBP_Damage.WBP_Damage_C")); + + if (DamageWidgetRef.Class) + { + SetWidgetClass(DamageWidgetRef.Class); + } +} + +void UMJDamageComponent::SetDamageWidget(FVector CharacterLocation, float OffSet) +{ + // 기타 세팅 + SetWidgetSpace(EWidgetSpace::Screen); + + // // 1. 배 주변 랜덤 생성할 경우 + // float OffsetX = FMath::FRandRange(0.f, 100.f); + // float OffsetZ= FMath::FRandRange(0.f, 100.f); + // SetWorldLocation(CharacterLocation + FVector(OffsetX, 0, OffsetZ)); // 랜덤하게 뜨게 해보자 배 주변 와다닥 + + // 2. 메이플스토리 마냥 위로 쌓이게 할라면? + SetWorldLocation(CharacterLocation + FVector(0, 0, OffSet * 50)); + + SetDrawSize(FVector2D(30.0f,30.0f)); + SetCollisionEnabled(ECollisionEnabled::NoCollision); +} + + diff --git a/Source/ProjectMJ/UI/World/MJDamageComponent.h b/Source/ProjectMJ/UI/World/MJDamageComponent.h new file mode 100644 index 00000000..c54d1959 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJDamageComponent.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/WidgetComponent.h" +#include "MJDamageComponent.generated.h" + +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJDamageComponent : public UWidgetComponent +{ + GENERATED_BODY() + + UMJDamageComponent(); +protected: + +public: + void SetDamageWidget(FVector CharacterLocation, float OffSet); +}; diff --git a/Source/ProjectMJ/UI/World/MJDamageWidget.cpp b/Source/ProjectMJ/UI/World/MJDamageWidget.cpp new file mode 100644 index 00000000..6d200812 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJDamageWidget.cpp @@ -0,0 +1,63 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UI/World/MJDamageWidget.h" +#include "Components/TextBlock.h" +#include "Animation/WidgetAnimationEvents.h" +#include "Character/MJCharacterBase.h" + +void UMJDamageWidget::NativeConstruct() +{ + Super::NativeConstruct(); + +} + + +void UMJDamageWidget::SetDamage(float damage, bool IsCritical, EOwnerType type) +{ + Damage->SetText(FText::AsNumber(damage)); + + SetDamageColor(IsCritical, type); + +} + +void UMJDamageWidget::SetDamageColor(bool IsCritical, EOwnerType type) +{ + switch (type) + { + case EOwnerType::Player: + Damage->SetColorAndOpacity( FSlateColor(FLinearColor::Red)); + break; + + case EOwnerType::Monster: + if (IsCritical) + { + Damage->SetColorAndOpacity( FSlateColor(FLinearColor::Yellow)); + } + else + { + Damage->SetColorAndOpacity( FSlateColor(FLinearColor::White)); + } + break; + default: + break; + } +} + +void UMJDamageWidget::PlayAnim() +{ + if (DamageAnim) + { + PlayAnimation(DamageAnim); + } +} + +void UMJDamageWidget::OnAnimFinished() +{ + // jisoo : 컴포넌트가 사라지면 어차피 widget도 사라지기는 하는데, + // 이렇게 하면 나중에 몹이 많을 때 메모리 차지 심해질까봐 그냥 애니메이션 끝나면 위젯 죽어라 하는중입니다. + RemoveFromParent(); +} + + + diff --git a/Source/ProjectMJ/UI/World/MJDamageWidget.h b/Source/ProjectMJ/UI/World/MJDamageWidget.h new file mode 100644 index 00000000..b8d1fac3 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJDamageWidget.h @@ -0,0 +1,39 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJDamageWidget.generated.h" + +enum class EOwnerType : uint8; +struct FGameplayModifierEvaluatedData; +class UMJCharacterAttributeSet; +class UMJAbilitySystemComponent; +class UTextBlock; +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJDamageWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr Damage; + + UPROPERTY(meta = (BindWidgetAnim),Transient) + TObjectPtr DamageAnim; + + UFUNCTION() + void OnAnimFinished(); +public: + virtual void NativeConstruct() override; + void SetDamage(float damage, bool IsCritical, EOwnerType type); + void SetDamageColor(bool IsCritical, EOwnerType type); + void PlayAnim(); + + FWidgetAnimationDynamicEvent AnimFinishedDelegate; + +}; diff --git a/Source/ProjectMJ/UI/World/MJInteractionComponent.cpp b/Source/ProjectMJ/UI/World/MJInteractionComponent.cpp new file mode 100644 index 00000000..5e9869b8 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJInteractionComponent.cpp @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJInteractionComponent.h" +#include "MJInteractionWidget.h" + +UMJInteractionComponent::UMJInteractionComponent() +{ + static ConstructorHelpers::FClassFinder InteractionWidgetRef(TEXT("/Game/UI/WBP/World/WBP_DialogueInteraction.WBP_DialogueInteraction_C")); + + if (InteractionWidgetRef.Class) + { + SetWidgetClass(InteractionWidgetRef.Class); + } + + SetInteractionWidget(); +} + +void UMJInteractionComponent::SetInteractionWidget() +{ + // 기타 세팅 + SetRelativeLocation(FVector(0.0f, 0.0f, 250.0f)); + SetWidgetSpace(EWidgetSpace::Screen); + SetDrawSize(FVector2D(80.0f,80.0f)); + SetCollisionEnabled(ECollisionEnabled::NoCollision); + SetVisibility(false); +} + +void UMJInteractionComponent::Active(FString key) +{ + SetVisibility(true); + UMJInteractionWidget* InteractionWidget = Cast(GetUserWidgetObject()); + InteractionWidget->SetText(key); + InteractionWidget->PlayTestAnimation(); +} + +void UMJInteractionComponent::Deactive() +{ + SetVisibility(false); +} + diff --git a/Source/ProjectMJ/UI/World/MJInteractionComponent.h b/Source/ProjectMJ/UI/World/MJInteractionComponent.h new file mode 100644 index 00000000..5b2e9af9 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJInteractionComponent.h @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/WidgetComponent.h" +#include "MJInteractionComponent.generated.h" + +class UMJUIManagerSubsystem; +/** + * + */ +UCLASS( meta=(BlueprintSpawnableComponent)) +class PROJECTMJ_API UMJInteractionComponent : public UWidgetComponent +{ + GENERATED_BODY() + + UMJInteractionComponent(); + +public: + void SetInteractionWidget(); + void Active(FString key); + void Deactive(); + +}; diff --git a/Source/ProjectMJ/UI/World/MJInteractionWidget.cpp b/Source/ProjectMJ/UI/World/MJInteractionWidget.cpp new file mode 100644 index 00000000..e352eb6a --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJInteractionWidget.cpp @@ -0,0 +1,18 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJInteractionWidget.h" +#include "Components/TextBlock.h" + +void UMJInteractionWidget::SetText(FString text) +{ + Key->SetText(FText::FromString(text)); +} + +void UMJInteractionWidget::PlayTestAnimation() +{ + if (Test) + { + PlayAnimation(Test,0.f,0); + } +} diff --git a/Source/ProjectMJ/UI/World/MJInteractionWidget.h b/Source/ProjectMJ/UI/World/MJInteractionWidget.h new file mode 100644 index 00000000..5d8b5174 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJInteractionWidget.h @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJInteractionWidget.generated.h" + +class UBorder; +class UTextBlock; +/** + * + */ +UCLASS() +class PROJECTMJ_API UMJInteractionWidget : public UUserWidget +{ + GENERATED_BODY() + + UPROPERTY(meta = (BindWidget)) + TObjectPtr Key; + + UPROPERTY(meta = (BindWidgetAnim), Transient) + TObjectPtr Test; + +public: + void SetText(FString text); + void PlayTestAnimation(); +}; diff --git a/Source/ProjectMJ/UI/World/MJNameBarWidget.cpp b/Source/ProjectMJ/UI/World/MJNameBarWidget.cpp new file mode 100644 index 00000000..40ebc74c --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJNameBarWidget.cpp @@ -0,0 +1,10 @@ +// ThenOneDayStudio + + +#include "UI/World/MJNameBarWidget.h" +#include "Components/TextBlock.h" + +void UMJNameBarWidget::SetNameText(FText Name) +{ + NameText->SetText(Name); +} diff --git a/Source/ProjectMJ/UI/World/MJNameBarWidget.h b/Source/ProjectMJ/UI/World/MJNameBarWidget.h new file mode 100644 index 00000000..07fdebb1 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJNameBarWidget.h @@ -0,0 +1,29 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "MJNameBarWidget.generated.h" + +class UTextBlock; +/** +* Class Description: 네임바 위젯 + * Author: 이지수 + * Created Date: 2025.08.12 + * Last Modified By: + * Last Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJNameBarWidget : public UUserWidget +{ + GENERATED_BODY() + +protected: + UPROPERTY(meta = (BindWidget)) + TObjectPtr NameText; + +public: + UFUNCTION() + void SetNameText(FText Name); +}; diff --git a/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.cpp b/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.cpp new file mode 100644 index 00000000..63a990ba --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.cpp @@ -0,0 +1,25 @@ +// ThenOneDayStudio + + +#include "UI/World/MJNameBarWidgetComponent.h" +#include "MJNameBarWidget.h" + +UMJNameBarWidgetComponent::UMJNameBarWidgetComponent() +{ + static ConstructorHelpers::FClassFinder NameBarWidgetRef(TEXT("/Game/UI/WBP/World/WBP_ItemNameBar.WBP_ItemNameBar_C")); + + if (NameBarWidgetRef.Class) + { + SetWidgetClass(NameBarWidgetRef.Class); + } + + SetNameBarWidget(); +} + +void UMJNameBarWidgetComponent::SetNameBarWidget() +{ + SetWidgetSpace(EWidgetSpace::Screen); + SetCollisionEnabled(ECollisionEnabled::NoCollision); + SetRelativeLocation(FVector(0.f, 0.f, 100.f)); + SetDrawSize({100,40}); +} diff --git a/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.h b/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.h new file mode 100644 index 00000000..9fc151dd --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJNameBarWidgetComponent.h @@ -0,0 +1,24 @@ +// ThenOneDayStudio + +#pragma once + +#include "CoreMinimal.h" +#include "Components/WidgetComponent.h" +#include "MJNameBarWidgetComponent.generated.h" + +/** +* Class Description: 네임바 위젯 컴포넌트 + * Author: 이지수 + * Created Date: 2025.08.12 + * Last Modified By: + * Last Modified Date: + */ +UCLASS() +class PROJECTMJ_API UMJNameBarWidgetComponent : public UWidgetComponent +{ + GENERATED_BODY() + + UMJNameBarWidgetComponent(); +public: + void SetNameBarWidget(); +}; diff --git a/Source/ProjectMJ/UI/World/MJStatWidget.cpp b/Source/ProjectMJ/UI/World/MJStatWidget.cpp new file mode 100644 index 00000000..688de9e2 --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJStatWidget.cpp @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MJStatWidget.h" +#include "AbilitySystem/MJAbilitySystemComponent.h" +#include "AbilitySystem/Attributes/MJCharacterAttributeSet.h" +#include "Character/Component/MJPlayerStatComponent.h" +#include "Components/TextBlock.h" + +void UMJStatWidget::BindToAttribute(UMJAbilitySystemComponent* ASC, UMJCharacterAttributeSet* AttributeSet, UMJPlayerStatComponent* Stat) +{ + if (!ASC || !AttributeSet) + { + return; + } + + this->AttributSet = AttributeSet; + + Health = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetMaxHealthAttribute()); + AttackPower = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetAttackDamageAttribute()); + SpellPower = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetAbilityPowerAttribute()); + Speed = ASC->GetNumericAttribute(UMJCharacterAttributeSet::GetAbilityPowerAttribute()); + + Stat->OnLevelUp.AddDynamic(this, &UMJStatWidget::UpdateLevel); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxHealthAttribute()).AddUObject(this, &UMJStatWidget::UpdateStat); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetAttackDamageAttribute()).AddUObject(this, &UMJStatWidget::UpdateStat); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetAbilityPowerAttribute()).AddUObject(this, &UMJStatWidget::UpdateStat); + ASC->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxMovementSpeedAttribute()).AddUObject(this, &UMJStatWidget::UpdateStat); + + UpdateStat(FOnAttributeChangeData{}); +} + +void UMJStatWidget::UpdateLevel(int32 NewLevel) +{ + if (StatLevel) + { + StatLevel->SetText(FText::FromString(FString::Printf(TEXT("%d"), NewLevel))); + } +} + +void UMJStatWidget::UpdateStat(const FOnAttributeChangeData& Data) // settext +{ + FGameplayAttribute ChangedAttr = Data.Attribute; + if (ChangedAttr == AttributSet->GetMaxHealthAttribute()) + { + if (StatHealth) + { + Health = Data.NewValue; + StatHealth->SetText(FText::FromString(FString::Printf(TEXT("%.0f"), Health))); + } + } + + if (ChangedAttr == AttributSet->GetAttackDamageAttribute()) + { + if (StatAttackPower) + { + AttackPower = Data.NewValue; + StatAttackPower->SetText(FText::FromString(FString::Printf(TEXT("%.0f"), AttackPower))); + } + } + if (ChangedAttr == AttributSet->GetAbilityPowerAttribute()) + { + if (StatSpellPower) + { + SpellPower = Data.NewValue; + StatSpellPower->SetText(FText::FromString(FString::Printf(TEXT("%.0f"), SpellPower))); + } + } + + if (ChangedAttr == AttributSet->GetMaxMovementSpeedAttribute()) + { + if (StatSpeed) + { + Speed = Data.NewValue; + StatSpeed->SetText(FText::FromString(FString::Printf(TEXT("%.0f"), Speed))); + } + } + +} diff --git a/Source/ProjectMJ/UI/World/MJStatWidget.h b/Source/ProjectMJ/UI/World/MJStatWidget.h new file mode 100644 index 00000000..e58475ce --- /dev/null +++ b/Source/ProjectMJ/UI/World/MJStatWidget.h @@ -0,0 +1,53 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "AbilitySystemComponent.h" +#include "MJStatWidget.generated.h" + +class UMJCharacterAttributeSet; +class UMJAttributeSet; +/** + * + */ +class UTextBlock; + +UCLASS() +class PROJECTMJ_API UMJStatWidget : public UUserWidget +{ + GENERATED_BODY() + +private: + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatLevel; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatHealth; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatAttackPower; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatSpellPower; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr StatSpeed; + + UPROPERTY() + TObjectPtr AttributSet; + + float Health; + float AttackPower; + float SpellPower; + float Speed; + +public: + UFUNCTION() + void BindToAttribute(class UMJAbilitySystemComponent* ASC, class UMJCharacterAttributeSet* AttributeSet, class UMJPlayerStatComponent* Stat); + + UFUNCTION() + void UpdateLevel(int32 NewLevel); + void UpdateStat(const FOnAttributeChangeData& Data); +};