diff --git a/EndlessVendetta/Config/DefaultEngine.ini b/EndlessVendetta/Config/DefaultEngine.ini index 18ee5e14..d13429f4 100644 --- a/EndlessVendetta/Config/DefaultEngine.ini +++ b/EndlessVendetta/Config/DefaultEngine.ini @@ -36,6 +36,7 @@ +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="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",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="Projectile",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Projectile",CustomResponses=,HelpMessage="Preset for projectiles") ++Profiles=(Name="Vault",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="Needs description") +DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Projectile") +DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Waypoint") +EditProfiles=(Name="Trigger",CustomResponses=((Channel="Projectile",Response=ECR_Ignore))) @@ -128,6 +129,7 @@ AppliedDefaultGraphicsPerformance=Maximum +ActiveClassRedirects=(OldClassName="TP_FirstPersonProjectile",NewClassName="EndlessVendettaProjectile") +ActiveClassRedirects=(OldClassName="TP_FirstPersonGameMode",NewClassName="EndlessVendettaGameMode") +ActiveClassRedirects=(OldClassName="TP_FirstPersonCharacter",NewClassName="EndlessVendettaCharacter") +AssetManagerClassName=/Script/VaultIt.VIAssetManager [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True diff --git a/EndlessVendetta/Config/DefaultGameplayTags.ini b/EndlessVendetta/Config/DefaultGameplayTags.ini new file mode 100644 index 00000000..283178ec --- /dev/null +++ b/EndlessVendetta/Config/DefaultGameplayTags.ini @@ -0,0 +1,16 @@ +[/Script/GameplayTags.GameplayTagsSettings] +ImportTagsFromConfig=True +WarnOnInvalidTags=True +ClearInvalidTags=False +AllowEditorTagUnloading=True +AllowGameTagUnloading=False +FastReplication=True +InvalidTagCharacters="\"\'," ++CommonlyReplicatedTags=State.Vaulting ++CommonlyReplicatedTags=State.VaultingRemoval +NumBitsForContainerSize=6 +NetIndexFirstBitSegment=16 ++GameplayTagList=(Tag="Ability.Vault",DevComment="") ++GameplayTagList=(Tag="State.Vaulting",DevComment="") ++GameplayTagList=(Tag="State.VaultingRemoval",DevComment="") + diff --git a/EndlessVendetta/Content/Levels/TrainingFacility.umap b/EndlessVendetta/Content/Levels/TrainingFacility.umap index adaa584c..7850a572 100644 --- a/EndlessVendetta/Content/Levels/TrainingFacility.umap +++ b/EndlessVendetta/Content/Levels/TrainingFacility.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:382de64e5d1bd7a390f39800df5728e144d6c7198abc0f7e942b17816fdf175d -size 679254 +oid sha256:23f1d30713deb7b63e333e5bffd36f97365280f94c773c2b5a1128418a658c42 +size 679253 diff --git a/EndlessVendetta/Content/StarterContent/Architecture/Floor_400x400.uasset b/EndlessVendetta/Content/StarterContent/Architecture/Floor_400x400.uasset index 9fe9e00c..c2a34afc 100644 --- a/EndlessVendetta/Content/StarterContent/Architecture/Floor_400x400.uasset +++ b/EndlessVendetta/Content/StarterContent/Architecture/Floor_400x400.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dab05352d920eb93c5a61a681634f187e65971458c68dd559602b2510811889 -size 14831 +oid sha256:4c0ecf0ac0702cc2bd355ec050232a61c49e10f102448efbeb735a51824adef6 +size 14948 diff --git a/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset b/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset index c5c3b84e..a0b975e9 100644 --- a/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset +++ b/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a60a29ad596546d481e43dfb8698842a78cc07f4a4b1000fa397cfba4e72331 -size 158206 +oid sha256:d64387f3ed1672d52c8e9e16320a53f60d3928e01b51c09f9bd32f98d6fb31aa +size 43745 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Ability/GA_Vault.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GA_Vault.uasset new file mode 100644 index 00000000..876d9653 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GA_Vault.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3365fec27bac97a5b30cb1f54c2bcd75d701ff407883c0410b618fc07e228fb +size 571723 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_Vault.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_Vault.uasset new file mode 100644 index 00000000..7b645bc3 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_Vault.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e65b5843384888f1d62d95b116e15b91970465139fd066c4d594ab0a34736c8f +size 5929 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_VaultRemoval.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_VaultRemoval.uasset new file mode 100644 index 00000000..2711892b --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Ability/GE_VaultRemoval.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cc7a96ea09e07571c3e7f48b84beba3a93fbbee068cb6110e8dd0ab09499829 +size 5502 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AB_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AB_VaultIt_Dog.uasset new file mode 100644 index 00000000..454d32d8 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AB_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfee5421a7475d150ecfe69801a87c855fc58b412a596619d97994034c253f5c +size 197759 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonIdle.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonIdle.uasset new file mode 100644 index 00000000..0699f4f4 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonIdle.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbf8213f85f2aa9b738cfe4fb76c6bb2fde293d10ddebaf53d7c7b57bc0f49c4 +size 173630 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Loop.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Loop.uasset new file mode 100644 index 00000000..387ac1ef --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Loop.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2ac97ba2b242695d5565bcef1295769a480348561c5f55c278d60181145c813 +size 127238 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Start.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Start.uasset new file mode 100644 index 00000000..edc77c06 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonJump_Start.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a59630fda5c68ef95070a45a4376fdd27e9ff444c3b921fb711bb9e74374f220 +size 118833 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonRun.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonRun.uasset new file mode 100644 index 00000000..a8170132 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonRun.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2217b141d64c68acf8edf091d4d229097daed2c96a71f67f0bb4686ea7b44bd5 +size 120046 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonWalk.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonWalk.uasset new file mode 100644 index 00000000..3f0aebf9 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPersonWalk.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59b5129d5c3a3e4c20fdef0d84adea861311cb4148f8aa9615bd0ba67bd705bd +size 131851 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_IdleRun_2D.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_IdleRun_2D.uasset new file mode 100644 index 00000000..53c3d593 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_IdleRun_2D.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b03c7cc251cafeb3baf14bd7cc8ee5cb12b837401d4f3be0e2b8d37c05498e65 +size 92612 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_Jump.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_Jump.uasset new file mode 100644 index 00000000..bb93e25f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsFP/FirstPerson_Jump.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea0dd6f4ab4d2c631127b808b332c9cb384fade6a6dc5721a7d01a5c227b34ab +size 111098 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonIdle.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonIdle.uasset new file mode 100644 index 00000000..61c523a0 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonIdle.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5760add9800ccbc64b9ce411a3c7049ad85a78ce78c8aae50bf3fd5993d2f1aa +size 150254 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_End.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_End.uasset new file mode 100644 index 00000000..8bb315b6 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_End.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73f3f733f90747e0d1656368a167f36a2ceeca494e66a07b2faf7ffb4fe6a878 +size 116275 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Loop.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Loop.uasset new file mode 100644 index 00000000..b8e08e60 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Loop.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5abd97b9ba02925df90ca5eb9f4129e10d90c08d0dc0b0371ad80912e9dcc0cb +size 123499 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Start.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Start.uasset new file mode 100644 index 00000000..e994b971 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonJump_Start.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52b5a6f4d267367615811150634317780b33f350c0ada1f44daf468b95b14555 +size 118621 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonRun.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonRun.uasset new file mode 100644 index 00000000..eab1b381 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonRun.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f69c78a62c1d3b4e32968a50c5b3ed38dd042e96f106ede19c4604ba09e5f25 +size 120116 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonWalk.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonWalk.uasset new file mode 100644 index 00000000..0163e95f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPersonWalk.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d1828d6405429dfbcee2e55aa0586f759b58200d974416f417c0c4d93248188 +size 130199 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_IdleRun_2D.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_IdleRun_2D.uasset new file mode 100644 index 00000000..7ee3daa3 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_IdleRun_2D.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:462e30770035c02ead135f6e462a8eb84ce0dcbe5e7d93a18703f92b44b8c9d5 +size 100303 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_Jump.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_Jump.uasset new file mode 100644 index 00000000..83e4ad89 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsTP/ThirdPerson_Jump.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba16e6f20abcf3f32afa3efe0182d35674607d4da6c2bab7543d93731a36ccfd +size 116259 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_BothHands.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_BothHands.uasset new file mode 100644 index 00000000..b9f94c68 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_BothHands.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4592c3c9e9059a6ba5350a5e9fd44a4abe6eb60b9612e869a2f64a185008b3c +size 163779 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_LeftHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_LeftHand.uasset new file mode 100644 index 00000000..025ec91f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_LeftHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1a0ca5e44c32789cdb40e0c7b8ea3f7d63d80d7ddf24d7926f77c4b8575a19c +size 120398 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_RightHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_RightHand.uasset new file mode 100644 index 00000000..6a8b6532 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/CR_Dog_RightHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ea7ac3d06b46dd33899627a9ae77ca047c1cb64a08ed36942912ef1cf187c18 +size 120418 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100.uasset new file mode 100644 index 00000000..4f98e181 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bc9bdd434e28077a9ec7c44e72fe3fc7d996f835c0797a65e756df05ccb7a2b +size 119989 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100_Montage.uasset new file mode 100644 index 00000000..e49fd731 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_100_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fffc31873d32ea0302137f352954cbc35eca66d5152d049029110c5bd34454bd +size 12817 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150.uasset new file mode 100644 index 00000000..5426f3c5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1eb1ea0915e9d435c1a6372ac9f1ae857655c19cc5275231e66772d7d7fb051c +size 125435 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150_Montage.uasset new file mode 100644 index 00000000..8aa15951 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_150_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff38b862295f10073ae5c8a947dc26a4b8e689c3cfe2999d6c05b4f04bf3e4ea +size 14351 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200.uasset new file mode 100644 index 00000000..9d2760bd --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13acd01b316308b1ca85566d7466be0b8f22e9777ccf2022ca9a428adb2df12d +size 146899 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200_Montage.uasset new file mode 100644 index 00000000..d775dd5b --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_200_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52c4a59430916d63c67cb7727e5b981272d3f7b070b1c1a13284fd82859479f6 +size 17753 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250.uasset new file mode 100644 index 00000000..3f44c796 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd82a70de652e7b5dae841acfa45e59622ce88e5e57f2bc87401288e0b3260b9 +size 159537 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250_Montage.uasset new file mode 100644 index 00000000..bcad7388 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_250_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7aa7f2d00ec7f4458c9548d1db970c7c5922ffb31c147cf85331c2d4fc8e217 +size 16443 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45.uasset new file mode 100644 index 00000000..66c6f1e2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b35a2e57552d0561dfedc35733898eb156d546a783c5195aee1412248bf91d0d +size 111009 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45_Montage.uasset new file mode 100644 index 00000000..8e3e67a8 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_45_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:313e80a6735e50aef38446dced1b16c3125981c2ea63ab7292f665bfdf6c7031 +size 12708 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75.uasset new file mode 100644 index 00000000..d256759a --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8442cf02321d4595f79412904e299b98dab500232c2f9fbc1bfeec3be7990384 +size 114347 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75_Montage.uasset new file mode 100644 index 00000000..a3c60e17 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultDog/VaultIt_Quadruped_75_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a94190b47408743868ddee4b67bf7239a90e497fed8c23dc0fa8ff514017d375 +size 12828 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_BothHands.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_BothHands.uasset new file mode 100644 index 00000000..66d53588 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_BothHands.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0adb28c8e72e6af5bdaf6d2488399a3d04ca29ed948c71cebe647d3d9a76316f +size 189588 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_LeftHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_LeftHand.uasset new file mode 100644 index 00000000..782c7900 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_LeftHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ba216e1c090066029aea37b9c4599bfd365a1266d1dab0d672cf8861899c0ed +size 146340 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_RIghtHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_RIghtHand.uasset new file mode 100644 index 00000000..c06b26a2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/CR_VaultIt_FP_RIghtHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf211a7647a5dd43b964fbf79e8e076ed0cea0b31478c287dfa52112b7bb0552 +size 146177 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100.uasset new file mode 100644 index 00000000..e4a896ba --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b03648c89e663308e13b943b201fc48327fb90a3b4f3be5566822da03dbafe7a +size 123280 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100_Montage.uasset new file mode 100644 index 00000000..5e1bdcdb --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_100_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2d2e98316c2278840c94fccce1e49947819a07af6c1fd464dd220985ef55faf +size 92565 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150.uasset new file mode 100644 index 00000000..10f110d4 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fef0f709ea0912edbb94b8037426e15a3ad0eeffdabaf637d0fd17114153d405 +size 128636 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150_Montage.uasset new file mode 100644 index 00000000..90894e68 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_150_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:486ce47465908bcac3d69571c6b8fc35490fcf356d45c6856072677ea12f47ba +size 96671 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200.uasset new file mode 100644 index 00000000..85e0cdf1 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f338175de4f5e3a14d56828c2a3bfc50579fbf084d4625a69cbfb848cb1d6fe +size 143604 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200_Montage.uasset new file mode 100644 index 00000000..a00a3718 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_200_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58f7e8cc37c7c49c5b6691fee03b22b742c79b385224382800004628f57ec16a +size 97800 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250.uasset new file mode 100644 index 00000000..4573411f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86c982492525d0dfddae90a90f86c14ae23149d0423e555738852a7189d5649c +size 149396 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250_Montage.uasset new file mode 100644 index 00000000..a0a1ad3f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_250_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89f2e0a7016feadd03d2ab9a14b7ee55f75abac40c52a97f4541f21590700265 +size 97292 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45.uasset new file mode 100644 index 00000000..8e4ef366 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd2f3a7fe77b82e8613a1bd1a9a7ebee0b080fe6dc3769f685b49033408cf51e +size 125725 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45_Montage.uasset new file mode 100644 index 00000000..f40615d0 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_45_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c557d75b458fddbedfb91c97f583c349ccca39fddf6b49cd808852f7f3f43ee6 +size 92983 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75.uasset new file mode 100644 index 00000000..83505139 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:469278aa08716615d5f28c47344bbdadbefb97745713e76bfa92b48995f00e28 +size 123421 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75_Montage.uasset new file mode 100644 index 00000000..fa1ec223 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultFP/VaultItFP_Height_75_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41b2a460d9a83c48dfa30947d73f27c65466a5639611c9392c94484a716ba40a +size 92715 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_BothHands.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_BothHands.uasset new file mode 100644 index 00000000..15ed50a2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_BothHands.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d3abd4d022937d9aabd0f92a017ba7e954203f54e66e3b3bf304c6921b637ae +size 197493 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_LeftHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_LeftHand.uasset new file mode 100644 index 00000000..ad4437f9 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_LeftHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:706cdac812d0b877fe51727b9ead1f4d8765ed40f3063f0f647eb64a6b726aac +size 147680 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_RightHand.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_RightHand.uasset new file mode 100644 index 00000000..6490a55f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/CR_VaultIt_Mannequin_RightHand.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40a303ee64ba92b51beaf043f8e58d22631bbf11eba590e7e7e91c4b93027fa1 +size 147624 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100.uasset new file mode 100644 index 00000000..8e863819 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efa708de593856f65cc289e17ce3b53a435e32d0927a52325b2a35dd956d11cb +size 129231 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100_Montage.uasset new file mode 100644 index 00000000..eb8fa2a1 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_100_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e470026e3df5063d01dc68c1625985dffe2743124fbb625fe8ccdc05a90512f +size 12628 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150.uasset new file mode 100644 index 00000000..65a2ad7c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fbf7a0ba28e8e0f98668b7730f9656ee570d4c5afa71a5139c150d50309f7a9 +size 132415 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150_Montage.uasset new file mode 100644 index 00000000..7f84a851 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_150_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9baba6adfb5368702a7ba03f31584665e0009ae52cea0e13f1544db3363ec054 +size 14219 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200.uasset new file mode 100644 index 00000000..0424f688 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e4737f22e66f499d617278ea3e0d8420f0d690be609dc9f13cd489339746e6b +size 150184 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200_Montage.uasset new file mode 100644 index 00000000..1732f438 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_200_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:572847486c52e99412555ab88673bfed289972f018e7388cefe2a137db7614b9 +size 15661 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250.uasset new file mode 100644 index 00000000..96bb125c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b676d67efa01ad692475a73a2cfe604614b1beb0bceeb76dceda8e1a5dbdec74 +size 156017 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250_Montage.uasset new file mode 100644 index 00000000..3d08a6ed --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_250_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d058e5461fa16e1ebaee031a8fd05efb085a7c8ed525139132aaf8a83523135b +size 16097 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45.uasset new file mode 100644 index 00000000..cc50b86b --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8ed22c65c174cca4ccdf5bbc821059cdca04ffa653a90d4cc8da0844cf9a291 +size 129523 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45_Montage.uasset new file mode 100644 index 00000000..f3345c83 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_45_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48a1a24e7abff51c65fc309c6aeddd3b70bbfba8e55dc09963e5545c77c6bd82 +size 12584 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75.uasset new file mode 100644 index 00000000..f6be385e --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:410734eff136f29fcc690df4d7ff6eccdd4ca16a9386b3e5dd408e79dac540fb +size 129713 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75_Montage.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75_Montage.uasset new file mode 100644 index 00000000..bf8d2d47 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/AnimsVaultTP/VaultIt_Height_75_Montage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e9248f32c2351d717b25c0397d67e4f2e2fc36a8e15dbc38dea945f076c6576 +size 12578 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimBP.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimBP.uasset new file mode 100644 index 00000000..b0db1ee5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimBP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68eed825e72503a48e339dc7ed9d75be769ec5149fddbd3750d43ef704f2135c +size 525536 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimCPP.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimCPP.uasset new file mode 100644 index 00000000..5b2ef3fc --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/FirstPerson_AnimCPP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42aaef02d8242a8a2a734c133c791e3552f141b845308bf4aafd41276340f68b +size 271884 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimBP.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimBP.uasset new file mode 100644 index 00000000..c2754f9f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimBP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71c9924f07084d25f69b32653bebdefe9acd187c74dbffce3f8e170489335622 +size 457152 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimCPP.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimCPP.uasset new file mode 100644 index 00000000..05bea5c8 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Anims/ThirdPerson_AnimCPP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc9e70a7f359b4aaa21ecab8191006f8552783c4128a2efc4438ba75d40ce9ad +size 265825 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_FirstPersonCharacter.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_FirstPersonCharacter.uasset new file mode 100644 index 00000000..abe09806 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_FirstPersonCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edaf3b6429e17258e879c82ded01267c2b2ef0ea5ef9b90566fbf2d131ff6d04 +size 284169 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HUD.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HUD.uasset new file mode 100644 index 00000000..4509ee7a --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HUD.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be753a21a923f1aae69fb17c48b6b5c0ee8e65c6e6eafa41af40036ffd3b83d7 +size 113018 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HeightActor.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HeightActor.uasset new file mode 100644 index 00000000..15ffb7a2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_HeightActor.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0e3583507c24aec1de39c1a1925aa00cfe7765fa8c1566f42c2514b771fbcb7 +size 175932 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_JumpNavLink.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_JumpNavLink.uasset new file mode 100644 index 00000000..3d614ff4 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_JumpNavLink.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35132292ac138757447718d19d3063584724c161b3f57213df2cfcc859c02ce1 +size 26571 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacter.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacter.uasset new file mode 100644 index 00000000..348f9d4f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f97ad551dabb113d83556da5e522cebfe7dd1f32386707c0bf8d740cc81f9d4 +size 175392 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacterAI.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacterAI.uasset new file mode 100644 index 00000000..a4f2f30e --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonCharacterAI.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32678c087d0576020754e76005004785ddd8945859017bf2be619d99d8cf0179 +size 128415 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonDog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonDog.uasset new file mode 100644 index 00000000..65b7f854 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonDog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e55454ac8eadb71612ef6447c2e3bdd0c0de1764d5dd53bb8f8967b7ac7a8b8e +size 173314 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonGameMode.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonGameMode.uasset new file mode 100644 index 00000000..21b5f924 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Blueprints/BP_ThirdPersonGameMode.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:601cb27de27c951e53727edcafc483c5adbab6542c6196a1e4a50a569078bbe9 +size 14565 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Dog/M_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Dog/M_VaultIt_Dog.uasset new file mode 100644 index 00000000..fee39f6f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Dog/M_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb5b513596ef83ccdca44445c2831b383985526fb3af88605789556224b68044 +size 102692 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Dog/PHAT_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Dog/PHAT_VaultIt_Dog.uasset new file mode 100644 index 00000000..e8909c6c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Dog/PHAT_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e81b1c3ab219c3703b0bc3ecd3c5b76382cc5950db9a88308b018c9ddd1b9c26 +size 138916 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Dog/SKL_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SKL_VaultIt_Dog.uasset new file mode 100644 index 00000000..bc532ab1 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SKL_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d0278f0d91a5ec927cf3e06b63ead87e171585410d6e767943f75384bb5c26b +size 12416 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Dog/SK_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SK_VaultIt_Dog.uasset new file mode 100644 index 00000000..0b82302f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SK_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0dea832d983dd78d5553260d47c0167a37ac901eaebcc316362d55cff6105b6d +size 2142534 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Dog/SRIG_VaultIt_Dog.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SRIG_VaultIt_Dog.uasset new file mode 100644 index 00000000..24f9cb96 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Dog/SRIG_VaultIt_Dog.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8ebe6485c753bae212629edea433c8cb96d14f867f413e0a8c4e248e3c99c72 +size 38068 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Camera.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Camera.uasset new file mode 100644 index 00000000..8cbb877e --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Camera.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f229274da306981e357a2bf8a9887dccc7d3e2527dbc5942508722c8c2da8d41 +size 1382 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Jump.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Jump.uasset new file mode 100644 index 00000000..d5a40851 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Jump.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6894ba1e10af0dc3de3a588c2d2b2c1efc4f4c0471c4cc84a5b4954eab6ce4c0 +size 1372 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Look.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Look.uasset new file mode 100644 index 00000000..a95f1209 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Look.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a22cd7133ffbaef7e02ae8d3e88b4f9862c717917dfb17b5fce3a3016250978 +size 1519 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Move.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Move.uasset new file mode 100644 index 00000000..da256155 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Move.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f366ded7ed98abe9513bea2b3464f5f3caecaf50d54738d240197eb53787ab5 +size 1519 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Vault.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Vault.uasset new file mode 100644 index 00000000..506d629c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/Actions/IA_VaultIt_Vault.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09489b70a62fe88e10361d75d9317e6184d7c31bc434684e896d56efc532c61e +size 1377 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Input/IMC_VaultIt_Default.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Input/IMC_VaultIt_Default.uasset new file mode 100644 index 00000000..2d58c9de --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Input/IMC_VaultIt_Default.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:420db2ed4f31e60bdff55b19b2849db37cb3d01e999ac3ed76267f03ce9fc670 +size 14109 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_Male_Body.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_Male_Body.uasset new file mode 100644 index 00000000..9cf8a200 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_Male_Body.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0a29eb8912142c240671f6f2f094d6a1ba908f499b323399e9bf91737a00a8d +size 156181 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_UE4Man_ChestLogo.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_UE4Man_ChestLogo.uasset new file mode 100644 index 00000000..df71aaf9 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/M_UE4Man_ChestLogo.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32930931c798a8769d7c18bf393db753cf427bd39cf8b2ddc039c823478aeb1f +size 109296 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset new file mode 100644 index 00000000..235ded94 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2914cf46139832e1a84abd1df8c9cffd4dfdc416b83fbf5c0574ec83511b076f +size 80549 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 00000000..7a77388c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16288356505356b444a3c353b0d3fb1462e8a5344f5a65e6e1c51e8d999974a5 +size 76884 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 00000000..8b048018 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:601bf39df015f229525a2467024cfa2739881632c2e8346b86213669f161a586 +size 78033 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset new file mode 100644 index 00000000..f4f54818 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c0ba6f906c1e0b9b7220d8f86949e0c5bf6d7803878435d862a8a77cc642ef9 +size 81229 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01.uasset new file mode 100644 index 00000000..14a1e879 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:820d3fcd679e374b9ae6e3abb676bf025c4e2f1882cf1d3fe71f3fd38bc94873 +size 457650 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset new file mode 100644 index 00000000..9781ef15 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0a1bd1454208ded735a836db627e4b8d2366b7f4d4f7c82f1e540aed44a6f55 +size 406022 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 00000000..e44bc716 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:040b44407473013b13e6e039843f79653a98e9cd7bc0af884763d4eab1373672 +size 502390 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 00000000..f5cb8d23 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c72d724ef767a40988bb48a1df5eedf6a3ab3aeef50892b7206fdbad0bad7ac5 +size 362883 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SKL_VaultIt_Mannequin.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SKL_VaultIt_Mannequin.uasset new file mode 100644 index 00000000..1129f9c6 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SKL_VaultIt_Mannequin.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75f175b596d5649e632f53ddc78a2e3b62d76ade6ec83d0992afdfc66015ee43 +size 22359 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms.uasset new file mode 100644 index 00000000..e881f3bf --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee8505cce94a1c23c2567bcf0e2bcd7b2b5ad7491aa22674885e1e2746770710 +size 2860869 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms_Skeleton.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms_Skeleton.uasset new file mode 100644 index 00000000..51d4fc7a --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_Mannequin_Arms_Skeleton.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:240997f4df5d1a9be6bfcf22ff9fc55ae22ccc22d7b2b8db58b2fa6648cba5a3 +size 19102 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_VaultIt_Mannequin.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_VaultIt_Mannequin.uasset new file mode 100644 index 00000000..54ee6304 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Mesh/SK_VaultIt_Mannequin.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:599b5a51b348857efe17f7a6bbf8a86737a42bd291000bc180bfaf8ab619cf3a +size 5669675 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_Mask.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_Mask.uasset new file mode 100644 index 00000000..e83ec6aa --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_Mask.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:513a67e81cac13e93285b0dc78484fe4ab4a9838c474d61a6255a06d89b304fe +size 225135 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_N.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_N.uasset new file mode 100644 index 00000000..842338ad --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_Male_N.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b40fe7bad5b1eaaf729a822b520d9531c9510978425ee9ddf4765c6f3c9f7d4 +size 5532850 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_Mask.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_Mask.uasset new file mode 100644 index 00000000..7f6e0b95 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_Mask.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a06cbd00cdd144538d2ba0494526b81fd9a0e5031c706a0f247e1c379b67082 +size 88009 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_N.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_N.uasset new file mode 100644 index 00000000..49bda957 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Mannequin/Textures/T_UE4Logo_N.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:594c67060b515e918cd1fdb0ff3a0092eba2f48f04f291ca81ca9087b5716c3a +size 147601 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube.uasset new file mode 100644 index 00000000..dc8fbbc5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0edb0e5b9a191a4e084ffb8535630cc23bea7d0994cb26378775a385e0b52c0 +size 80039 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube_Chamfer.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube_Chamfer.uasset new file mode 100644 index 00000000..4347d6a0 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/1M_Cube_Chamfer.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5d42173b6b749cf9f393aeda28f4710d5673c80f7dd98e9ce96a5a48a042c4c +size 89180 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Bump_StaticMesh.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Bump_StaticMesh.uasset new file mode 100644 index 00000000..c933e496 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Bump_StaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:698838b26dab98088f12dc6ed9c36912269e7f821fd495875974df02aaac39e4 +size 88317 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/CubeMaterial.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/CubeMaterial.uasset new file mode 100644 index 00000000..db222bec --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/CubeMaterial.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c70a288121c45f54f712e10490331a2a99817a290e6637d15668fb0fd392e6d +size 87526 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/LeftArm_StaticMesh.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/LeftArm_StaticMesh.uasset new file mode 100644 index 00000000..b4f29c90 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/LeftArm_StaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:112c30e49e442438984bd4e7e64bab8df96b8474d986f1b24565a0acddda8883 +size 89373 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Linear_Stair_StaticMesh.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Linear_Stair_StaticMesh.uasset new file mode 100644 index 00000000..1ad2e35e --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Linear_Stair_StaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed8a0f64d137866a98398cf6b1004876060c0b9b36abc486e99ab3dc624bc3ad +size 93103 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RampMaterial.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RampMaterial.uasset new file mode 100644 index 00000000..9a4d8c07 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RampMaterial.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:356fbc0831a229d2d3f37b0b9ad57d757818086fdd0fed5e9724b252174f8648 +size 83859 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Ramp_StaticMesh.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Ramp_StaticMesh.uasset new file mode 100644 index 00000000..486f94a5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/Ramp_StaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:355f76083704fe028c8853e8e271cb8596367582dedec68ba21ede164dbf22a8 +size 88414 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RightArm_StaticMesh.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RightArm_StaticMesh.uasset new file mode 100644 index 00000000..6d037919 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/RightArm_StaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e504dd6086cab7a0321eba98db420ea797eb61111061a3f50bc3b21c84ab1c4 +size 88181 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/TemplateFloor.uasset b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/TemplateFloor.uasset new file mode 100644 index 00000000..133cbeed --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/Meshes/TemplateFloor.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eff1e1fa4f2201cd3dac7d9688f6d3b79f280d05e5e590d8ba91301d9d85034b +size 94488 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/Maps/ThirdPersonExampleMap.umap b/EndlessVendetta/Plugins/VaultIt/Content/Maps/ThirdPersonExampleMap.umap new file mode 100644 index 00000000..6ed5e5eb --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/Maps/ThirdPersonExampleMap.umap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e433412ff9ab29a88210b3b6a9a67910546e1dcca6a5110038d9050fba1a91f +size 160673 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/UI/FF_Font.uasset b/EndlessVendetta/Plugins/VaultIt/Content/UI/FF_Font.uasset new file mode 100644 index 00000000..eab2abe5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/UI/FF_Font.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:460a512ff918bb1eccae989ac45cb05a270527559a17d9c6be8e233cdceb2208 +size 34999 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/UI/F_Font.uasset b/EndlessVendetta/Plugins/VaultIt/Content/UI/F_Font.uasset new file mode 100644 index 00000000..b57de7b3 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/UI/F_Font.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:518ae21a854853971b6cc1516bbead00c7381ab29af789fce49128e05b5983ad +size 6960 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/UI/T_Dot.uasset b/EndlessVendetta/Plugins/VaultIt/Content/UI/T_Dot.uasset new file mode 100644 index 00000000..c39e85cf --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/UI/T_Dot.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58b1eb7b2837f2854260f55851d3b7afb542769a281b2966e3f64fbd1fca118c +size 5882 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/UI/W_MasterWidget.uasset b/EndlessVendetta/Plugins/VaultIt/Content/UI/W_MasterWidget.uasset new file mode 100644 index 00000000..80ab827f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/UI/W_MasterWidget.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98d569fa2b59f9b9b93550ef039e9e7dca3c05ff0f4293f3b163c6d243c5e7cf +size 23737 diff --git a/EndlessVendetta/Plugins/VaultIt/Content/UI/W_Net.uasset b/EndlessVendetta/Plugins/VaultIt/Content/UI/W_Net.uasset new file mode 100644 index 00000000..7b41acbd --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Content/UI/W_Net.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31849472649d929ae0ca8748c2d1f72d76468dce5842d77f04fe407c2aff7169 +size 178210 diff --git a/EndlessVendetta/Plugins/VaultIt/Resources/Icon128.png b/EndlessVendetta/Plugins/VaultIt/Resources/Icon128.png new file mode 100644 index 00000000..e95da900 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Resources/Icon128.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12ff1fa00f71fb76c7246bc1cdf7a09e39541c970c854550972cb94e911915e1 +size 32275 diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstance.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstance.cpp new file mode 100644 index 00000000..8e58270c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstance.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Animation/VIAnimInstance.h" +#include "Pawn/VICharacterBase.h" +#include "VIBlueprintFunctionLibrary.h" +#include "GameFramework/CharacterMovementComponent.h" + +void UVIAnimInstance::NativeInitializeAnimation() +{ + Super::NativeInitializeAnimation(); + + Character = (TryGetPawnOwner()) ? Cast(TryGetPawnOwner()) : nullptr; +} + +void UVIAnimInstance::NativeUpdateAnimation(float DeltaTime) +{ + if (Character) + { + const bool bWasVaulting = bIsVaulting; + bIsVaulting = Character->IsVaulting(); + if (bIsVaulting) + { + // Resetting these while vaulting leads to better blending out + bIsJumping = false; + bIsFalling = false; + Speed = 0.f; + + // Interp FBIK + UVIBlueprintFunctionLibrary::InterpolateFBIK(DeltaTime, FBIK); + + // Right Hand + { + const FVIBoneFBIKData* BoneData = UVIBlueprintFunctionLibrary::GetBoneForFBIK(RHandName, FBIK); + bRHand = BoneData->bEnabled; + RHandLoc = BoneData->Location; + } + // Left Hand + { + const FVIBoneFBIKData* BoneData = UVIBlueprintFunctionLibrary::GetBoneForFBIK(LHandName, FBIK); + bLHand = BoneData->bEnabled; + LHandLoc = BoneData->Location; + } + // Both Hands + { + bBothHand = (bRHand && bLHand); + if (bBothHand) + { + // Use only control rig with both IK + // Only ever uses one control rig at a time + bRHand = false; + bLHand = false; + } + } + } + else + { + bIsFalling = Character->GetCharacterMovement() && Character->GetCharacterMovement()->IsFalling(); + bIsJumping = bIsFalling && UVIBlueprintFunctionLibrary::ActorIsAscending(Character, false); + Speed = Character->GetVelocity().Size(); + } + + if (bIsVaulting && !bWasVaulting) + { + OnStartVault(); + } + else if (bWasVaulting && !bIsVaulting) + { + OnStopVault(); + } + } +} + +void UVIAnimInstance::OnStartVault() +{ + K2_OnStartVault(); +} + +void UVIAnimInstance::OnStopVault() +{ + // Stopped vaulting + bRHand = false; + bLHand = false; + bBothHand = false; + + K2_OnStopVault(); +} + +void UVIAnimInstance::SetBoneFBIK_Implementation(const FName& BoneName, const FVector& BoneLocation, bool bEnabled) +{ + UVIBlueprintFunctionLibrary::ToggleBoneFBIK(BoneName, BoneLocation, bEnabled, FBIK); +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstanceFP.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstanceFP.cpp new file mode 100644 index 00000000..0faa683b --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimInstanceFP.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Animation/VIAnimInstanceFP.h" +#include "Pawn/VICharacterBase.h" +#include "VIBlueprintFunctionLibrary.h" + +bool UVIAnimInstanceFP::CanPlayFPMontage() const +{ + return Character && Character->IsLocallyControlled() && !Character->IsBotControlled(); +} + +void UVIAnimInstanceFP::OnStartVault() +{ + Super::OnStartVault(); + + if (CanPlayFPMontage()) + { + if (MontageLinkMap.Contains(Character->GetCurrentMontage())) + { + // Find the FP animation that corresponds to the current TP animation + MontageToPlay = MontageLinkMap.FindChecked(Character->GetCurrentMontage()); + } + + if (MontageToPlay) + { + // Play the animation, borrowing the length and blend out time from the TP animation + VaultBlendOutTime = Character->GetCurrentMontage()->GetDefaultBlendOutTime(); + const float PlayLength = Character->GetCurrentMontage()->GetPlayLength(); + const float PlayRate = UVIBlueprintFunctionLibrary::ComputeAnimationPlayRateFromDuration(MontageToPlay, PlayLength); + Montage_Play(MontageToPlay, PlayRate); + } + else + { + // Should have been able to play an animation but it doesn't exist + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("{ %s} No FP Vault Animation found for { %s }"), *Character->GetName(), *Character->GetCurrentMontage()->GetName())); + } + } +} + +void UVIAnimInstanceFP::OnStopVault() +{ + Super::OnStopVault(); + + // Stop the montage if vaulting ended + if (MontageToPlay) + { + Montage_Stop(VaultBlendOutTime, MontageToPlay); + MontageToPlay = nullptr; + } +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimNotifyState_FBIK.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimNotifyState_FBIK.cpp new file mode 100644 index 00000000..a7907e45 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Animation/VIAnimNotifyState_FBIK.cpp @@ -0,0 +1,237 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Animation/VIAnimNotifyState_FBIK.h" +#include "Components/SkeletalMeshComponent.h" +#include "Animation/AnimInstance.h" +#include "Animation/VIAnimationInterface.h" +#include "Pawn/VIPawnInterface.h" +#include "VIBlueprintFunctionLibrary.h" +#include "GameFramework/Pawn.h" +#include "Kismet/KismetSystemLibrary.h" + +DECLARE_CYCLE_STAT(TEXT("Update FBIK"), STAT_VAULTFBIK, STATGROUP_VaultIt); + +#if WITH_EDITOR +void UVIAnimNotifyState_FBIK::OnAnimNotifyCreatedInEditor(FAnimNotifyEvent& ContainingAnimNotifyEvent) +{ + ContainingAnimNotifyEvent.bTriggerOnDedicatedServer = false; + ContainingAnimNotifyEvent.NotifyFilterType = ENotifyFilterType::LOD; + ContainingAnimNotifyEvent.NotifyFilterLOD = 2; +} +#endif // WITH_EDITOR + +void UVIAnimNotifyState_FBIK::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) +{ + // Compute LateralOffset + APawn* const PawnOwner = GetPawnOwner(MeshComp); + if (!PawnOwner) + { + return; + } + + if (!HasValidRole(PawnOwner)) + { + return; + } + + LateralOffset = 0.f; + SkippedTicks = TickSkipAmount; + + const FVector& Loc = PawnOwner->GetActorLocation(); + const FVector& BoneLoc = MeshComp->GetSocketLocation(BoneName); + + LateralOffset = UVIBlueprintFunctionLibrary::ComputeDirectionToFloat((BoneLoc - Loc), PawnOwner->GetActorRightVector()); + + if (UpdateType == EVIFBIKUpdateType::FUT_Single) + { + UpdateFBIK(MeshComp); + } +} + +void UVIAnimNotifyState_FBIK::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime) +{ + if (UpdateType == EVIFBIKUpdateType::FUT_Tick) + { + UpdateFBIK(MeshComp); + } +} + +void UVIAnimNotifyState_FBIK::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) +{ + if (MeshComp && MeshComp->GetAnimInstance()) + { + APawn* const PawnOwner = GetPawnOwner(MeshComp); + if (!PawnOwner) + { + return; + } + + if (!HasValidRole(PawnOwner)) + { + return; + } + + LateralOffset = 0.f; + SkippedTicks = TickSkipAmount; + + if (MeshComp->GetAnimInstance()->Implements()) + { + // Disable FBIK + IVIAnimationInterface::Execute_SetBoneFBIK(MeshComp->GetAnimInstance(), BoneName, FVector::ZeroVector, false); + } + else + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("{ %s } does not implement interface VIAnimationInterface. Aborting FBIK update."), *MeshComp->GetAnimInstance()->GetName())); + } + } +} + +void UVIAnimNotifyState_FBIK::UpdateFBIK(USkeletalMeshComponent* MeshComp) +{ + if (MeshComp && MeshComp->GetAnimInstance()) + { + if (LateralOffset == 0.f) + { + return; + } + + APawn* const PawnOwner = GetPawnOwner(MeshComp); + if (!PawnOwner) + { + return; + } + + if (!HasValidRole(PawnOwner)) + { + return; + } + + SCOPE_CYCLE_COUNTER(STAT_VAULTFBIK); + + // Skip ticks to reduce overhead + // (as this is only cosmetic and old values can be safely used for the insignificant amount of time lapsed as values are interpolated anyway) + if (SkippedTicks != TickSkipAmount) + { + SkippedTicks++; + return; + } + else + { + SkippedTicks = 0; + } + + if (!PawnOwner->Implements()) + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("{ %s } does not implement interface VIPawnInterface. Aborting FBIK update."), *PawnOwner->GetName())); + return; + } + + if (MeshComp->GetAnimInstance()->Implements()) + { + // Perform the trace + FHitResult Hit(ForceInit); + { + const FVITraceSettings& TraceSettings = IVIPawnInterface::Execute_GetVaultTraceSettings(PawnOwner); + const TArray TraceIgnore = { PawnOwner }; + + FVector VaultLoc; + FVector VaultDir; + IVIPawnInterface::Execute_GetVaultLocationAndDirection(PawnOwner, VaultLoc, VaultDir); + + const FVector& Right = PawnOwner->GetActorRightVector(); + const FVector RightLoc = PawnOwner->GetActorLocation() + (Right * LateralOffset); + const FVector VaultRightLoc = VaultLoc + (Right * LateralOffset); + + const FVector& TraceStart = RightLoc; + const FVector TraceEnd = VaultRightLoc + (VaultDir * TraceLength); + + bool bTraceComplex = (TraceType != EVIFBIKTraceType::FTT_Simple); + if (TraceType == EVIFBIKTraceType::FTT_ComplexLocalOnly) + { + bTraceComplex = IsLocalPlayer(PawnOwner); + } + +#if WITH_EDITOR + const EDrawDebugTrace::Type DrawDebugType = (bDebugTraceDuringPIE) ? ((UpdateType == EVIFBIKUpdateType::FUT_Single) ? EDrawDebugTrace::ForDuration : EDrawDebugTrace::ForOneFrame) : EDrawDebugTrace::None; +#else + const EDrawDebugTrace::Type DrawDebugType = EDrawDebugTrace::None; +#endif // WITH_EDITOR + + UKismetSystemLibrary::SphereTraceSingleForObjects( + PawnOwner, // World Context + TraceStart, // Start + TraceEnd, // End + TraceRadius, // Radius + TraceSettings.GetObjectTypes(), // TraceChannels + bTraceComplex, // bTraceComplex + TraceIgnore, // ActorsToIgnore + DrawDebugType, // DrawDebugType + Hit, // OutHit + false, // bIgnoreSelf + FLinearColor::Yellow, // TraceColor + FLinearColor::Blue, // TraceHitColor + 1.f // DrawTime + ); + + if (Hit.bBlockingHit) + { + Hit.Location += Hit.ImpactNormal * BoneOffset; + } + } + + if (Hit.bBlockingHit) + { + IVIAnimationInterface::Execute_SetBoneFBIK(MeshComp->GetAnimInstance(), BoneName, Hit.Location, true); + } + else if (bDisableIfHitFails) + { + // We generally leave it enabled if there was no hit and just use the last successful hit + // but here the user has specified to disable it + IVIAnimationInterface::Execute_SetBoneFBIK(MeshComp->GetAnimInstance(), BoneName, FVector::ZeroVector, false); + } + } + else + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("{ %s } does not implement interface VIAnimationInterface. Aborting FBIK update."), *MeshComp->GetAnimInstance()->GetName())); + } + } +} + +APawn* UVIAnimNotifyState_FBIK::GetPawnOwner(const USkeletalMeshComponent* const MeshComp) +{ + if (MeshComp && MeshComp->GetOwner()) + { + return MeshComp->GetOwner(); + } + + return nullptr; +} + +bool UVIAnimNotifyState_FBIK::HasValidRole(const APawn* const PawnOwner) const +{ + switch (ApplyToRoles) + { + case EVIFBIKUpdateRole::FUR_LocalOnly: + if (IsLocalPlayer(PawnOwner)) + { + return true; + } + return false; + case EVIFBIKUpdateRole::FUR_SimulatedOnly: + if (PawnOwner->GetLocalRole() == ROLE_SimulatedProxy) + { + return true; + } + return false; + case EVIFBIKUpdateRole::FUR_All: + default: + break; + } + + return true; +} + +bool UVIAnimNotifyState_FBIK::IsLocalPlayer(const APawn* const PawnOwner) +{ + return PawnOwner && PawnOwner->IsLocallyControlled() && (PawnOwner->GetNetMode() == NM_Standalone || !PawnOwner->HasAuthority()); +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/GameplayAbility_Vault.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/GameplayAbility_Vault.cpp new file mode 100644 index 00000000..6c6b8046 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/GameplayAbility_Vault.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "GAS/GameplayAbility_Vault.h" + +UGameplayAbility_Vault::UGameplayAbility_Vault() +{ + InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; + + bServerRespectsRemoteAbilityCancellation = false; + NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ServerOnlyTermination; +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/PlayMontageForMeshAndWait.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/PlayMontageForMeshAndWait.cpp new file mode 100644 index 00000000..f42ac439 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/PlayMontageForMeshAndWait.cpp @@ -0,0 +1,266 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +// Based on GASShooter by Dan Kestranek + +#include "GAS/PlayMontageForMeshAndWait.h" +#include "GAS/VIAbilitySystemComponent.h" +#include "VITypes.h" +#include "AbilitySystemGlobals.h" +#include "GameFramework/Character.h" + +DEFINE_LOG_CATEGORY_STATIC(LogVIPlayMontage, Log, All); + +DECLARE_CYCLE_STAT(TEXT("UPlayMontageForMeshAndWait Activate"), STAT_PLAYMONTAGEFORMESHANDWAIT_ACTIVATE, STATGROUP_VaultIt); + +UPlayMontageForMeshAndWait::UPlayMontageForMeshAndWait(const FObjectInitializer& OI) + : Super(OI) +{ + Rate = 1.f; + bStopWhenAbilityEnds = true; +} + +UPlayMontageForMeshAndWait* UPlayMontageForMeshAndWait::PlayMontageForMeshAndWait(UGameplayAbility* OwningAbility, FName TaskInstanceName, USkeletalMeshComponent* InMesh, UAnimMontage* MontageToPlay, float Rate /*= 1.f*/, FName StartSection /*= NAME_None*/, bool bStopWhenAbilityEnds /*= true*/, float AnimRootMotionTranslationScale /*= 1.f*/, bool bReplicateMontage /*= true*/, float OverrideBlendOutTimeForCancelAbility /*= -1.f*/, float OverrideBlendOutTimeForStopWhenEndAbility /*= -1.f*/) +{ + UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate); + + UPlayMontageForMeshAndWait* MyObj = NewAbilityTask(OwningAbility, TaskInstanceName); + MyObj->Mesh = InMesh; + MyObj->MontageToPlay = MontageToPlay; + MyObj->Rate = Rate; + MyObj->StartSection = StartSection; + MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale; + MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds; + MyObj->bReplicateMontage = bReplicateMontage; + MyObj->OverrideBlendOutTimeForCancelAbility = OverrideBlendOutTimeForCancelAbility; + MyObj->OverrideBlendOutTimeForStopWhenEndAbility = OverrideBlendOutTimeForStopWhenEndAbility; + + return MyObj; +} + +void UPlayMontageForMeshAndWait::Activate() +{ + if (!Ability) + { + return; + } + + if (!Mesh) + { + UE_LOG(LogVIPlayMontage, Error, TEXT("UPlayMontageForMeshAndWait::Activate - invalid Mesh")); + return; + } + + SCOPE_CYCLE_COUNTER(STAT_PLAYMONTAGEFORMESHANDWAIT_ACTIVATE); + + bool bPlayedMontage = false; + UVIAbilitySystemComponent* ASC = GetTargetASC(); + + if (ASC) + { + UAnimInstance* AnimInstance = Mesh->GetAnimInstance(); + if (AnimInstance != nullptr) + { + if (ASC->PlayMontageForMesh(Ability, Mesh, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection, bReplicateMontage) > 0.f) + { + // Playing a montage could potentially fire off a callback into game code which could kill this ability! Early out if we are pending kill. + if (ShouldBroadcastAbilityTaskDelegates() == false) + { + return; + } + + CancelledHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &UPlayMontageForMeshAndWait::OnAbilityCancelled); + + BlendingOutDelegate.BindUObject(this, &UPlayMontageForMeshAndWait::OnMontageBlendingOut); + AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay); + + MontageEndedDelegate.BindUObject(this, &UPlayMontageForMeshAndWait::OnMontageEnded); + AnimInstance->Montage_SetEndDelegate(MontageEndedDelegate, MontageToPlay); + + ACharacter* Character = Cast(GetAvatarActor()); + if (Character && (Character->GetLocalRole() == ROLE_Authority || + (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted))) + { + Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale); + } + + bPlayedMontage = true; + } + } + else + { + UE_LOG(LogVIPlayMontage, Warning, TEXT("UPlayMontageForMeshAndWait call to PlayMontage failed!")); + } + } + else + { + UE_LOG(LogVIPlayMontage, Warning, TEXT("UPlayMontageForMeshAndWait called on invalid AbilitySystemComponent")); + } + + if (!bPlayedMontage) + { + UE_LOG(LogVIPlayMontage, Warning, TEXT("UPlayMontageForMeshAndWait called in Ability %s failed to play montage %s; Task Instance Name %s."), *Ability->GetName(), *GetNameSafe(MontageToPlay), *InstanceName.ToString()); + if (ShouldBroadcastAbilityTaskDelegates()) + { + //ABILITY_LOG(Display, TEXT("%s: OnCancelled"), *GetName()); + OnCancelled.Broadcast(); + } + } + + SetWaitingOnAvatar(); +} + +void UPlayMontageForMeshAndWait::ExternalCancel() +{ + check(AbilitySystemComponent.IsValid()); + + OnAbilityCancelled(); + + Super::ExternalCancel(); +} + +void UPlayMontageForMeshAndWait::OnDestroy(bool AbilityEnded) +{ + // Note: Clearing montage end delegate isn't necessary since its not a multicast and will be cleared when the next montage plays. + // (If we are destroyed, it will detect this and not do anything) + if (Ability) + { + Ability->OnGameplayAbilityCancelled.Remove(CancelledHandle); + if (AbilityEnded && bStopWhenAbilityEnds) + { + StopPlayingMontage(OverrideBlendOutTimeForStopWhenEndAbility); + } + } + + Super::OnDestroy(AbilityEnded); +} + +bool UPlayMontageForMeshAndWait::StopPlayingMontage(float OverrideBlendOutTime /*= -1.f*/) +{ + if (!Mesh) + { + return false; + } + + UVIAbilitySystemComponent* const ASC = GetTargetASC(); + if (!ASC) + { + return false; + } + + const FGameplayAbilityActorInfo* const ActorInfo = Ability->GetCurrentActorInfo(); + if (!ActorInfo) + { + return false; + } + + const UAnimInstance* const AnimInstance = Mesh->GetAnimInstance(); + if (!AnimInstance) + { + return false; + } + + // Check if the montage is still playing + // The ability would have been interrupted, in which case we should automatically stop the montage + if (ASC && Ability) + { + if (ASC->GetAnimatingAbilityFromAnyMesh() == Ability + && ASC->GetCurrentMontageForMesh(Mesh) == MontageToPlay) + { + // Unbind delegates so they don't get called as well + FAnimMontageInstance* MontageInstance = AnimInstance->GetActiveInstanceForMontage(MontageToPlay); + if (MontageInstance) + { + MontageInstance->OnMontageBlendingOutStarted.Unbind(); + MontageInstance->OnMontageEnded.Unbind(); + } + + ASC->CurrentMontageStopForMesh(Mesh, OverrideBlendOutTime); + return true; + } + } + + return false; +} + +UVIAbilitySystemComponent* UPlayMontageForMeshAndWait::GetTargetASC() const +{ + return Cast(AbilitySystemComponent); +} + +void UPlayMontageForMeshAndWait::OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted) +{ + if (Ability && Ability->GetCurrentMontage() == MontageToPlay) + { + if (Montage == MontageToPlay) + { + AbilitySystemComponent->ClearAnimatingAbility(Ability); + + // Reset AnimRootMotionTranslationScale + ACharacter* Character = Cast(GetAvatarActor()); + if (Character && (Character->GetLocalRole() == ROLE_Authority || + (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted))) + { + Character->SetAnimRootMotionTranslationScale(1.f); + } + + } + } + + if (bInterrupted) + { + if (ShouldBroadcastAbilityTaskDelegates()) + { + OnInterrupted.Broadcast(); + } + } + else + { + if (ShouldBroadcastAbilityTaskDelegates()) + { + OnBlendOut.Broadcast(); + } + } +} + +void UPlayMontageForMeshAndWait::OnAbilityCancelled() +{ + // UE4 was calling the wrong callback + + if (StopPlayingMontage(OverrideBlendOutTimeForCancelAbility)) + { + // Let the BP handle the interrupt as well + if (ShouldBroadcastAbilityTaskDelegates()) + { + OnCancelled.Broadcast(); + } + } +} + +void UPlayMontageForMeshAndWait::OnMontageEnded(UAnimMontage* Montage, bool bInterrupted) +{ + if (!bInterrupted) + { + if (ShouldBroadcastAbilityTaskDelegates()) + { + OnCompleted.Broadcast(); + } + } + + EndTask(); +} + +FString UPlayMontageForMeshAndWait::GetDebugString() const +{ + const UAnimMontage* PlayingMontage = nullptr; + if (Ability && Mesh) + { + const UAnimInstance* const AnimInstance = Mesh->GetAnimInstance(); + + if (AnimInstance != nullptr) + { + PlayingMontage = AnimInstance->Montage_IsActive(MontageToPlay) ? MontageToPlay : AnimInstance->GetCurrentActiveMontage(); + } + } + + return FString::Printf(TEXT("PlayMontageForMeshAndWait. MontageToPlay: %s (Currently Playing): %s"), *GetNameSafe(MontageToPlay), *GetNameSafe(PlayingMontage)); +} \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAbilitySystemComponent.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAbilitySystemComponent.cpp new file mode 100644 index 00000000..791c3c38 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAbilitySystemComponent.cpp @@ -0,0 +1,500 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "GAS/VIAbilitySystemComponent.h" + +#include "AbilitySystemLog.h" +#include "Net/UnrealNetwork.h" +#include "Components/SkeletalMeshComponent.h" +#include "GAS/VIGameplayAbility.h" +#include "VITypes.h" +#include "Engine/World.h" +#include "Engine/Engine.h" +#include "Runtime/Launch/Resources/Version.h" + +DECLARE_CYCLE_STAT(TEXT("VIAbilitySystemComponent Tick"), STAT_VIABILITYSYSTEM_TICK, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("VIAbilitySystemComponent PlayMontageForMesh"), STAT_PLAYMONTAGEFORMESH, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("VIAbilitySystemComponent PlaySimulatedMontageForMesh"), STAT_PLAYSIMULATEDMONTAGEFORMESH, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("VIAbilitySystemComponent UpdateReplicatedDataForMesh"), STAT_VIABILITYSYSTEM_UPDATEREPLDATAFORMESH, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("VIAbilitySystemComponent OnReplicatedAnimMontageForMesh"), STAT_VIABILITYSYSTEM_ONREPLICATEDANIMMONTAGEFORMESH, STATGROUP_VaultIt); + +static TAutoConsoleVariable CVarReplayMontageErrorThreshold( + TEXT("VI.replay.MontageErrorThreshold"), + 0.5f, + TEXT("Tolerance level for when montage playback position correction occurs in replays") +); + +void UVIAbilitySystemComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME(UVIAbilitySystemComponent, RepAnimMontageInfoForMeshes); +} + +bool UVIAbilitySystemComponent::GetShouldTick() const +{ + for (const FVIGameplayAbilityRepAnimMontageForMesh RepMontageInfo : RepAnimMontageInfoForMeshes) + { + const bool bHasReplicatedMontageInfoToUpdate = (IsOwnerActorAuthoritative() && RepMontageInfo.RepMontageInfo.IsStopped == false); + + if (bHasReplicatedMontageInfoToUpdate) + { + return true; + } + } + + return Super::GetShouldTick(); +} + +void UVIAbilitySystemComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + SCOPE_CYCLE_COUNTER(STAT_VIABILITYSYSTEM_TICK); + + if (IsOwnerActorAuthoritative()) + { + for (const FVIGameplayAbilityLocalAnimMontageForMesh& MontageInfo : LocalAnimMontageInfoForMeshes) + { + AnimMontage_UpdateReplicatedDataForMesh(MontageInfo.Mesh); + } + } + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); +} + +void UVIAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) +{ + Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor); + + LocalAnimMontageInfoForMeshes = TArray(); + RepAnimMontageInfoForMeshes = TArray(); + + if (bPendingMontageRep) + { + OnRep_ReplicatedAnimMontageForMesh(); + } +} + +void UVIAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) +{ + Super::NotifyAbilityEnded(Handle, Ability, bWasCancelled); + + // If AnimatingAbility ended, clear the pointer + ClearAnimatingAbilityForAllMeshes(Ability); +} + +float UVIAbilitySystemComponent::PlayMontageForMesh(UGameplayAbility* InAnimatingAbility, class USkeletalMeshComponent* InMesh, FGameplayAbilityActivationInfo ActivationInfo, UAnimMontage* NewAnimMontage, float InPlayRate, FName StartSectionName /*= NAME_None*/, bool bReplicateMontage /*= true*/) +{ + SCOPE_CYCLE_COUNTER(STAT_PLAYMONTAGEFORMESH); + + UVIGameplayAbility* InAbility = Cast(InAnimatingAbility); + + float Duration = -1.f; + + UAnimInstance* AnimInstance = IsValid(InMesh) && InMesh->GetOwner() == AbilityActorInfo->AvatarActor ? InMesh->GetAnimInstance() : nullptr; + if (AnimInstance && NewAnimMontage) + { + Duration = AnimInstance->Montage_Play(NewAnimMontage, InPlayRate); + if (Duration > 0.f) + { + FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(InMesh); + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 2 + if (AnimMontageInfo.LocalMontageInfo.AnimatingAbility.IsValid() && AnimMontageInfo.LocalMontageInfo.AnimatingAbility != InAnimatingAbility) +#else + if (AnimMontageInfo.LocalMontageInfo.AnimatingAbility && AnimMontageInfo.LocalMontageInfo.AnimatingAbility != InAnimatingAbility) +#endif + { + // The ability that was previously animating will have already gotten the 'interrupted' callback. + // It may be a good idea to make this a global policy and 'cancel' the ability. + // + // For now, we expect it to end itself when this happens. + } + + if (NewAnimMontage->HasRootMotion() && AnimInstance->GetOwningActor()) + { + UE_LOG(LogRootMotion, Log, TEXT("UAbilitySystemComponent::PlayMontage %s, Role: %s") + , *GetNameSafe(NewAnimMontage) + , *UEnum::GetValueAsString(TEXT("Engine.ENetRole"), AnimInstance->GetOwningActor()->GetLocalRole()) + ); + } + + AnimMontageInfo.LocalMontageInfo.AnimMontage = NewAnimMontage; + AnimMontageInfo.LocalMontageInfo.AnimatingAbility = InAnimatingAbility; + + if (InAbility) + { + InAbility->SetCurrentMontageForMesh(InMesh, NewAnimMontage); + } + + // Start at a given Section. + if (StartSectionName != NAME_None) + { + AnimInstance->Montage_JumpToSection(StartSectionName, NewAnimMontage); + } + + // Replicate to non owners + if (IsOwnerActorAuthoritative()) + { + if (bReplicateMontage) + { + // Those are static parameters, they are only set when the montage is played. They are not changed after that. + FVIGameplayAbilityRepAnimMontageForMesh& AbilityRepMontageInfo = GetGameplayAbilityRepAnimMontageForMesh(InMesh); + AbilityRepMontageInfo.RepMontageInfo.AnimMontage = NewAnimMontage; + AbilityRepMontageInfo.RepMontageInfo.PlayInstanceId = (AbilityRepMontageInfo.RepMontageInfo.PlayInstanceId + 1) % 255; + + // Update parameters that change during Montage life time. + AnimMontage_UpdateReplicatedDataForMesh(InMesh); + + // Force net update on our avatar actor + if (AbilityActorInfo->AvatarActor != nullptr) + { + AbilityActorInfo->AvatarActor->ForceNetUpdate(); + } + } + } + else + { + // If this prediction key is rejected, we need to end the preview + FPredictionKey PredictionKey = GetPredictionKeyForNewAction(); + if (PredictionKey.IsValidKey()) + { + PredictionKey.NewRejectedDelegate().BindUObject(this, &UVIAbilitySystemComponent::OnPredictiveMontageRejectedForMesh, InMesh, NewAnimMontage); + } + } + } + } + + return Duration; +} + +float UVIAbilitySystemComponent::PlayMontageSimulatedForMesh(USkeletalMeshComponent* InMesh, UAnimMontage* NewAnimMontage, float InPlayRate, FName StartSectionName /*= NAME_None*/) +{ + SCOPE_CYCLE_COUNTER(STAT_PLAYSIMULATEDMONTAGEFORMESH); + + float Duration = -1.f; + UAnimInstance* AnimInstance = IsValid(InMesh) && InMesh->GetOwner() == AbilityActorInfo->AvatarActor ? InMesh->GetAnimInstance() : nullptr; + if (AnimInstance && NewAnimMontage) + { + Duration = AnimInstance->Montage_Play(NewAnimMontage, InPlayRate); + if (Duration > 0.f) + { + FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(InMesh); + AnimMontageInfo.LocalMontageInfo.AnimMontage = NewAnimMontage; + } + } + + return Duration; +} + +UGameplayAbility* UVIAbilitySystemComponent::GetAnimatingAbilityFromAnyMesh() +{ + // Only one ability can be animating for all meshes + for (const FVIGameplayAbilityLocalAnimMontageForMesh& GameplayAbilityLocalAnimMontageForMesh : LocalAnimMontageInfoForMeshes) + { +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 2 + if (GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility.IsValid()) + { + return GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility.Get(); + } +#else + if (GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility) + { + return GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility; + } +#endif + } + + return nullptr; +} + +UAnimMontage* UVIAbilitySystemComponent::GetCurrentMontageForMesh(USkeletalMeshComponent* InMesh) +{ + const UAnimInstance* const AnimInstance = IsValid(InMesh) && InMesh->GetOwner() == AbilityActorInfo->AvatarActor ? InMesh->GetAnimInstance() : nullptr; + const FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(InMesh); + + if (AnimMontageInfo.LocalMontageInfo.AnimMontage && AnimInstance + && AnimInstance->Montage_IsActive(AnimMontageInfo.LocalMontageInfo.AnimMontage)) + { + return AnimMontageInfo.LocalMontageInfo.AnimMontage; + } + + return nullptr; +} + +void UVIAbilitySystemComponent::CurrentMontageStopForMesh(USkeletalMeshComponent* InMesh, float OverrideBlendOutTime /*= -1.0f*/) +{ + UAnimInstance* AnimInstance = IsValid(InMesh) && InMesh->GetOwner() == AbilityActorInfo->AvatarActor ? InMesh->GetAnimInstance() : nullptr; + const FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(InMesh); + const UAnimMontage* MontageToStop = AnimMontageInfo.LocalMontageInfo.AnimMontage; + const bool bShouldStopMontage = AnimInstance && MontageToStop && !AnimInstance->Montage_GetIsStopped(MontageToStop); + + if (bShouldStopMontage) + { + const float BlendOutTime = (OverrideBlendOutTime >= 0.0f ? OverrideBlendOutTime : MontageToStop->BlendOut.GetBlendTime()); + + AnimInstance->Montage_Stop(BlendOutTime, MontageToStop); + + if (IsOwnerActorAuthoritative()) + { + AnimMontage_UpdateReplicatedDataForMesh(InMesh); + } + } +} + +void UVIAbilitySystemComponent::ClearAnimatingAbilityForAllMeshes(UGameplayAbility* Ability) +{ + UVIGameplayAbility* VIAbility = Cast(Ability); + for (FVIGameplayAbilityLocalAnimMontageForMesh& GameplayAbilityLocalAnimMontageForMesh : LocalAnimMontageInfoForMeshes) + { + if (GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility == Ability) + { + VIAbility->SetCurrentMontageForMesh(GameplayAbilityLocalAnimMontageForMesh.Mesh, nullptr); + GameplayAbilityLocalAnimMontageForMesh.LocalMontageInfo.AnimatingAbility = nullptr; + } + } +} + +void UVIAbilitySystemComponent::AnimMontage_UpdateReplicatedDataForMesh(USkeletalMeshComponent* InMesh) +{ + check(IsOwnerActorAuthoritative()); + + AnimMontage_UpdateReplicatedDataForMesh(GetGameplayAbilityRepAnimMontageForMesh(InMesh)); +} + +void UVIAbilitySystemComponent::AnimMontage_UpdateReplicatedDataForMesh(FVIGameplayAbilityRepAnimMontageForMesh& OutRepAnimMontageInfo) +{ + SCOPE_CYCLE_COUNTER(STAT_VIABILITYSYSTEM_UPDATEREPLDATAFORMESH); + + const UAnimInstance* AnimInstance = IsValid(OutRepAnimMontageInfo.Mesh) && OutRepAnimMontageInfo.Mesh->GetOwner() == + AbilityActorInfo->AvatarActor ? OutRepAnimMontageInfo.Mesh->GetAnimInstance() : nullptr; + const FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(OutRepAnimMontageInfo.Mesh); + + if (AnimInstance && AnimMontageInfo.LocalMontageInfo.AnimMontage) + { + OutRepAnimMontageInfo.RepMontageInfo.AnimMontage = AnimMontageInfo.LocalMontageInfo.AnimMontage; + + // Compressed Flags + const bool bIsStopped = AnimInstance->Montage_GetIsStopped(AnimMontageInfo.LocalMontageInfo.AnimMontage); + + if (!bIsStopped) + { + OutRepAnimMontageInfo.RepMontageInfo.PlayRate = AnimInstance->Montage_GetPlayRate(AnimMontageInfo.LocalMontageInfo.AnimMontage); + OutRepAnimMontageInfo.RepMontageInfo.Position = AnimInstance->Montage_GetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage); + OutRepAnimMontageInfo.RepMontageInfo.BlendTime = AnimInstance->Montage_GetBlendTime(AnimMontageInfo.LocalMontageInfo.AnimMontage); + } + + if (OutRepAnimMontageInfo.RepMontageInfo.IsStopped != bIsStopped) + { + // Set this prior to calling UpdateShouldTick, so we start ticking if we are playing a Montage + OutRepAnimMontageInfo.RepMontageInfo.IsStopped = bIsStopped; + + // When we start or stop an animation, update the clients right away for the Avatar Actor + if (AbilityActorInfo->AvatarActor != nullptr) + { + AbilityActorInfo->AvatarActor->ForceNetUpdate(); + } + + // When this changes, we should update whether or not we should be ticking + UpdateShouldTick(); + } + + // Replicate NextSectionID to keep it in sync. + // We actually replicate NextSectionID+1 on a BYTE to put INDEX_NONE in there. + const int32 CurrentSectionID = AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionIndexFromPosition(OutRepAnimMontageInfo.RepMontageInfo.Position); + if (CurrentSectionID != INDEX_NONE) + { + const int32 NextSectionID = AnimInstance->Montage_GetNextSectionID(AnimMontageInfo.LocalMontageInfo.AnimMontage, CurrentSectionID); + if (NextSectionID >= (256 - 1)) + { + ABILITY_LOG(Error, TEXT("AnimMontage_UpdateReplicatedData. NextSectionID = %d. RepAnimMontageInfo.Position: %.2f, CurrentSectionID: %d. LocalAnimMontageInfo.AnimMontage %s"), + NextSectionID, OutRepAnimMontageInfo.RepMontageInfo.Position, CurrentSectionID, *GetNameSafe(AnimMontageInfo.LocalMontageInfo.AnimMontage)); + ensure(NextSectionID < (256 - 1)); + } + OutRepAnimMontageInfo.RepMontageInfo.NextSectionID = uint8(NextSectionID + 1); + } + else + { + OutRepAnimMontageInfo.RepMontageInfo.NextSectionID = 0; + } + } +} + +void UVIAbilitySystemComponent::OnRep_ReplicatedAnimMontageForMesh() +{ + SCOPE_CYCLE_COUNTER(STAT_VIABILITYSYSTEM_ONREPLICATEDANIMMONTAGEFORMESH); + + for (FVIGameplayAbilityRepAnimMontageForMesh& NewRepMontageInfoForMesh : RepAnimMontageInfoForMeshes) + { + FVIGameplayAbilityLocalAnimMontageForMesh& AnimMontageInfo = GetLocalAnimMontageInfoForMesh(NewRepMontageInfoForMesh.Mesh); + + const UWorld* const World = GetWorld(); + + if (NewRepMontageInfoForMesh.RepMontageInfo.bSkipPlayRate) + { + NewRepMontageInfoForMesh.RepMontageInfo.PlayRate = 1.f; + } + + const bool bIsPlayingReplay = World && World->IsPlayingReplay(); + + const float MONTAGE_REP_POS_ERR_THRESH = bIsPlayingReplay ? CVarReplayMontageErrorThreshold.GetValueOnGameThread() : 0.1f; + + UAnimInstance* AnimInstance = IsValid(NewRepMontageInfoForMesh.Mesh) && NewRepMontageInfoForMesh.Mesh->GetOwner() + == AbilityActorInfo->AvatarActor ? NewRepMontageInfoForMesh.Mesh->GetAnimInstance() : nullptr; + if (AnimInstance == nullptr || !IsReadyForReplicatedMontageForMesh()) + { + // We can't handle this yet + bPendingMontageRep = true; + return; + } + bPendingMontageRep = false; + + if (!AbilityActorInfo->IsLocallyControlled()) + { + static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("net.Montage.Debug")); + const bool DebugMontage = (CVar && CVar->GetValueOnGameThread() == 1); + if (DebugMontage) + { + ABILITY_LOG(Warning, TEXT("\n\nOnRep_ReplicatedAnimMontage, %s"), *GetNameSafe(this)); + ABILITY_LOG(Warning, TEXT("\tAnimMontage: %s\n\tPlayRate: %f\n\tPosition: %f\n\tBlendTime: %f\n\tNextSectionID: %d\n\tIsStopped: %d\n\tForcePlayBit: %d"), + *GetNameSafe(NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage), + NewRepMontageInfoForMesh.RepMontageInfo.PlayRate, + NewRepMontageInfoForMesh.RepMontageInfo.Position, + NewRepMontageInfoForMesh.RepMontageInfo.BlendTime, + NewRepMontageInfoForMesh.RepMontageInfo.NextSectionID, + NewRepMontageInfoForMesh.RepMontageInfo.IsStopped, + NewRepMontageInfoForMesh.RepMontageInfo.PlayInstanceId); + ABILITY_LOG(Warning, TEXT("\tLocalAnimMontageInfo.AnimMontage: %s\n\tPosition: %f"), + *GetNameSafe(AnimMontageInfo.LocalMontageInfo.AnimMontage), AnimInstance->Montage_GetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage)); + } + + if (NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage) + { + // New Montage to play + const uint8 ReplicatedPlayBit = NewRepMontageInfoForMesh.RepMontageInfo.PlayInstanceId; + if ((AnimMontageInfo.LocalMontageInfo.AnimMontage != NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage) || (AnimMontageInfo.LocalMontageInfo.PlayInstanceId != ReplicatedPlayBit)) + { + AnimMontageInfo.LocalMontageInfo.PlayInstanceId = ReplicatedPlayBit; + PlayMontageSimulatedForMesh(NewRepMontageInfoForMesh.Mesh, NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage, NewRepMontageInfoForMesh.RepMontageInfo.PlayRate); + } + + if (AnimMontageInfo.LocalMontageInfo.AnimMontage == nullptr) + { + ABILITY_LOG(Warning, TEXT("OnRep_ReplicatedAnimMontage: PlayMontageSimulated failed. Name: %s, AnimMontage: %s"), *GetNameSafe(this), *GetNameSafe(NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage)); + return; + } + + // Play Rate has changed + if (AnimInstance->Montage_GetPlayRate(AnimMontageInfo.LocalMontageInfo.AnimMontage) != NewRepMontageInfoForMesh.RepMontageInfo.PlayRate) + { + AnimInstance->Montage_SetPlayRate(AnimMontageInfo.LocalMontageInfo.AnimMontage, NewRepMontageInfoForMesh.RepMontageInfo.PlayRate); + } + + // Compressed Flags + const bool bIsStopped = AnimInstance->Montage_GetIsStopped(AnimMontageInfo.LocalMontageInfo.AnimMontage); + const bool bReplicatedIsStopped = bool(NewRepMontageInfoForMesh.RepMontageInfo.IsStopped); + + // Process stopping first, so we don't change sections and cause blending to pop. + if (bReplicatedIsStopped) + { + if (!bIsStopped) + { + CurrentMontageStopForMesh(NewRepMontageInfoForMesh.Mesh, NewRepMontageInfoForMesh.RepMontageInfo.BlendTime); + } + } + else if (!NewRepMontageInfoForMesh.RepMontageInfo.SkipPositionCorrection) + { + const int32 RepSectionID = AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionIndexFromPosition(NewRepMontageInfoForMesh.RepMontageInfo.Position); + const int32 RepNextSectionID = int32(NewRepMontageInfoForMesh.RepMontageInfo.NextSectionID) - 1; + + // And NextSectionID for the replicated SectionID. + if (RepSectionID != INDEX_NONE) + { + const int32 NextSectionID = AnimInstance->Montage_GetNextSectionID(AnimMontageInfo.LocalMontageInfo.AnimMontage, RepSectionID); + + // If NextSectionID is different than the replicated one, then set it. + if (NextSectionID != RepNextSectionID) + { + AnimInstance->Montage_SetNextSection(AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionName(RepSectionID), AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionName(RepNextSectionID), AnimMontageInfo.LocalMontageInfo.AnimMontage); + } + + // Make sure we haven't received that update too late and the client hasn't already jumped to another section. + const int32 CurrentSectionID = AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionIndexFromPosition(AnimInstance->Montage_GetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage)); + if ((CurrentSectionID != RepSectionID) && (CurrentSectionID != RepNextSectionID)) + { + // Client is in a wrong section, teleport him into the begining of the right section + const float SectionStartTime = AnimMontageInfo.LocalMontageInfo.AnimMontage->GetAnimCompositeSection(RepSectionID).GetTime(); + AnimInstance->Montage_SetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage, SectionStartTime); + } + } + + // Update Position. If error is too great, jump to replicated position. + const float CurrentPosition = AnimInstance->Montage_GetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage); + const int32 CurrentSectionID = AnimMontageInfo.LocalMontageInfo.AnimMontage->GetSectionIndexFromPosition(CurrentPosition); + const float DeltaPosition = NewRepMontageInfoForMesh.RepMontageInfo.Position - CurrentPosition; + + // Only check threshold if we are located in the same section. Different sections require a bit more work as we could be jumping around the timeline. + // And therefore DeltaPosition is not as trivial to determine. + if ((CurrentSectionID == RepSectionID) && (FMath::Abs(DeltaPosition) > MONTAGE_REP_POS_ERR_THRESH) && (NewRepMontageInfoForMesh.RepMontageInfo.IsStopped == 0)) + { + // fast forward to server position and trigger notifies + if (FAnimMontageInstance* MontageInstance = AnimInstance->GetActiveInstanceForMontage(NewRepMontageInfoForMesh.RepMontageInfo.AnimMontage)) + { + // Skip triggering notifies if we're going backwards in time, we've already triggered them. + const float DeltaTime = !FMath::IsNearlyZero(NewRepMontageInfoForMesh.RepMontageInfo.PlayRate) ? (DeltaPosition / NewRepMontageInfoForMesh.RepMontageInfo.PlayRate) : 0.f; + if (DeltaTime >= 0.f) + { + MontageInstance->UpdateWeight(DeltaTime); + MontageInstance->HandleEvents(CurrentPosition, NewRepMontageInfoForMesh.RepMontageInfo.Position, nullptr); + AnimInstance->TriggerAnimNotifies(DeltaTime); + } + } + AnimInstance->Montage_SetPosition(AnimMontageInfo.LocalMontageInfo.AnimMontage, NewRepMontageInfoForMesh.RepMontageInfo.Position); + } + } + } + } + } +} + +FVIGameplayAbilityLocalAnimMontageForMesh& UVIAbilitySystemComponent::GetLocalAnimMontageInfoForMesh(USkeletalMeshComponent* InMesh) +{ + for (FVIGameplayAbilityLocalAnimMontageForMesh& MontageInfo : LocalAnimMontageInfoForMeshes) + { + if (MontageInfo.Mesh == InMesh) + { + return MontageInfo; + } + } + + const FVIGameplayAbilityLocalAnimMontageForMesh MontageInfo = FVIGameplayAbilityLocalAnimMontageForMesh(InMesh); + LocalAnimMontageInfoForMeshes.Add(MontageInfo); + return LocalAnimMontageInfoForMeshes.Last(); +} + +FVIGameplayAbilityRepAnimMontageForMesh& UVIAbilitySystemComponent::GetGameplayAbilityRepAnimMontageForMesh(USkeletalMeshComponent* InMesh) +{ + for (FVIGameplayAbilityRepAnimMontageForMesh& RepMontageInfo : RepAnimMontageInfoForMeshes) + { + if (RepMontageInfo.Mesh == InMesh) + { + return RepMontageInfo; + } + } + + const FVIGameplayAbilityRepAnimMontageForMesh RepMontageInfo = FVIGameplayAbilityRepAnimMontageForMesh(InMesh); + RepAnimMontageInfoForMeshes.Add(RepMontageInfo); + return RepAnimMontageInfoForMeshes.Last(); +} + +void UVIAbilitySystemComponent::OnPredictiveMontageRejectedForMesh( + USkeletalMeshComponent* const InMesh, UAnimMontage* const PredictiveMontage) +{ + UAnimInstance* AnimInstance = IsValid(InMesh) && InMesh->GetOwner() == AbilityActorInfo->AvatarActor ? InMesh->GetAnimInstance() : nullptr; + if (AnimInstance && PredictiveMontage) + { + // If this montage is still playing, kill it + if (AnimInstance->Montage_IsPlaying(PredictiveMontage)) + { + AnimInstance->Montage_Stop(PredictiveMontage->GetDefaultBlendOutTime(), PredictiveMontage); + } + } +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAssetManager.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAssetManager.cpp new file mode 100644 index 00000000..93f74bbd --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIAssetManager.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "GAS/VIAssetManager.h" +#include "AbilitySystemGlobals.h" + +void UVIAssetManager::StartInitialLoading() +{ + Super::StartInitialLoading(); + + UAbilitySystemGlobals::Get().InitGlobalData(); +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIGameplayAbility.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIGameplayAbility.cpp new file mode 100644 index 00000000..99c801ff --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/GAS/VIGameplayAbility.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "GAS/VIGameplayAbility.h" +#include "Logging/MessageLog.h" +#include "AbilitySystemComponent.h" + +#if WITH_EDITOR +void UVIGameplayAbility::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.GetPropertyName().IsEqual(TEXT("InstancingPolicy"))) + { + if (InstancingPolicy == EGameplayAbilityInstancingPolicy::NonInstanced) + { + InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; + + FMessageLog MsgLog("AssetCheck"); + + MsgLog.Error(FText::FromString("VIGameplayAbility must be instanced, resetting to default automatically")); + MsgLog.Open(EMessageSeverity::Error); + } + } +} +#endif // WITH_EDITOR + +void UVIGameplayAbility::SetCurrentMontageForMesh(USkeletalMeshComponent* InMesh, UAnimMontage* InCurrentMontage) +{ + ensure(IsInstantiated()); + + FVIAbilityMeshMontage AbilityMeshMontage; + if (FindAbillityMeshMontage(InMesh, AbilityMeshMontage)) + { + AbilityMeshMontage.Montage = InCurrentMontage; + } + else + { + CurrentAbilityMeshMontages.Add(FVIAbilityMeshMontage(InMesh, InCurrentMontage)); + } +} + +bool UVIGameplayAbility::FindAbillityMeshMontage(const USkeletalMeshComponent* InMesh, FVIAbilityMeshMontage& InAbilityMeshMontage) +{ + for (const FVIAbilityMeshMontage& MeshMontage : CurrentAbilityMeshMontages) + { + if (MeshMontage.Mesh == InMesh) + { + InAbilityMeshMontage = MeshMontage; + return true; + } + } + + return false; +} + +FString UVIGameplayAbility::GetCurrentPredictionKeyStatus() const +{ + const UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo(); + return ASC->ScopedPredictionKey.ToString() + " is valid for more prediction: " + (ASC->ScopedPredictionKey.IsValidForMorePrediction() ? TEXT("true") : TEXT("false")); +} + +bool UVIGameplayAbility::IsPredictionKeyValidForMorePrediction() const +{ + const UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo(); + return ASC->ScopedPredictionKey.IsValidForMorePrediction(); +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacter.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacter.cpp new file mode 100644 index 00000000..309179ae --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacter.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Pawn/VICharacter.h" +#include "GAS/VIAbilitySystemComponent.h" +#include "Pawn/VIPawnVaultComponent.h" +#include "MotionWarpingComponent.h" + +AVICharacter::AVICharacter(const FObjectInitializer& OI) + : Super(OI) +{ + VaultComponent = CreateDefaultSubobject(TEXT("PawnVaulting")); + MotionWarpingComponent = CreateDefaultSubobject(TEXT("MotionWarping")); +} + +void AVICharacter::BeginPlay() +{ + Super::BeginPlay(); + + // Init simulated proxy + if (AbilitySystem && GetLocalRole() == ROLE_SimulatedProxy) + { + // Will never have a valid controller + AbilitySystem->InitAbilityActorInfo(this, this); + } +} + +void AVICharacter::PossessedBy(AController* NewController) +{ + Super::PossessedBy(NewController); + + // Init authority/standalone + if (AbilitySystem) + { + AbilitySystem->InitAbilityActorInfo(this, this); + } +} + +void AVICharacter::OnRep_Controller() +{ + Super::OnRep_Controller(); + + // Init local client + if (AbilitySystem) + { + AbilitySystem->InitAbilityActorInfo(this, this); + } +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterAbilityBase.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterAbilityBase.cpp new file mode 100644 index 00000000..cd61d954 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterAbilityBase.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Pawn/VICharacterAbilityBase.h" +#include "GAS/VIAbilitySystemComponent.h" + +AVICharacterAbilityBase::AVICharacterAbilityBase(const FObjectInitializer& OI) + : Super(OI) +{ + AbilitySystem = CreateDefaultSubobject(TEXT("AbilitySystem")); + AbilitySystem->SetIsReplicated(true); + AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Mixed); + AbilitySystemReplicationMode = (EVIGameplayEffectReplicationMode)(uint8)AbilitySystem->ReplicationMode; +} + +#if WITH_EDITOR +void AVICharacterAbilityBase::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.GetPropertyName().IsEqual(GET_MEMBER_NAME_CHECKED(AVICharacterAbilityBase, AbilitySystemReplicationMode))) + { + AbilitySystem->SetReplicationMode((EGameplayEffectReplicationMode)(uint8)AbilitySystemReplicationMode); + } +} +#endif // WITH_EDITOR + +UAbilitySystemComponent* AVICharacterAbilityBase::GetAbilitySystemComponent() const +{ + return AbilitySystem; +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterBase.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterBase.cpp new file mode 100644 index 00000000..d994a713 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VICharacterBase.cpp @@ -0,0 +1,308 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Pawn/VICharacterBase.h" +#include "Net/UnrealNetwork.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "Pawn/VIPawnVaultComponent.h" +#include "MotionWarpingComponent.h" +#include "VIBlueprintFunctionLibrary.h" + +void AVICharacterBase::BeginPlay() +{ + Super::BeginPlay(); + + VaultComponent = IVIPawnInterface::Execute_GetPawnVaultComponent(this); + MotionWarpingComponent = IVIPawnInterface::Execute_GetMotionWarpingComponent(this); +} + +void AVICharacterBase::CheckJumpInput(float DeltaTime) +{ + const bool bIsVaulting = IsVaulting(); + + // Server update simulated proxies with correct vaulting state + if (GetLocalRole() == ROLE_Authority && GetNetMode() != NM_Standalone) + { + bRepIsVaulting = bIsVaulting; + } + + // Try to vault from local input + if (IsLocallyControlled() && VaultComponent) + { + // Disable jump if vaulting + if (VaultComponent->bPressedVault) + { + bPressedJump = false; + } + + // Possibly execute vault + if (GetCharacterMovement()) + { + VaultComponent->CheckVaultInput(DeltaTime, GetCharacterMovement()->MovementMode); + } + else + { + VaultComponent->CheckVaultInput(DeltaTime); + } + } + + // Pick up changes in vaulting state to change movement mode + // to something other than flying (required for root motion on Z) + if (bWasVaulting && !bIsVaulting) + { + StopVaultAbility(); + } + + // Call super so we actually jump if we're meant to + Super::CheckJumpInput(DeltaTime); + + // Cache end of frame + bWasVaulting = bIsVaulting; +} + +void AVICharacterBase::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME_CONDITION(AVICharacterBase, bRepIsVaulting, COND_SimulatedOnly); + DOREPLIFETIME_CONDITION(AVICharacterBase, RepMotionMatch, COND_SimulatedOnly); +} + +void AVICharacterBase::Jump() +{ + // If missing critical components then jump and exit + if (!VaultComponent || !GetCharacterMovement()) + { + Super::Jump(); + return; + } + + // Either jump or vault, determined by VaultComponent::EVIJumpKeyPriority + if (VaultComponent->Jump(GetCharacterMovement()->GetGravityZ(), CanJump(), GetCharacterMovement()->IsFalling())) + { + // Jump normally + Super::Jump(); + } + else + { + // Jump key essentially presses the vault input + VaultComponent->Vault(); + } +} + +void AVICharacterBase::StopJumping() +{ + Super::StopJumping(); + + // Release vault input if the jump key pressed vault instead + if (VaultComponent) + { + VaultComponent->StopJumping(); + } +} + +void AVICharacterBase::StartVaultAbility_Implementation() +{ + // Called by GA_Vault + // Need to be in flying mode to have root motion on Z axis + if (GetCharacterMovement() && GetLocalRole() > ROLE_SimulatedProxy) + { + GetCharacterMovement()->SetMovementMode(MOVE_Flying); + } +} + +void AVICharacterBase::StopVaultAbility() +{ + // Called by CheckJumpInput() + // Exiting flying mode + // This may put is straight into falling if we aren't properly grounded, which is fine + if (GetCharacterMovement() && GetLocalRole() > ROLE_SimulatedProxy) + { + GetCharacterMovement()->SetMovementMode(GetCharacterMovement()->GetGroundMovementMode()); + } + + OnStopVaultAbility(); +} + +void AVICharacterBase::OnRep_MotionMatch() +{ + // Simulated proxies update their sync points here, sent from the server during GA_Vault + MotionWarpingComponent->AddOrUpdateWarpTargetFromLocationAndRotation(TEXT("VaultSyncPoint"), RepMotionMatch.Location, RepMotionMatch.Direction.Rotation()); +} + +bool AVICharacterBase::IsVaulting() const +{ + // Simulated proxies use the value provided by server + if (GetLocalRole() == ROLE_SimulatedProxy) + { + return bRepIsVaulting; + } + + // Local and authority uses gameplay tags for a predicted result + if (VaultComponent) + { + return VaultComponent->IsVaulting(); + } + + return false; +} + +// *********************************************** // +// ******** Begin Pawn Vaulting Interface ******** // +// *********************************************** // + +UVIPawnVaultComponent* AVICharacterBase::GetPawnVaultComponent_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetPawnVaultComponent not implemented for { %s }. Cannot Vault."), *GetName())); + return nullptr; +} + +UMotionWarpingComponent* AVICharacterBase::GetMotionWarpingComponent_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetMotionWarpingComponent not implemented for { %s }. Cannot Vault."), *GetName())); + return nullptr; +} + +FVIAnimSet AVICharacterBase::GetVaultAnimSet_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetVaultAnimSet not implemented for { %s }. Cannot Vault."), *GetName())); + return FVIAnimSet(); +} + +FVITraceSettings AVICharacterBase::GetVaultTraceSettings_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetVaultTraceSettings not implemented for { %s }. Using default trace settings."), *GetName()), false); + return FVITraceSettings(); +} + +FVector AVICharacterBase::GetVaultDirection_Implementation() const +{ + // Use input vector if available + if (GetCharacterMovement() && !GetCharacterMovement()->GetCurrentAcceleration().IsNearlyZero()) + { + return GetCharacterMovement()->GetCurrentAcceleration(); + } + + // Use character facing direction if not providing input + return GetActorForwardVector(); +} + +bool AVICharacterBase::CanVault_Implementation() const +{ + // Vaulting must finish before starting another vault attempt + if (IsVaulting()) + { + return false; + } + + // Invalid components + if (!VaultComponent || !GetCharacterMovement()) + { + return false; + } + + // Animation instance is required to play vault montage + if (!GetMesh() || !GetMesh()->GetAnimInstance()) + { + return false; + } + + // Authority not initialized (this isn't set on clients) + if (HasAuthority() && !VaultComponent->bVaultAbilityInitialized) + { + return false; + } + + // Exit if character is in a state they cannot vault from + if (GetCharacterMovement()->IsMovingOnGround() || GetCharacterMovement()->IsFalling() || GetCharacterMovement()->IsSwimming()) + { + if (GetCharacterMovement()->IsMovingOnGround() && !VaultComponent->bCanVaultFromGround) + { + return false; + } + + if (GetCharacterMovement()->IsFalling() && !VaultComponent->bCanVaultFromFalling) + { + return false; + } + + if (GetCharacterMovement()->IsSwimming() && !VaultComponent->bCanVaultFromSwimming) + { + return false; + } + } + else + { + return false; + } + + // Can't vault while crouching + if (!VaultComponent->bCanVaultFromCrouching && GetCharacterMovement()->IsCrouching()) + { + return false; + } + + // Passed all conditions + return true; +} + +void AVICharacterBase::OnLocalPlayerVault_Implementation(const FVector& Location, const FVector& Direction) +{ + // LocalPlayer just stores the data in the same place for convenience, ease of use, memory reduction, etc + RepMotionMatch = FVIRepMotionMatch(Location, Direction); +} + +void AVICharacterBase::GetVaultLocationAndDirection_Implementation(FVector& OutLocation, FVector& OutDirection) const +{ + // Because LocalPlayer stores in the same place, no need for any testing as they all use RepMotionMatch to store this + + // This is only currently used for FBIK tracing + OutLocation = RepMotionMatch.Location; + OutDirection = RepMotionMatch.Direction; +} + +void AVICharacterBase::ReplicateMotionMatch_Implementation(const FVIRepMotionMatch& MotionMatch) +{ + // GA_Vault has directed server to update it's RepMotionMatch property so that it will + // be replicated to simulated proxies with 1 decimal point of precision (net quantization) + RepMotionMatch = MotionMatch; +} + +bool AVICharacterBase::IsWalkable_Implementation(const FHitResult& HitResult) const +{ + // Surface we hit can be walked on or not + return GetCharacterMovement() && GetCharacterMovement()->IsWalkable(HitResult); +} + +bool AVICharacterBase::CanAutoVaultInCustomMovementMode_Implementation() const +{ + return true; + + // Example usage commented out + + /* + + if (GetCharacterMovement()) + { + switch (GetCharacterMovement()->CustomMovementMode) + { + case 0: + return false; + case 1: // Some example custom mode where auto vault can work + return true; + case 2: + return false; + default: + return true; + } + } + + */ +} + +// *********************************************** // +// ********* End Pawn Vaulting Interface ********* // +// *********************************************** // \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VIPawnVaultComponent.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VIPawnVaultComponent.cpp new file mode 100644 index 00000000..8ad33b58 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/Pawn/VIPawnVaultComponent.cpp @@ -0,0 +1,517 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "Pawn/VIPawnVaultComponent.h" +#include "VIBlueprintFunctionLibrary.h" +#include "GameFramework/Character.h" +#include "Components/CapsuleComponent.h" +#include "AbilitySystemInterface.h" +#include "GAS/VIAbilitySystemComponent.h" +#include "Pawn/VIPawnInterface.h" +#include "GameFramework/PawnMovementComponent.h" +#include "Kismet/GameplayStaticsTypes.h" +#include "AbilitySystemBlueprintLibrary.h" +#include "Kismet/KismetSystemLibrary.h" + +DECLARE_CYCLE_STAT(TEXT("AutoVault"), STAT_VAULTAUTOVAULT, STATGROUP_VaultIt); + +DEFINE_LOG_CATEGORY_STATIC(LogVaultItAntiCheat, Log, All); + +void UVIPawnVaultComponent::BeginPlay() +{ + Super::BeginPlay(); + + // Cache PawnOwner + PawnOwner = (GetOwner()) ? GetOwner() : nullptr; + + if (PawnOwner) + { + // Cache CapsuleInfo + CapsuleInfo = GetCapsuleInfo(); + + // Cache ASC + if (const IAbilitySystemInterface* const Interface = Cast(PawnOwner)) + { + ASC = Interface->GetAbilitySystemComponent(); + if (!ASC) + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("UVIPawnVaultComponent::BeginPlay %s IAbilitySystemInterface does not return a valid UAbilitySystemComponent. Vault will not work."), *PawnOwner->GetName())); + } + } + else + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("UVIPawnVaultComponent::BeginPlay %s does not implement interface IAbilitySystemInterface. Vault will not work."), *PawnOwner->GetName())); + return; + } + } + + // Grant Vault Ability to PawnOwner ASC (on server / standalone) + // Has a lot of error logging if anything is invalid to reduce user error + if (PawnOwner->GetLocalRole() == ROLE_Authority && !bVaultAbilityInitialized) + { + if (VaultAbility) + { + if (VaultAbilityTag.IsValid()) + { + bVaultAbilityInitialized = true; + ASC->GiveAbility(FGameplayAbilitySpec(VaultAbility, 1, INDEX_NONE, PawnOwner)); + } + else + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("VaultTag not assigned for { %s }"), *GetName())); + } + } + else + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("VaultAbility class not assigned for { %s }"), *GetName())); + } + + if (!VaultStateTag.IsValid()) + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("VaultStateTag not assigned for { %s }"), *GetName())); + } + + if (!VaultRemovalTag.IsValid()) + { + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("VaultRemovalTag not assigned for { %s }"), *GetName())); + } + } + + AutoVaultSkippedTicks = AutoVaultCheckSkip; +} + +bool UVIPawnVaultComponent::Jump(float GravityZ /* = 0.f */, bool bCanJump /* = false */, bool bIsFalling /* = false */) +{ + bLastJumpInputVaulted = false; + + switch (JumpKeyPriority) + { + case EVIJumpKeyPriority::JKP_SelectHighestPoint: + // Test to see if jump is likely to succeed, otherwise vault + if (IVIPawnInterface::Execute_CanVault(PawnOwner)) + { + PendingVaultResult = ComputeVault(); + if (PendingVaultResult.bSuccess) + { + // Have a surface we can land on from the result + // now test if jumping will get us there instead + // by testing against the vertical height of predicted + // landing location + + FPredictProjectilePathResult PathResult = FPredictProjectilePathResult(); + bool bUseJump = PredictLandingLocation(PathResult, CapsuleInfo.HalfHeight, CapsuleInfo.Radius, GravityZ); + + if (bUseJump) + { + const FVector& Up = PawnOwner->GetActorUpVector(); + + const FVector& LandLoc = PathResult.HitResult.Location; + const FVector VaultLoc = PendingVaultResult.Location - (Up * CapsuleInfo.HalfHeight); + + const float LandZ = UVIBlueprintFunctionLibrary::ComputeDirectionToFloat(LandLoc, Up); + const float VaultZ = UVIBlueprintFunctionLibrary::ComputeDirectionToFloat(VaultLoc, Up); + + if (LandZ < VaultZ) + { + bUseJump = false; + } + } + + if (bUseJump) + { + PendingVaultResult = FVIVaultResult(); + return true; + } + else + { + bLastJumpInputVaulted = true; + return false; + } + } + else + { + return true; + } + } + else + { + return true; + } + break; + case EVIJumpKeyPriority::JKP_AlwaysVault: + // If it can vault it will, otherwise jump + if (IVIPawnInterface::Execute_CanVault(PawnOwner)) + { + PendingVaultResult = ComputeVault(); + if (PendingVaultResult.bSuccess) + { + bLastJumpInputVaulted = true; + return false; + } + else + { + PendingVaultResult = FVIVaultResult(); + return true; + } + } + break; + case EVIJumpKeyPriority::JKP_OnlyVaultFromAir: + // Determine if jump key can do anything + if (bIsFalling && !bCanJump) + { + bLastJumpInputVaulted = true; + return false; + } + else + { + return true; + } + break; + case EVIJumpKeyPriority::JKP_DisableVault: + default: + break; + } + + return true; +} + +void UVIPawnVaultComponent::StopJumping() +{ + if (bLastJumpInputVaulted) + { + StopVault(); + } +} + +void UVIPawnVaultComponent::Vault() +{ + bPressedVault = true; +} + +void UVIPawnVaultComponent::StopVault() +{ + bPressedVault = false; +} + +void UVIPawnVaultComponent::CheckVaultInput(float DeltaTime, TEnumAsByte MovementMode /* = MOVE_Walking */) +{ + if (PawnOwner && PawnOwner->IsLocallyControlled()) + { + // Compute AutoVault + bool bAutoVault = false; + if (!bPressedVault) + { + // Skip ticks optionally to reduce overhead + if (AutoVaultSkippedTicks == AutoVaultCheckSkip) + { + // Waste of resources to check if already vaulting + if (!IsVaulting() && AutoVaultStates != (uint8)EVIAutoVault::VIAV_None) + { + // Compare bit flags to see if we are in a state we're allowed to auto-vault from + switch (MovementMode) + { + case MOVE_Walking: + case MOVE_NavWalking: + bAutoVault = (AutoVaultStates & (uint8)EVIAutoVault::VIAV_Walking) != 0; + break; + case MOVE_Falling: + bAutoVault = (AutoVaultStates & (uint8)EVIAutoVault::VIAV_Falling) != 0; + break; + case MOVE_Swimming: + bAutoVault = (AutoVaultStates & (uint8)EVIAutoVault::VIAV_Swimming) != 0; + break; + case MOVE_Flying: + bAutoVault = (AutoVaultStates & (uint8)EVIAutoVault::VIAV_Flying) != 0; + break; + case MOVE_Custom: + bAutoVault = (AutoVaultStates & (uint8)EVIAutoVault::VIAV_Custom) != 0; + if (bAutoVault) + { + bAutoVault = IVIPawnInterface::Execute_CanAutoVaultInCustomMovementMode(PawnOwner); + } + default: + break; + } + + if (bAutoVault) + { + // Perform traces to test if we should auto vault + bAutoVault &= ComputeShouldAutoVault(); + } + } + + AutoVaultSkippedTicks = 0; + } + else + { + AutoVaultSkippedTicks++; + } + } + + if (bPressedVault || bAutoVault) + { + if (IVIPawnInterface::Execute_CanVault(PawnOwner)) + { + const FVIVaultResult& VaultResult = (PendingVaultResult.IsValid()) ? PendingVaultResult : ComputeVault(); + + if (VaultResult.bSuccess) + { + // Consume input if required + if (AutoReleaseVaultInput != EVIVaultInputRelease::VIR_Never) { bPressedVault = false; } + + // Send vault result as info through EventData + FVIGameplayAbilityTargetData_VaultInfo Info; + Info.VaultInfo = ComputeVaultInfoFromResult(VaultResult); + + // Cache gameplay ability event data to be sent + FGameplayEventData EventData; + EventData.Instigator = PawnOwner; + EventData.TargetData.Add(new FVIGameplayAbilityTargetData_VaultInfo(Info)); + + // Trigger ability and send event data to ability & server + UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(PawnOwner, VaultAbilityTag, EventData); + + // Send to Pawn to use for FBIK (or anything extended by user) + IVIPawnInterface::Execute_OnLocalPlayerVault(PawnOwner, VaultResult.Location, VaultResult.Direction); + } + } + } + } + + if (AutoReleaseVaultInput == EVIVaultInputRelease::VIR_Always) + { + bPressedVault = false; + } + + // Clear at end of frame always; triggered by jump key + PendingVaultResult = FVIVaultResult(); +} + +bool UVIPawnVaultComponent::IsVaulting() const +{ + if (ASC) + { + return ASC->GetTagCount(VaultStateTag) > ASC->GetTagCount(VaultRemovalTag); + } + + return false; +} + +FVIVaultResult UVIPawnVaultComponent::ComputeVault() const +{ + return UVIBlueprintFunctionLibrary::ComputeVault(PawnOwner, IVIPawnInterface::Execute_GetVaultDirection(PawnOwner), IVIPawnInterface::Execute_GetVaultTraceSettings(PawnOwner), CapsuleInfo, bVaultTraceComplex); +} + +FVIVaultInfo UVIPawnVaultComponent::ComputeVaultInfoFromResult_Implementation(const FVIVaultInfo& VaultResult) const +{ + FVIVaultInfo Result; + + // Cache properties + const FVector& Up = PawnOwner->GetActorUpVector(); + const FVector Location = VaultResult.Location - Up * CapsuleInfo.HalfHeight; + + const bool bUseAdditionalHeight = (AdditionalVaultHeight > 0.f || AdditionalVaultHeightFalling > 0.f); + const float AdditionalHeight = (bUseAdditionalHeight && PawnOwner->GetMovementComponent() && PawnOwner->GetMovementComponent()->IsFalling()) ? AdditionalVaultHeightFalling : AdditionalVaultHeight; + const FVector AdditionalHeightOffset = (bUseAdditionalHeight) ? Up * AdditionalHeight : FVector::ZeroVector; + + // Compute + Result.Location = Location + AdditionalHeightOffset; // Where we are going on the ledge + Result.Direction = VaultResult.Direction; // Used by montage notify to face ledge + Result.SetHeight(VaultResult.Height); // Used to determine which animation set to use + + if (PawnOwner->IsLocallyControlled()) + { + Result.RandomSeed = FMath::RandRange(0, 999); // Used as a rand seed to randomize animations + } + + return Result; +} + +bool UVIPawnVaultComponent::ComputeShouldAutoVault_Implementation() +{ + if (!CapsuleInfo.IsValidCapsule()) + { + return false; + } + + const FVector& VaultDirection = IVIPawnInterface::Execute_GetVaultDirection(PawnOwner).GetSafeNormal(); + if (VaultDirection.IsNearlyZero()) + { + // Do not have a usable vector + return false; + } + + SCOPE_CYCLE_COUNTER(STAT_VAULTAUTOVAULT); + + // Trace to see if a potentially vault-able object is ahead of us + const FVITraceSettings& TraceSettings = IVIPawnInterface::Execute_GetVaultTraceSettings(PawnOwner); + + const float HeightOffset = CapsuleInfo.HalfHeight + TraceSettings.CollisionFloatHeight; + const FVector& Up = PawnOwner->GetActorUpVector(); + const FVector BaseLoc = PawnOwner->GetActorLocation() - (Up * HeightOffset) - (Up * CapsuleInfo.Radius * 0.05f); + + const FVector TraceStart = BaseLoc + Up * ((TraceSettings.MaxLedgeHeight + TraceSettings.MinLedgeHeight) * 0.5f); + const FVector TraceEnd = TraceStart + (VaultDirection * TraceSettings.ReachDistance); + const float TraceHalfHeight = 1.f + ((TraceSettings.MaxLedgeHeight - TraceSettings.MinLedgeHeight) * 0.5f); + + const TArray TraceIgnore = { PawnOwner }; + + FHitResult Hit(ForceInit); + UVIBlueprintFunctionLibrary::CapsuleTraceSingleForObjects( + PawnOwner, // World Context + TraceStart, // Start + TraceEnd, // End + PawnOwner->GetActorQuat(), // Rotation + CapsuleInfo.Radius, // Radius + TraceHalfHeight, // HalfHeight + TraceSettings.GetObjectTypes(), // TraceChannels + false, // bTraceComplex + TraceIgnore, // ActorsToIgnore + false, // bDrawDebug (ForDuration) + Hit, // OutHit + false, // bIgnoreSelf + FLinearColor::Red, // TraceColor + FLinearColor::Green, // TraceHitColor + 1.f // DrawTime + ); + + return Hit.bBlockingHit; +} + +FVICapsuleInfo UVIPawnVaultComponent::GetCapsuleInfo_Implementation() const +{ + // Always return the cached info if its valid, no need to do anything + // User is expected to update CapsuleInfo themselves if the size changes + if (CapsuleInfo.IsValidCapsule()) + { + return CapsuleInfo; + } + else if (const ACharacter* const CharacterOwner = Cast(PawnOwner)) + { + // Automatically use the character's capsule if owner is a character + if (CharacterOwner->GetCapsuleComponent()) + { + return FVICapsuleInfo(CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleRadius()); + } + else + { + // Just to prevent a crash in some strange contexts, better to pick it up so it can be resolved + ensure(false); + } + } + else + { + // Pawns must override this and return information akin to a capsule + UVIBlueprintFunctionLibrary::MessageLogError("UVIPawnVaultComponent::GetCapsuleInfo requires override for non-characters", true); + } + + return CapsuleInfo; +} + +bool UVIPawnVaultComponent::ComputeAntiCheatResult(const FVIVaultInfo& ClientVaultInfo) const +{ + if (!PawnOwner) + { + return true; + } + + // AntiCheat disabled + if (AntiCheatType == EVIAntiCheatType::VIACT_None) + { + return true; + } + + // Standalone and listen server and AI and client doesn't need to test + // Only need to test for remote players who sent info to server + if (PawnOwner->IsLocallyControlled()) + { + return true; + } + + // If too out of sync, deny vault (this will cause client to de-sync as server will force vaulting to end and return to original location) + switch (AntiCheatType) + { + case EVIAntiCheatType::VIACT_Enabled: + { + // Custom logic for expensive anti-cheat, server doing same checks as client and comparing the result + const FVIVaultResult& ServerVaultResult = ComputeVault(); + if (!ServerVaultResult.bSuccess) + { + // User may want to use more lenient settings for authority in VIPawnInterface::GetVaultTraceSettings() + return false; + } + + // Convert trace to usable info + const FVIVaultInfo ServerVaultInfo = ComputeVaultInfoFromResult(ServerVaultResult); + + if (!AntiCheatSettings.ComputeAntiCheat(ClientVaultInfo, ServerVaultInfo, PawnOwner)) + { + return false; + } + } + return true; + case EVIAntiCheatType::VIACT_Custom: + // Using custom AntiCheat override + return ComputeCustomAntiCheat(ClientVaultInfo); + case EVIAntiCheatType::VIACT_None: + default: + break; + } + + return true; +} + +bool FVIAntiCheatSettings::ComputeAntiCheat(const FVIVaultInfo& ClientInfo, const FVIVaultInfo& ServerInfo, const APawn* const Pawn) const +{ + // The results here are almost always nearly identical due to prediction unless de-syncing, lower tolerances can be used + + if (!Pawn) + { + return true; + } + + // Location test (magnitude) + if (LocationErrorThreshold > -1.f) + { + const float LocDiff = (ServerInfo.Location - ClientInfo.Location).Size(); + if (LocDiff >= LocationErrorThreshold) + { + UE_LOG(LogVaultItAntiCheat, Warning, TEXT("{ %s } failed anti-cheat test due to error in location comparison with diff %f"), *Pawn->GetName(), LocDiff); + return false; + } + } + + // Direction test (dot product) + if (DirectionErrorThreshold > -1.f) + { + const float DirDiff = (ServerInfo.Direction | ClientInfo.Direction); + if (DirDiff < (1.f - DirectionErrorThreshold)) + { + UE_LOG(LogVaultItAntiCheat, Warning, TEXT("{ %s } failed anti-cheat test due to error in direction comparison with diff %f"), *Pawn->GetName(), DirDiff); + return false; + } + } + + // Height test (magnitude) + if (HeightErrorThreshold > -1.f) + { + const float HeightDiff = (ServerInfo.Height - ClientInfo.Height); + if (HeightDiff >= HeightErrorThreshold) + { + UE_LOG(LogVaultItAntiCheat, Warning, TEXT("{ %s } failed anti-cheat test due to error in height comparison with diff %f"), *Pawn->GetName(), HeightDiff); + return false; + } + } + + return true; +} + +bool UVIPawnVaultComponent::ComputeCustomAntiCheat_Implementation(const FVIVaultInfo& ClientVaultInfo) const +{ + UVIBlueprintFunctionLibrary::MessageLogError("UVIPawnVaultComponent::PassCustomAntiCheatAnalysis requires override and testing, otherwise will always return true", true); + return true; +} + +bool UVIPawnVaultComponent::PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, float HalfHeight, float Radius, float GravityZ) const +{ + return UVIBlueprintFunctionLibrary::PredictLandingLocation(OutPredictResult, PawnOwner, IVIPawnInterface::Execute_GetVaultTraceSettings(PawnOwner).GetObjectTypes(), HalfHeight, Radius, GravityZ, bVaultTraceComplex); +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VIBlueprintFunctionLibrary.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VIBlueprintFunctionLibrary.cpp new file mode 100644 index 00000000..69b53a0d --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VIBlueprintFunctionLibrary.cpp @@ -0,0 +1,576 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "VIBlueprintFunctionLibrary.h" +#include "Kismet/GameplayStaticsTypes.h" +#include "Pawn/VICharacterBase.h" +#include "Components/CapsuleComponent.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "Pawn/VIPawnInterface.h" +#include "Stats/Stats.h" +#include "CollisionQueryParams.h" +#include "PhysicsEngine/PhysicsSettings.h" +#include "DrawDebugHelpers.h" + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("ComputeVault"), STAT_COMPUTEVAULT_COUNT, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("ComputeVault"), STAT_COMPUTEVAULT, STATGROUP_VaultIt); + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("PredictCapsulePath"), STAT_PREDICTLANDINGLOCATION_COUNT, STATGROUP_VaultIt); +DECLARE_CYCLE_STAT(TEXT("PredictCapsulePath"), STAT_PREDICTLANDINGLOCATION, STATGROUP_VaultIt); + +void UVIBlueprintFunctionLibrary::MessageLogError(const FString& ErrorMsg, bool bOpenLog /*= true*/) +{ + FMessageLog MsgLog("PIE"); + + MsgLog.Error(FText::FromString(ErrorMsg)); + + if (bOpenLog) + { + MsgLog.Open(EMessageSeverity::Error); + } +} + +float UVIBlueprintFunctionLibrary::GetMaxHeightFromGround(const ACharacter* Character) +{ + if(!Character || !Character->GetCharacterMovement()) + { + return 0.f; + } + + return GetMaxHeightFromGroundForPawn(Character, Character->GetCharacterMovement()->GetGravityZ(), Character->GetCharacterMovement()->JumpZVelocity); +} + +float UVIBlueprintFunctionLibrary::GetMaxVaultHeight(const APawn* Pawn) +{ + if (Pawn) + { + if (Pawn->Implements()) + { + return IVIPawnInterface::Execute_GetVaultTraceSettings(Pawn).MaxLedgeHeight; + } + else + { + FMessageLog MsgLog("AssetCheck"); + + MsgLog.Error(FText::FromString(FString::Printf(TEXT("UVIBlueprintFunctionLibrary::GetMaxHeightFromGroundForPawn failed: %s does not implement interface UVIPawnInterface"), *Pawn->GetName()))); + MsgLog.Open(EMessageSeverity::Error); + } + } + + return 0.f; +} + +float UVIBlueprintFunctionLibrary::GetMaxHeightFromGroundForPawn(const APawn* Pawn, const float Gravity, const float JumpZVelocity) +{ + return GetMaxJumpHeight(Gravity, JumpZVelocity) + GetMaxVaultHeight(Pawn); +} + +float UVIBlueprintFunctionLibrary::GetMaxJumpHeightForCharacter(const ACharacter* Character) +{ + if (Character && Character->GetCharacterMovement()) + { + return GetMaxJumpHeight(Character->GetCharacterMovement()->GetGravityZ(), Character->GetCharacterMovement()->JumpZVelocity); + } + + return 0.f; +} + +bool UVIBlueprintFunctionLibrary::ActorIsAscending(const AActor* InActor, bool bWorldUpIsZ) +{ + if (!InActor) + { + return false; + } + + if (bWorldUpIsZ) + { + return InActor->GetVelocity().Z > 0.f; + } + + return ComputeDirectionToFloat(InActor->GetVelocity(), InActor->GetActorUpVector()) > 0.f; +} + +float UVIBlueprintFunctionLibrary::ComputeAnimationPlayRateFromDuration(const UAnimSequenceBase* Animation, + float Duration) +{ + if (Animation && Duration > 0.f) + { + return Animation->GetPlayLength() / Duration; + } + return 0.f; +} + +FVIVaultInfo UVIBlueprintFunctionLibrary::GetVaultInfoFromTargetData(const FGameplayAbilityTargetDataHandle& TargetData) +{ + if (TargetData.Data.IsValidIndex(0)) + { + const FGameplayAbilityTargetData* Data = TargetData.Data[0].Get(); + if (Data) + { + const FVIGameplayAbilityTargetData_VaultInfo* VaultData = (const FVIGameplayAbilityTargetData_VaultInfo*)(Data); + const FVIVaultInfo* VaultInfoPtr = VaultData->GetVaultInfo(); + if (VaultInfoPtr) + { + return *VaultInfoPtr; + } + } + } + + return FVIVaultInfo(); +} + +bool UVIBlueprintFunctionLibrary::PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, ACharacter* ForCharacter, const TArray>& ObjectTypes, bool bTraceComplex) +{ + if (!ForCharacter) + { + return false; + } + + return PredictLandingLocation( + OutPredictResult, + ForCharacter, + ObjectTypes, + ForCharacter->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), + ForCharacter->GetCapsuleComponent()->GetScaledCapsuleRadius(), + ForCharacter->GetCharacterMovement()->GetGravityZ(), + bTraceComplex + ); +} + +bool UVIBlueprintFunctionLibrary::PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, AActor* ForActor, const TArray>& ObjectTypes, float HalfHeight, float Radius, const FVector& Gravity, bool bTraceComplex) +{ + if (!ForActor) + { + return false; + } + + const float GravityZ = ComputeDirectionToFloat(Gravity, ForActor->GetActorUpVector()); + return PredictLandingLocation(OutPredictResult, ForActor, ObjectTypes, HalfHeight, Radius, GravityZ, bTraceComplex); +} + +bool UVIBlueprintFunctionLibrary::PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, AActor* ForActor, const TArray>& ObjectTypes, float HalfHeight, float Radius, float GravityZ, bool bTraceComplex) +{ + if (!ForActor) + { + return false; + } + + // Performance profiling + INC_DWORD_STAT(STAT_PREDICTLANDINGLOCATION_COUNT); + SCOPE_CYCLE_COUNTER(STAT_PREDICTLANDINGLOCATION); + + Radius *= 0.98f; + constexpr float MaxSimTime = 2.f; + constexpr float SimFrequency = 15.f; + const TArray TraceIgnore{ ForActor }; + + FPredictProjectilePathParams Params = FPredictProjectilePathParams(Radius, ForActor->GetActorLocation(), ForActor->GetVelocity(), MaxSimTime); + Params.bTraceWithCollision = true; + Params.bTraceComplex = bTraceComplex; + Params.ActorsToIgnore = TraceIgnore; + Params.SimFrequency = SimFrequency; + Params.OverrideGravityZ = GravityZ; + Params.ObjectTypes = ObjectTypes; + + // Do the trace + if (!PredictCapsulePath(ForActor, HalfHeight, Params, OutPredictResult, ForActor->GetActorUpVector())) + { + return false; + } + return true; +} + +bool UVIBlueprintFunctionLibrary::PredictCapsulePath(const UObject* WorldContextObject, float HalfHeight, const struct FPredictProjectilePathParams& PredictParams, struct FPredictProjectilePathResult& PredictResult, const FVector& UpVector) +{ + PredictResult.Reset(); + bool bBlockingHit = false; + + UWorld const* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); + if (World && PredictParams.SimFrequency > KINDA_SMALL_NUMBER) + { + const float SubstepDeltaTime = 1.f / PredictParams.SimFrequency; + const float GravityZ = FMath::IsNearlyEqual(PredictParams.OverrideGravityZ, 0.0f) ? World->GetGravityZ() : PredictParams.OverrideGravityZ; + const float ProjectileRadius = PredictParams.ProjectileRadius; + + FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(PredictProjectilePath), PredictParams.bTraceComplex); + FCollisionObjectQueryParams ObjQueryParams; + const bool bTraceWithObjectType = (PredictParams.ObjectTypes.Num() > 0); + const bool bTracePath = PredictParams.bTraceWithCollision && (PredictParams.bTraceWithChannel || bTraceWithObjectType); + if (bTracePath) + { + QueryParams.AddIgnoredActors(PredictParams.ActorsToIgnore); + if (bTraceWithObjectType) + { + for (auto Iter = PredictParams.ObjectTypes.CreateConstIterator(); Iter; ++Iter) + { + const ECollisionChannel& Channel = UCollisionProfile::Get()->ConvertToCollisionChannel(false, *Iter); + ObjQueryParams.AddObjectTypesToQuery(Channel); + } + } + } + + FVector CurrentVel = PredictParams.LaunchVelocity; + FVector TraceStart = PredictParams.StartLocation; + FVector TraceEnd = TraceStart; + float CurrentTime = 0.f; + PredictResult.PathData.Reserve(FMath::Min(128, FMath::CeilToInt(PredictParams.MaxSimTime * PredictParams.SimFrequency))); + PredictResult.AddPoint(TraceStart, CurrentVel, CurrentTime); + + FHitResult ObjectTraceHit(NoInit); + FHitResult ChannelTraceHit(NoInit); + ObjectTraceHit.Time = 1.f; + ChannelTraceHit.Time = 1.f; + + const float MaxSimTime = PredictParams.MaxSimTime; + while (CurrentTime < MaxSimTime) + { + // Limit step to not go further than total time. + const float PreviousTime = CurrentTime; + const float ActualStepDeltaTime = FMath::Min(MaxSimTime - CurrentTime, SubstepDeltaTime); + CurrentTime += ActualStepDeltaTime; + + // Integrate (Velocity Verlet method) + TraceStart = TraceEnd; + FVector OldVelocity = CurrentVel; + CurrentVel = OldVelocity + (UpVector * GravityZ * ActualStepDeltaTime); + //CurrentVel = OldVelocity + FVector(0.f, 0.f, GravityZ * ActualStepDeltaTime); + TraceEnd = TraceStart + (OldVelocity + CurrentVel) * (0.5f * ActualStepDeltaTime); + PredictResult.LastTraceDestination.Set(TraceEnd, CurrentVel, CurrentTime); + + if (bTracePath) + { + bool bObjectHit = false; + bool bChannelHit = false; + if (bTraceWithObjectType) + { + bObjectHit = World->SweepSingleByObjectType(ObjectTraceHit, TraceStart, TraceEnd, FQuat::Identity, ObjQueryParams, FCollisionShape::MakeCapsule(ProjectileRadius, HalfHeight), QueryParams); + } + if (PredictParams.bTraceWithChannel) + { + bChannelHit = World->SweepSingleByChannel(ChannelTraceHit, TraceStart, TraceEnd, FQuat::Identity, PredictParams.TraceChannel, FCollisionShape::MakeCapsule(ProjectileRadius, HalfHeight), QueryParams); + } + + // See if there were any hits. + if (bObjectHit || bChannelHit) + { + // Hit! We are done. Choose trace with earliest hit time. + PredictResult.HitResult = (ObjectTraceHit.Time < ChannelTraceHit.Time) ? ObjectTraceHit : ChannelTraceHit; + const float HitTimeDelta = ActualStepDeltaTime * PredictResult.HitResult.Time; + const float TotalTimeAtHit = PreviousTime + HitTimeDelta; + const FVector VelocityAtHit = OldVelocity + (UpVector * GravityZ * HitTimeDelta); + //const FVector VelocityAtHit = OldVelocity + FVector(0.f, 0.f, GravityZ * HitTimeDelta); + PredictResult.AddPoint(PredictResult.HitResult.Location, VelocityAtHit, TotalTimeAtHit); + bBlockingHit = true; + break; + } + } + + PredictResult.AddPoint(TraceEnd, CurrentVel, CurrentTime); + } + + // Draw debug path +#if ENABLE_DRAW_DEBUG + if (PredictParams.DrawDebugType != EDrawDebugTrace::None) + { + const bool bPersistent = PredictParams.DrawDebugType == EDrawDebugTrace::Persistent; + const float LifeTime = (PredictParams.DrawDebugType == EDrawDebugTrace::ForDuration) ? PredictParams.DrawDebugTime : 0.f; + const float DrawRadius = (ProjectileRadius > 0.f) ? ProjectileRadius : 5.f; + + // draw the path + for (const FPredictProjectilePathPointData& PathPt : PredictResult.PathData) + { + ::DrawDebugCapsule(World, PathPt.Location, HalfHeight, DrawRadius, FQuat::Identity, FColor::Green, bPersistent, LifeTime); + } + // draw the impact point + if (bBlockingHit) + { + ::DrawDebugCapsule(World, PredictResult.HitResult.Location, HalfHeight, DrawRadius + 1.0f, FQuat::Identity, FColor::Red, bPersistent, LifeTime); + } + } +#endif //ENABLE_DRAW_DEBUG + } + + return bBlockingHit; +} + +FVIVaultResult UVIBlueprintFunctionLibrary::ComputeVault(APawn* const Pawn, const FVector& InVaultDirection, const FVITraceSettings& TraceSettings, const FVICapsuleInfo& Capsule, bool bTraceComplex) +{ + FVIVaultResult Result = FVIVaultResult(); + + if (!IsValid(Pawn)) + { + // Invalid + return Result; + } + + if (!Pawn->Implements()) + { + // Needs pawn interface + FMessageLog MsgLog("PIE"); + + MsgLog.Error(FText::FromString(FString::Printf(TEXT("ComputeVault() failed: %s does not implement interface UVIPawnInterface"), *Pawn->GetName()))); + MsgLog.Open(EMessageSeverity::Error); + return Result; + } + + const FVector& VaultDirection = InVaultDirection.GetSafeNormal(); + if (VaultDirection.IsNearlyZero()) + { + // Do not have a usable vector + return Result; + } + + // Performance profiling + INC_DWORD_STAT(STAT_COMPUTEVAULT_COUNT); + SCOPE_CYCLE_COUNTER(STAT_COMPUTEVAULT); + + // Ease of access + const float HalfHeight = Capsule.HalfHeight; + const float Radius = Capsule.Radius; + + // Adding 1.f overcomes issues with step height and inconsistencies with MaxLedgeHeight + const float MaxLedgeHeight = TraceSettings.MaxLedgeHeight + 1.f; + const float MinLedgeHeight = TraceSettings.MinLedgeHeight + 1.f; + + const TArray TraceIgnore = { Pawn }; + + // Cache properties + const float HeightOffset = HalfHeight + TraceSettings.CollisionFloatHeight; + const FVector& Up = Pawn->GetActorUpVector(); + const FVector& Loc = Pawn->GetActorLocation(); + const FVector BaseLoc = Loc - (Up * HeightOffset) - (Up * Radius * 0.05f); + + // Trace forward to find something not walkable; don't climb something character can simply walk on + FHitResult NotWalkableHit(ForceInit); + { + // Cache trace + const FVector TraceStart = BaseLoc + Up * ((MaxLedgeHeight + MinLedgeHeight) * 0.5f); + const FVector TraceEnd = TraceStart + (VaultDirection * TraceSettings.ReachDistance); + const float TraceHalfHeight = 1.f + ((MaxLedgeHeight - MinLedgeHeight) * 0.5f); + + CapsuleTraceSingleForObjects( + Pawn, // World Context + TraceStart, // Start + TraceEnd, // End + Pawn->GetActorQuat(), // Rotation + TraceSettings.ForwardTraceRadius, // Radius + TraceHalfHeight, // HalfHeight + TraceSettings.GetObjectTypes(), // TraceChannels + bTraceComplex, // bTraceComplex + TraceIgnore, // ActorsToIgnore + false, // bDrawDebug (ForDuration) + NotWalkableHit, // OutHit + false, // bIgnoreSelf + FLinearColor::Red, // TraceColor + FLinearColor::Green, // TraceHitColor + 1.f // DrawTime + ); + + if (!NotWalkableHit.IsValidBlockingHit() || IVIPawnInterface::Execute_IsWalkable(Pawn, NotWalkableHit)) + { + // IsValidBlockingHit() returns false if no hit or starting in penetration + return Result; + } + } + + // Abort if object is moving too fast to vault onto + if (TraceSettings.MaxObjectVelocity > 0.f && NotWalkableHit.GetActor()->GetVelocity().Size() > TraceSettings.MaxObjectVelocity) + { + return Result; + } + + // Determined whats in front of us isn't walkable so lets check if we can stand on it + // Trace downward from first trace to ensure surface is walkable + FHitResult GroundHit(ForceInit); + { + const FVector ImpactLoc = NotWalkableHit.ImpactPoint; + const FVector ImpactNormal = NotWalkableHit.ImpactNormal; + + const FVector TraceFwd = FVector::VectorPlaneProject(ImpactLoc, Up); + const FVector TraceUp = BaseLoc.ProjectOnTo(Up); + + const FVector TraceEnd = TraceFwd + TraceUp + (ImpactNormal * -15.f); + const FVector TraceStart = TraceEnd + (Up * (MaxLedgeHeight + TraceSettings.DownwardTraceRadius + 1.f)); + + UKismetSystemLibrary::SphereTraceSingleForObjects( + Pawn, // World Context + TraceStart, // Start + TraceEnd, // End + TraceSettings.DownwardTraceRadius, // Radius + TraceSettings.GetObjectTypes(), // TraceChannels + bTraceComplex, // bTraceComplex + TraceIgnore, // ActorsToIgnore + EDrawDebugTrace::None, // DrawDebugType + GroundHit, // OutHit + false, // bIgnoreSelf + FLinearColor::Yellow, // TraceColor + FLinearColor::Blue, // TraceHitColor + 1.f // DrawTime + ); + + // If we can't walk on the surface don't try to vault onto it + if (!IVIPawnInterface::Execute_IsWalkable(Pawn, GroundHit)) + { + return Result; + } + } + + // Ensure capsule can fit at location + const FVector GroundLoc = FVector(GroundHit.Location.X, GroundHit.Location.Y, GroundHit.ImpactPoint.Z) + (Up * HeightOffset); + FHitResult Hit(1.f); + { + // Identical to GetCapsuleComponent()->GetScaledCapsuleHalfHeight_WithoutHemisphere() + const float HalfHeightNoHemisphere = HalfHeight - Radius; + + const FVector TraceStart = GroundLoc + (Up * HalfHeightNoHemisphere); + const FVector TraceEnd = GroundLoc - (Up * HalfHeightNoHemisphere); + + UKismetSystemLibrary::SphereTraceSingleByProfile( + Pawn, // World Context + TraceStart, // Start + TraceEnd, // End + Radius, // Radius + TraceSettings.TraceProfile, // ProfileName + bTraceComplex, // bTraceComplex + TraceIgnore, // ActorsToIgnore + EDrawDebugTrace::None, // DrawDebugType + Hit, // OutHit + false, // bIgnoreSelf + FLinearColor::Yellow, // TraceColor + FLinearColor::Blue, // TraceHitColor + 1.f // DrawTime + ); + + // No room if an obstacle is in the way + if (Hit.IsValidBlockingHit()) + { + return Result; + } + } + + // Can vault + Result.bSuccess = true; + Result.Location = GroundLoc; + Result.Direction = -FVector::VectorPlaneProject(NotWalkableHit.ImpactNormal, Up); + Result.Height = (GroundLoc - Loc).ProjectOnTo(Up).Size(); + + //DrawDebugSphere(Pawn->GetWorld(), GroundLoc, 32.f, 16, FColor::White, true); + //DrawDebugDirectionalArrow(Pawn->GetWorld(), GroundLoc, GroundLoc + Result.Direction * 200.f, 40.f, FColor::Green, true, -1.f, 0, 2.f); + + return Result; +} + +FCollisionQueryParams VIConfigureCollisionParams(FName TraceTag, bool bTraceComplex, const TArray& ActorsToIgnore, bool bIgnoreSelf, const UObject* WorldContextObject) +{ + FCollisionQueryParams Params(TraceTag, SCENE_QUERY_STAT_ONLY(KismetTraceUtils), bTraceComplex); + Params.bReturnPhysicalMaterial = true; + Params.bReturnFaceIndex = !UPhysicsSettings::Get()->bSuppressFaceRemapTable; // Ask for face index, as long as we didn't disable globally + Params.AddIgnoredActors(ActorsToIgnore); + if (bIgnoreSelf) + { + const AActor* IgnoreActor = Cast(WorldContextObject); + if (IgnoreActor) + { + Params.AddIgnoredActor(IgnoreActor); + } + else + { + // find owner + const UObject* CurrentObject = WorldContextObject; + while (CurrentObject) + { + CurrentObject = CurrentObject->GetOuter(); + IgnoreActor = Cast(CurrentObject); + if (IgnoreActor) + { + Params.AddIgnoredActor(IgnoreActor); + break; + } + } + } + } + + return Params; +} + +FCollisionObjectQueryParams VIConfigureCollisionObjectParams(const TArray >& ObjectTypes) +{ + TArray> CollisionObjectTraces; + CollisionObjectTraces.AddUninitialized(ObjectTypes.Num()); + + for (auto Iter = ObjectTypes.CreateConstIterator(); Iter; ++Iter) + { + CollisionObjectTraces[Iter.GetIndex()] = UEngineTypes::ConvertToCollisionChannel(*Iter); + } + + FCollisionObjectQueryParams ObjectParams; + for (auto Iter = CollisionObjectTraces.CreateConstIterator(); Iter; ++Iter) + { + const ECollisionChannel& Channel = (*Iter); + if (FCollisionObjectQueryParams::IsValidObjectQuery(Channel)) + { + ObjectParams.AddObjectTypesToQuery(Channel); + } + else + { + UE_LOG(LogBlueprintUserMessages, Warning, TEXT("%d isn't valid object type"), (int32)Channel); + } + } + + return ObjectParams; +} + +void UVIBlueprintFunctionLibrary::CapsuleTraceSingleForObjects(const UObject* WorldContextObject, const FVector& Start, const FVector& End, const FQuat& Rot, float Radius, float HalfHeight, const TArray >& ObjectTypes, bool bTraceComplex, const TArray& ActorsToIgnore, bool bDrawDebug, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor /*= FLinearColor::Red*/, FLinearColor TraceHitColor /*= FLinearColor::Green*/, float DrawTime /*= 5.0f*/) +{ + static const FName CapsuleTraceSingleName(TEXT("CapsuleTraceSingleForObjects")); + const FCollisionQueryParams Params = VIConfigureCollisionParams(CapsuleTraceSingleName, bTraceComplex, ActorsToIgnore, bIgnoreSelf, WorldContextObject); + + const FCollisionObjectQueryParams ObjectParams = VIConfigureCollisionObjectParams(ObjectTypes); + if (ObjectParams.IsValid() == false) + { + UE_LOG(LogBlueprintUserMessages, Warning, TEXT("Invalid object types")); + return; + } + + const UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); + bool const bHit = World ? World->SweepSingleByObjectType(OutHit, Start, End, Rot, ObjectParams, FCollisionShape::MakeCapsule(Radius, HalfHeight), Params) : false; + +#if ENABLE_DRAW_DEBUG + DrawDebugCapsuleTraceSingle(World, Start, End, Rot, Radius, HalfHeight, bDrawDebug, bHit, OutHit, TraceColor, TraceHitColor, DrawTime); +#endif +} + +void UVIBlueprintFunctionLibrary::DrawDebugCapsuleTraceSingle(const UWorld* World, const FVector& Start, const FVector& End, const FQuat& Rot, float Radius, float HalfHeight, bool bDrawDebug, bool bHit, const FHitResult& OutHit, FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime) +{ + if (bDrawDebug) + { + constexpr bool bPersistent = false; + const float LifeTime = DrawTime; + + if (bHit && OutHit.bBlockingHit) + { + // Red up to the blocking hit, green thereafter + DrawDebugCapsule(World, Start, HalfHeight, Radius, Rot, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugCapsule(World, OutHit.Location, HalfHeight, Radius, Rot, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugLine(World, Start, OutHit.Location, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugPoint(World, OutHit.ImpactPoint, 16.f, TraceColor.ToFColor(true), bPersistent, LifeTime); + + DrawDebugCapsule(World, End, HalfHeight, Radius, Rot, TraceHitColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugLine(World, OutHit.Location, End, TraceHitColor.ToFColor(true), bPersistent, LifeTime); + } + else + { + // no hit means all red + DrawDebugCapsule(World, Start, HalfHeight, Radius, Rot, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugCapsule(World, End, HalfHeight, Radius, Rot, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugLine(World, Start, End, TraceColor.ToFColor(true), bPersistent, LifeTime); + } + } +} + +float UVIBlueprintFunctionLibrary::ComputeDirectionToFloat(const FVector& Vector, const FVector& Dir) +{ + const FVector VectorProject = Vector.ProjectOnTo(Dir); + const float SignDirection = FMath::Sign(VectorProject | Dir); + + return VectorProject.Size() * SignDirection; +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VITypes.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VITypes.cpp new file mode 100644 index 00000000..5085bd0d --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VITypes.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "VITypes.h" + +bool FVIGameplayAbilityTargetData_VaultInfo::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) +{ + FVector_NetQuantize10 VaultLocation = FVector_NetQuantize10(VaultInfo.Location); + FVector_NetQuantize10 VaultDirection = FVector_NetQuantize10(VaultInfo.Direction); + VaultLocation.NetSerialize(Ar, Map, bOutSuccess); + VaultDirection.NetSerialize(Ar, Map, bOutSuccess); + + Ar << VaultInfo.Height; + Ar << VaultInfo.RandomSeed; + + if (Ar.IsLoading()) + { + VaultInfo.Location = VaultLocation; + VaultInfo.Direction = VaultDirection; + } + + bOutSuccess = true; + return true; +} + +bool FVIRepMotionMatch::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) +{ + FVector_NetQuantize10 NetLocation = FVector_NetQuantize10(Location); + FVector_NetQuantize10 NetDirection = FVector_NetQuantize10(Direction); + NetLocation.NetSerialize(Ar, Map, bOutSuccess); + NetDirection.NetSerialize(Ar, Map, bOutSuccess); + + if (Ar.IsLoading()) + { + Location = NetLocation; + Direction = NetDirection; + } + + bOutSuccess = true; + return true; +} diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VaultIt.cpp b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VaultIt.cpp new file mode 100644 index 00000000..b71359a9 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Private/VaultIt.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "VaultIt.h" + +#define LOCTEXT_NAMESPACE "FVaultItModule" + +void FVaultItModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FVaultItModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FVaultItModule, VaultIt) \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstance.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstance.h new file mode 100644 index 00000000..bda9db2e --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstance.h @@ -0,0 +1,99 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimInstance.h" +#include "VIAnimationInterface.h" +#include "VITypes.h" +#include "VIAnimInstance.generated.h" + +class AVICharacterBase; + +/** + * + */ +UCLASS() +class VAULTIT_API UVIAnimInstance : public UAnimInstance, public IVIAnimationInterface +{ + GENERATED_BODY() + +protected: + /** Name of Right FBIK Bone */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK) + FName RHandName; + + /** Name of Left FBIK Bone */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK) + FName LHandName; + + /** FBIK Settings and Data */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK) + TArray FBIK; + + /** Used by Anim Graph to determine right hand FBIK is used */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK, meta = (DisplayName = "R Hand")) + bool bRHand; + + /** Used by Anim Graph to determine left hand FBIK is used */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK, meta = (DisplayName = "L Hand")) + bool bLHand; + + /** Used by Anim Graph to determine both hand FBIK is used */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK) + bool bBothHand; + + /** Used by Anim Graph to determine right hand FBIK location */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK, meta = (DisplayName = "R Hand Loc")) + FVector RHandLoc; + + /** Used by Anim Graph to determine left hand FBIK location */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FBIK, meta = (DisplayName = "L Hand Loc")) + FVector LHandLoc; + + /** Is currently vaulting */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Vault) + bool bIsVaulting; + +protected: + UPROPERTY(BlueprintReadWrite, Category = References) + AVICharacterBase* Character; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = AnimGraph) + bool bIsJumping; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = AnimGraph) + bool bIsFalling; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = AnimGraph) + float Speed; + +public: + UVIAnimInstance() + : RHandName(TEXT("hand_r")) + , LHandName(TEXT("hand_l")) + , FBIK( { FVIBoneFBIKData(RHandName), FVIBoneFBIKData(LHandName) } ) + {} + + virtual void NativeInitializeAnimation() override; + virtual void NativeUpdateAnimation(float DeltaTime) override; + +protected: + virtual void OnStartVault(); + virtual void OnStopVault(); + + /** Called when starting vaulting */ + UFUNCTION(BlueprintImplementableEvent, Category = Vault, meta = (DisplayName = "On Start Vault")) + void K2_OnStartVault(); + + /** Called when stopping vaulting */ + UFUNCTION(BlueprintImplementableEvent, Category = Vault, meta = (DisplayName = "On Stop Vault")) + void K2_OnStopVault(); + +public: + /** + * Called by anim notify when a bone has its location updated for FBIK + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void SetBoneFBIK(const FName& BoneName, const FVector& BoneLocation, bool bEnabled); +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstanceFP.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstanceFP.h new file mode 100644 index 00000000..1352f328 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimInstanceFP.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/VIAnimInstance.h" +#include "VIAnimInstanceFP.generated.h" + +/** + * + */ +UCLASS() +class VAULTIT_API UVIAnimInstanceFP : public UVIAnimInstance +{ + GENERATED_BODY() + +public: + /** + * Key: ThirdPersonMontage + * Value: FirstPersonMontage that is played corresponding to the ThirdPersonMontage + */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Vault) + TMap MontageLinkMap; + +protected: + UPROPERTY() + UAnimMontage* MontageToPlay; + + UPROPERTY() + float VaultBlendOutTime; + +protected: + bool CanPlayFPMontage() const; + +public: + virtual void OnStartVault() override; + virtual void OnStopVault() override; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimNotifyState_FBIK.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimNotifyState_FBIK.h new file mode 100644 index 00000000..11b75d58 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimNotifyState_FBIK.h @@ -0,0 +1,150 @@ +// Copyright (c) 2019-2021 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimNotifies/AnimNotifyState.h" +#include "VIAnimNotifyState_FBIK.generated.h" + +class USkeletalMeshComponent; +class UAnimSequenceBase; +class APawn; + +UENUM(BlueprintType) +enum class EVIFBIKUpdateType : uint8 +{ + FUT_Single UMETA(DisplayName = "Single Update", ToolTip = "Updates once at the start of the notify, and is disabled at the end of the notify"), + FUT_Tick UMETA(DisplayName = "Continuous Update", ToolTip = "Updates on tick since the start of the notify, and is disabled at the end of the notify - be sure to LOD, this can be expensive"), +}; + +UENUM(BlueprintType) +enum class EVIFBIKUpdateRole : uint8 +{ + FUR_All UMETA(DisplayName = "All", ToolTip = "Applies to all roles"), + FUR_LocalOnly UMETA(DisplayName = "Local Player Only", ToolTip = "Will only apply to the local player"), + FUR_SimulatedOnly UMETA(DisplayName = "Simulated Only", ToolTip = "Will only apply to simulated players"), +}; + +UENUM(BlueprintType) +enum class EVIFBIKTraceType : uint8 +{ + FTT_Simple UMETA(DisplayName = "Collision", ToolTip = "Use the object's collision to trace against for bone placement"), + FTT_Complex UMETA(DisplayName = "Geometry", ToolTip = "Use the object's geometry to trace against for very accurate bone placement - can be a lot more expensive"), + FTT_ComplexLocalOnly UMETA(DisplayName = "Geometry Local Player Only", ToolTip = "Use the object's geometry to trace against for the local player only, otherwise use the object's collision"), +}; + +/** AnimNotifyState used to setup FBIK for a bone during a window in the animation */ +UCLASS(meta = (DisplayName = "VI FBIK")) +class VAULTIT_API UVIAnimNotifyState_FBIK : public UAnimNotifyState +{ + GENERATED_BODY() + +public: + /** + * The bone that is being planted onto a surface + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + FName BoneName; + + /** + * How far ahead of the bone (BoneName) to trace + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + float TraceLength; + + /** + * Radius of sphere trace + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + float TraceRadius; + + /** + * Offset along the trace direction to provide width for the hand so it doesn't penetrate the surface + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + float BoneOffset; + + /** + * If there is no hit, will disable the FBIK for this bone + * Default behaviour simply uses the last successful location + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + bool bDisableIfHitFails; + + /** + * What type of collision to trace against + * Collision will use the collision mesh on the object which is a lot cheaper + * Geometry will use a complex trace which uses the object's geometry which can be expensive + * Geometry Local Only will use a complex trace only for the local player which can be a good compromise for accuracy where it matters most and cost-cutting elsewhere + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + EVIFBIKTraceType TraceType; + + /** + * Whether to update at the start of the notify and never again or on tick + * If updating on tick be extra careful to LOD this (as well as the control rig anim graph nodes) so it doesn't waste resources + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + EVIFBIKUpdateType UpdateType; + + /** + * Will skip this many ticks if UpdateType is continuous to save on performance costs + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify, meta = (EditCondition = "UpdateType == EVIFBIKUpdateType::FUT_Tick")) + uint8 TickSkipAmount; + + /** + * Which network roles to apply the FBIK to + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + EVIFBIKUpdateRole ApplyToRoles; + + /** + * If true, will draw the trace during PIE + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimNotify) + bool bDebugTraceDuringPIE; + +protected: + /** When notify begins, caches bone offset lateral (the side of) to the owner */ + float LateralOffset; + + uint8 SkippedTicks; + +public: + UVIAnimNotifyState_FBIK() + : BoneName(TEXT("hand_r")) + , TraceLength(40.f) + , TraceRadius(20.f) + , BoneOffset(10.f) + , bDisableIfHitFails(false) + , TraceType(EVIFBIKTraceType::FTT_ComplexLocalOnly) + , UpdateType(EVIFBIKUpdateType::FUT_Tick) + , TickSkipAmount(3) + , ApplyToRoles(EVIFBIKUpdateRole::FUR_All) + , bDebugTraceDuringPIE(false) + , LateralOffset(0.f) + , SkippedTicks(0) + { +#if WITH_EDITOR + NotifyColor = FColor::Cyan; +#endif // WITH_EDITOR + } + +#if WITH_EDITOR + virtual void OnAnimNotifyCreatedInEditor(FAnimNotifyEvent& ContainingAnimNotifyEvent) override final; +#endif // WITH_EDITOR + + virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override final; + virtual void NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime) override final; + virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override final; + +protected: + void UpdateFBIK(USkeletalMeshComponent* MeshComp); + + static APawn* GetPawnOwner(const USkeletalMeshComponent* const MeshComp); + + bool HasValidRole(const APawn* const PawnOwner) const; + + static bool IsLocalPlayer(const APawn* const PawnOwner); +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimationInterface.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimationInterface.h new file mode 100644 index 00000000..1a8b0b2c --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Animation/VIAnimationInterface.h @@ -0,0 +1,27 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "VIAnimationInterface.generated.h" + +/** + * Used by AnimInstance by notify to update FBIK + */ +UINTERFACE(BlueprintType) +class VAULTIT_API UVIAnimationInterface : public UInterface +{ + GENERATED_BODY() +}; + +class VAULTIT_API IVIAnimationInterface +{ + GENERATED_BODY() + +public: + /** + * Called by anim notify when a bone has its location updated for FBIK + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void SetBoneFBIK(const FName& BoneName, const FVector& BoneLocation, bool bEnabled); +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/GameplayAbility_Vault.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/GameplayAbility_Vault.h new file mode 100644 index 00000000..a3b6d857 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/GameplayAbility_Vault.h @@ -0,0 +1,19 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "VIGameplayAbility.h" +#include "GameplayAbility_Vault.generated.h" + +/** + * + */ +UCLASS() +class VAULTIT_API UGameplayAbility_Vault : public UVIGameplayAbility +{ + GENERATED_BODY() + +public: + UGameplayAbility_Vault(); +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/PlayMontageForMeshAndWait.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/PlayMontageForMeshAndWait.h new file mode 100644 index 00000000..2dc393a5 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/PlayMontageForMeshAndWait.h @@ -0,0 +1,130 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +// Based on GASShooter by Dan Kestranek + +#pragma once + +#include "CoreMinimal.h" +#include "Abilities/Tasks/AbilityTask.h" +#include "PlayMontageForMeshAndWait.generated.h" + +class UVIAbilitySystemComponent; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FVIPlayMontageForMeshAndWaitDelegate); +DECLARE_DELEGATE_TwoParams( FOnMontageBlendingOutStarted, class UAnimMontage*, bool /*bInterrupted*/) +DECLARE_DELEGATE_TwoParams( FOnMontageEnded, class UAnimMontage*, bool /*bInterrupted*/) + +/** + * This task combines PlayMontageAndWait and WaitForEvent into one task, so you can wait for multiple types of activations such as from a melee combo + * Much of this code is copied from one of those two ability tasks + */ +UCLASS() +class VAULTIT_API UPlayMontageForMeshAndWait : public UAbilityTask +{ + GENERATED_BODY() + +public: + /** The montage completely finished playing */ + UPROPERTY(BlueprintAssignable) + FVIPlayMontageForMeshAndWaitDelegate OnCompleted; + + /** The montage started blending out */ + UPROPERTY(BlueprintAssignable) + FVIPlayMontageForMeshAndWaitDelegate OnBlendOut; + + /** The montage was interrupted */ + UPROPERTY(BlueprintAssignable) + FVIPlayMontageForMeshAndWaitDelegate OnInterrupted; + + /** The ability task was explicitly cancelled by another ability */ + UPROPERTY(BlueprintAssignable) + FVIPlayMontageForMeshAndWaitDelegate OnCancelled; + +public: + UPlayMontageForMeshAndWait(const FObjectInitializer& OI); + + /** + * Play a montage and wait for it end. If a gameplay event happens that matches EventTags (or EventTags is empty), the EventReceived delegate will fire with a tag and event data. + * If StopWhenAbilityEnds is true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled. + * On normal execution, OnBlendOut is called when the montage is blending out, and OnCompleted when it is completely done playing + * OnInterrupted is called if another montage overwrites this, and OnCancelled is called if the ability or task is cancelled + * + * @param TaskInstanceName Set to override the name of this task, for later querying + * @param MontageToPlay The montage to play on the character + * @param Rate Change to play the montage faster or slower + * @param bStopWhenAbilityEnds If true, this montage will be aborted if the ability ends normally. It is always stopped when the ability is explicitly cancelled + * @param AnimRootMotionTranslationScale Change to modify size of root motion or set to 0 to block it entirely + */ + UFUNCTION(BlueprintCallable, Category = "Ability|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE")) + static UPlayMontageForMeshAndWait* PlayMontageForMeshAndWait( + UGameplayAbility* OwningAbility, + FName TaskInstanceName, + USkeletalMeshComponent* Mesh, + UAnimMontage* MontageToPlay, + float Rate = 1.f, + FName StartSection = NAME_None, + bool bStopWhenAbilityEnds = true, + float AnimRootMotionTranslationScale = 1.f, + bool bReplicateMontage = true, + float OverrideBlendOutTimeForCancelAbility = -1.f, + float OverrideBlendOutTimeForStopWhenEndAbility = -1.f + ); + + /** + * The Blueprint node for this task, PlayMontageAndWaitForEvent, has some black magic from the plugin that automagically calls Activate() + * inside of K2Node_LatentAbilityCall as stated in the AbilityTask.h. Ability logic written in C++ probably needs to call Activate() itself manually. + */ + virtual void Activate() override; + virtual void ExternalCancel() override; + virtual void OnDestroy(bool AbilityEnded) override; + virtual FString GetDebugString() const override; + +private: + /** Mesh that the Montage is playing on. Must be owned by the AvatarActor. */ + UPROPERTY() + USkeletalMeshComponent* Mesh; + + /** Montage that is playing */ + UPROPERTY() + UAnimMontage* MontageToPlay; + + /** Playback rate */ + UPROPERTY() + float Rate; + + /** Section to start montage from */ + UPROPERTY() + FName StartSection; + + /** Modifies how root motion movement to apply */ + UPROPERTY() + float AnimRootMotionTranslationScale; + + /** Rather montage should be aborted if ability ends */ + UPROPERTY() + bool bStopWhenAbilityEnds; + + UPROPERTY() + bool bReplicateMontage; + + UPROPERTY() + float OverrideBlendOutTimeForCancelAbility; + + UPROPERTY() + float OverrideBlendOutTimeForStopWhenEndAbility; + +private: + /** Checks if the ability is playing a montage and stops that montage, returns true if a montage was stopped, false if not. */ + bool StopPlayingMontage(float OverrideBlendOutTime = -1.f); + + /** Returns our ability system component */ + UVIAbilitySystemComponent* GetTargetASC() const; + + void OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted); + void OnAbilityCancelled(); + void OnMontageEnded(UAnimMontage* Montage, bool bInterrupted); + + FOnMontageBlendingOutStarted BlendingOutDelegate; + FOnMontageEnded MontageEndedDelegate; + FDelegateHandle CancelledHandle; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAbilitySystemComponent.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAbilitySystemComponent.h new file mode 100644 index 00000000..5126bebe --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAbilitySystemComponent.h @@ -0,0 +1,143 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "AbilitySystemComponent.h" +#include "VIAbilitySystemComponent.generated.h" + +class USkeletalMeshComponent; + +/** + * Data about montages that were played locally (all montages in case of server. Predictive montages in case of client). Never replicated directly. + */ +USTRUCT() +struct VAULTIT_API FVIGameplayAbilityLocalAnimMontageForMesh +{ + GENERATED_BODY(); + +public: + UPROPERTY() + USkeletalMeshComponent* Mesh; + + UPROPERTY() + FGameplayAbilityLocalAnimMontage LocalMontageInfo; + + FVIGameplayAbilityLocalAnimMontageForMesh() + : Mesh(nullptr) + { + } + + FVIGameplayAbilityLocalAnimMontageForMesh(USkeletalMeshComponent* InMesh) + : Mesh(InMesh) + { + } + + FVIGameplayAbilityLocalAnimMontageForMesh(USkeletalMeshComponent* InMesh, FGameplayAbilityLocalAnimMontage& InLocalMontageInfo) + : Mesh(InMesh) + , LocalMontageInfo(InLocalMontageInfo) + { + } +}; + +/** +* Data about montages that is replicated to simulated clients. +* Based on GASShooter by Dan Kestranek +*/ +USTRUCT() +struct VAULTIT_API FVIGameplayAbilityRepAnimMontageForMesh +{ + GENERATED_BODY(); + +public: + UPROPERTY() + USkeletalMeshComponent* Mesh; + + UPROPERTY() + FGameplayAbilityRepAnimMontage RepMontageInfo; + + FVIGameplayAbilityRepAnimMontageForMesh() + : Mesh(nullptr) + { + } + + FVIGameplayAbilityRepAnimMontageForMesh(USkeletalMeshComponent* InMesh) + : Mesh(InMesh) + { + } +}; + +/** + * + */ +UCLASS() +class VAULTIT_API UVIAbilitySystemComponent : public UAbilitySystemComponent +{ + GENERATED_BODY() + +protected: + /** + * Montages instigated locally + * All if server, predicted if client, replicated if simulated + */ + UPROPERTY() + TArray LocalAnimMontageInfoForMeshes; + + /** + * Replicates montage info to simulated clients + */ + UPROPERTY(ReplicatedUsing = OnRep_ReplicatedAnimMontageForMesh) + TArray RepAnimMontageInfoForMeshes; + +public: + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + + virtual bool GetShouldTick() const override; + + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override; + + virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) override; + + virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) override; + + /** Plays montage, handles replication and prediction based on passed in ability or activation info */ + virtual float PlayMontageForMesh(UGameplayAbility* AnimatingAbility, class USkeletalMeshComponent* InMesh, FGameplayAbilityActivationInfo ActivationInfo, UAnimMontage* Montage, float InPlayRate, FName StartSectionName = NAME_None, bool bReplicateMontage = true); + + /** Plays a montage without updating replication or prediction structures. Used by simulated proxies when replication tells them to play a montage. */ + virtual float PlayMontageSimulatedForMesh(USkeletalMeshComponent* InMesh, UAnimMontage* Montage, float InPlayRate, FName StartSectionName = NAME_None); + + /** Returns the current animating ability */ + UGameplayAbility* GetAnimatingAbilityFromAnyMesh(); + + // Returns the montage that is playing for the mesh + UAnimMontage* GetCurrentMontageForMesh(USkeletalMeshComponent* InMesh); + + /** Stops whatever montage is currently playing. Expectation is caller should only be stopping it if they are the current animating ability (or have good reason not to check) */ + virtual void CurrentMontageStopForMesh(USkeletalMeshComponent* InMesh, float OverrideBlendOutTime = -1.0f); + + /** Clear the animating ability that is passed in, if it's still currently animating */ + virtual void ClearAnimatingAbilityForAllMeshes(UGameplayAbility* Ability); + +protected: + /** Copy LocalAnimMontageInfo into RepAnimMontageInfo */ + void AnimMontage_UpdateReplicatedDataForMesh(USkeletalMeshComponent* InMesh); + void AnimMontage_UpdateReplicatedDataForMesh(FVIGameplayAbilityRepAnimMontageForMesh& OutRepAnimMontageInfo); + + UFUNCTION() + virtual void OnRep_ReplicatedAnimMontageForMesh(); + + /** Finds existing FGameplayAbilityLocalAnimMontageForMesh for the mesh or creates one if it doesn't exist */ + FVIGameplayAbilityLocalAnimMontageForMesh& GetLocalAnimMontageInfoForMesh(USkeletalMeshComponent* InMesh); + + /** Finds existing FGameplayAbilityRepAnimMontageForMesh for the mesh or creates one if it doesn't exist */ + FVIGameplayAbilityRepAnimMontageForMesh& GetGameplayAbilityRepAnimMontageForMesh(USkeletalMeshComponent* InMesh); + + /** Called when a prediction key that played a montage is rejected */ + void OnPredictiveMontageRejectedForMesh(USkeletalMeshComponent* InMesh, UAnimMontage* PredictiveMontage); + + /** + * @return True if we are ready to handle replicated montage information + * Not used by default, may be overridden + */ + virtual bool IsReadyForReplicatedMontageForMesh() { return true; } +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAssetManager.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAssetManager.h new file mode 100644 index 00000000..5e3cd20b --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIAssetManager.h @@ -0,0 +1,19 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/AssetManager.h" +#include "VIAssetManager.generated.h" + +/** + * + */ +UCLASS() +class VAULTIT_API UVIAssetManager : public UAssetManager +{ + GENERATED_BODY() + +public: + virtual void StartInitialLoading() override; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIGameplayAbility.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIGameplayAbility.h new file mode 100644 index 00000000..089ee0c1 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/GAS/VIGameplayAbility.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Abilities/GameplayAbility.h" +#include "VIGameplayAbility.generated.h" + +class USkeletalMeshComponent; +class UAnimMontage; + +USTRUCT() +struct VAULTIT_API FVIAbilityMeshMontage +{ + GENERATED_BODY() + +public: + UPROPERTY() + USkeletalMeshComponent* Mesh; + + UPROPERTY() + UAnimMontage* Montage; + + FVIAbilityMeshMontage() + : Mesh(nullptr) + , Montage(nullptr) + {} + + FVIAbilityMeshMontage(USkeletalMeshComponent* InMesh, UAnimMontage* InMontage) + : Mesh(InMesh) + , Montage(InMontage) + {} +}; + +/** + * + */ +UCLASS() +class VAULTIT_API UVIGameplayAbility : public UGameplayAbility +{ + GENERATED_BODY() + +protected: + /** Active montages being played by this ability */ + UPROPERTY() + TArray CurrentAbilityMeshMontages; + +public: +#if WITH_EDITOR + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR + + /** Call to set the current montage from a montage task. Set to allow hooking up montage events to ability events */ + virtual void SetCurrentMontageForMesh(USkeletalMeshComponent* InMesh, UAnimMontage* InCurrentMontage); + +protected: + bool FindAbillityMeshMontage(const USkeletalMeshComponent* InMesh, FVIAbilityMeshMontage& InAbilityMontage); + +public: + /** Returns the current prediction key and if it's valid for more prediction. Used for debugging ability prediction windows */ + UFUNCTION(BlueprintPure, Category = "Ability") + virtual FString GetCurrentPredictionKeyStatus() const; + + /** Returns the current prediction key and if it's valid for more prediction */ + UFUNCTION(BlueprintPure, Category = "Ability") + virtual bool IsPredictionKeyValidForMorePrediction() const; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacter.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacter.h new file mode 100644 index 00000000..1b9d70d2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacter.h @@ -0,0 +1,41 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "VICharacterAbilityBase.h" +#include "VICharacter.generated.h" + +class AController; + + +/** + * This is a class that is fully setup for vaulting + * VIPawnComponent needs additional setup as it has per-project settings (GameplayTags) + * VaultTraceSettings also needs additional setup as it has per-project settings (Collision Profile) + */ +UCLASS() +class VAULTIT_API AVICharacter : public AVICharacterAbilityBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditDefaultsOnly, Category = Vault) + FVIAnimSet VaultAnimSet; + + UPROPERTY(EditDefaultsOnly, Category = Vault) + FVITraceSettings VaultTraceSettings; + +public: + AVICharacter(const FObjectInitializer& OI); + + virtual void BeginPlay() override; + virtual void PossessedBy(AController* NewController) override; + virtual void OnRep_Controller() override; + + virtual UVIPawnVaultComponent* GetPawnVaultComponent_Implementation() const override { return VaultComponent; } + virtual UMotionWarpingComponent* GetMotionWarpingComponent_Implementation() const override { return MotionWarpingComponent; } + + virtual FVIAnimSet GetVaultAnimSet_Implementation() const override { return VaultAnimSet; } + virtual FVITraceSettings GetVaultTraceSettings_Implementation() const override { return VaultTraceSettings; } +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterAbilityBase.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterAbilityBase.h new file mode 100644 index 00000000..eacfbdad --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterAbilityBase.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Pawn/VICharacterBase.h" +#include "AbilitySystemInterface.h" +#include "VICharacterAbilityBase.generated.h" + +class UVIAbilitySystemComponent; + +/** + * Adds a UVIAbilitySystemComponent to your character base + * Allows switching replication mode in Blueprint for the sake of AI using Minimal instead of Mixed + */ +UCLASS() +class VAULTIT_API AVICharacterAbilityBase : public AVICharacterBase, public IAbilitySystemInterface +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadOnly, Category = Abilities) + UVIAbilitySystemComponent* AbilitySystem; + +protected: + /** + * Used by blueprints to allow changing replication mode which is usually + * only accessible via C++ + * + * Recommended as follows: + * For player characters use Mixed + * For AI characters use Minimal + */ + UPROPERTY(EditDefaultsOnly, Category = Abilities) + EVIGameplayEffectReplicationMode AbilitySystemReplicationMode; + +public: + AVICharacterAbilityBase(const FObjectInitializer& OI); + +#if WITH_EDITOR + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR + +protected: + // *********************************************** // + // ******** Begin IAbilitySystemInterface ******** // + // *********************************************** // + + virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; + + // *********************************************** // + // ********* End IAbilitySystemInterface ********* // + // *********************************************** // +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterBase.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterBase.h new file mode 100644 index 00000000..c9dc3a43 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VICharacterBase.h @@ -0,0 +1,118 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Character.h" +#include "VITypes.h" +#include "VIPawnInterface.h" +#include "VICharacterBase.generated.h" + +class UMotionWarpingComponent; +class UVIPawnVaultComponent; + +/** + * An incomplete character base class + * Needs to inherit from IAbilitySystemInterface and implement a UVIAbilitySystemComponent + * @see: AVICharacterAbilityBase where this is done for you + * + * Requires multiple overrides which will cause errors if not correctly performed + */ +UCLASS(abstract) +class VAULTIT_API AVICharacterBase : public ACharacter, public IVIPawnInterface +{ + GENERATED_BODY() + +public: + /** + * Motion Warping Component used for vaulting + */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character) + UMotionWarpingComponent* MotionWarpingComponent; + + /** + * Pawn Vault Component used for core vaulting logic + * + * This is added in Blueprint and must be returned via + * the IVIPawnInterface::GetPawnVaultComponent function + */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character) + UVIPawnVaultComponent* VaultComponent; + +protected: + /** Simulated proxies use this to update their vaulting state based on server values */ + UPROPERTY(Replicated, BlueprintReadWrite, Category = Vault) + bool bRepIsVaulting; + + /** Used to detect changes in vaulting state and call StopVaultAbility() */ + UPROPERTY() + bool bWasVaulting; + + /** + * Simulated proxies use this to reproduce motion matching results provided + * by server in the GA_Vault gameplay ability + * + * Local players use this as a cache for FBIK testing (returned via GetVaultLocationAndDirection) + * + * Net Serialized to one decimal point of precision + */ + UPROPERTY(ReplicatedUsing="OnRep_MotionMatch", BlueprintReadWrite, Category = Vault) + FVIRepMotionMatch RepMotionMatch; + +public: + virtual void BeginPlay() override; + + virtual void CheckJumpInput(float DeltaTime) override; + + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + +public: + virtual void Jump() override; + virtual void StopJumping() override; + + /** Called from gameplay ability when vault stops */ + UFUNCTION(BlueprintCallable, Category = Vault) + void StopVaultAbility(); + + UFUNCTION(BlueprintImplementableEvent, Category = Vault) + void OnStopVaultAbility(); + +protected: + UFUNCTION() + void OnRep_MotionMatch(); + +public: + /** + * @return True if vaulting + * Correct value must be returned based on net role here + * Simulated proxies return bRepIsVaulting + * Server & Authority must return CMC bIsVaulting + */ + UFUNCTION(BlueprintPure, Category = Vault) + virtual bool IsVaulting() const; + + // *********************************************** // + // *********** Begin IVIPawnInterface ************ // + // *********************************************** // + + // Read VIPawnInterface.h for detailed descriptions of these functions or look + // inside their functions themselves + + virtual UVIPawnVaultComponent* GetPawnVaultComponent_Implementation() const override; + virtual UMotionWarpingComponent* GetMotionWarpingComponent_Implementation() const override; + virtual USkeletalMeshComponent* GetMeshForVaultMontage_Implementation() const override { return GetMesh(); } + virtual FVIAnimSet GetVaultAnimSet_Implementation() const override; + virtual FVITraceSettings GetVaultTraceSettings_Implementation() const override; + virtual FVector GetVaultDirection_Implementation() const override; + virtual bool CanVault_Implementation() const override; + virtual void StartVaultAbility_Implementation() override; + virtual void OnLocalPlayerVault_Implementation(const FVector& Location, const FVector& Direction) override; + virtual void GetVaultLocationAndDirection_Implementation(FVector& OutLocation, FVector& OutDirection) const override; + virtual void ReplicateMotionMatch_Implementation(const FVIRepMotionMatch& MotionMatch) override; + virtual bool IsWalkable_Implementation(const FHitResult& HitResult) const override; + virtual bool CanAutoVaultInCustomMovementMode_Implementation() const override; + + // *********************************************** // + // ************* End IVIPawnInterface ************ // + // *********************************************** // +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnInterface.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnInterface.h new file mode 100644 index 00000000..9fc9a21f --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnInterface.h @@ -0,0 +1,148 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "VITypes.h" +#include "VIPawnInterface.generated.h" + +class UMotionWarpingComponent; +class UVIPawnVaultComponent; + +UINTERFACE(BlueprintType) +class VAULTIT_API UVIPawnInterface : public UInterface +{ + GENERATED_BODY() +}; + +class VAULTIT_API IVIPawnInterface +{ + GENERATED_BODY() + +public: + /** + * VIPawnVaultComponent should either be derived as a blueprint or an + * existing blueprint should be copied and added to your pawn or character + * and returned by this event + * + * Ideally it should also be set and cached on the character for cheaper access + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + UVIPawnVaultComponent* GetPawnVaultComponent() const; + + /** + * Simply return the motion warping component that has been added to + * your pawn or character + * + * Ideally it should also be set and cached on the character for cheaper access + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + UMotionWarpingComponent* GetMotionWarpingComponent() const; + + /** + * This is the mesh that players the vaulting montage + * In most cases it is the character's mesh but it can be whichever + * is capable of driving root motion + * + * If you have first and third person, it is advisable to use third + * person mesh and have the first person animation line up both + * cosmetically and in terms of length + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + USkeletalMeshComponent* GetMeshForVaultMontage() const; + + /** + * Provide animation set used when vaulting + * You can use CharacterMovementComponent's movement mode such as + * Falling, Swimming, etc to provide different results + * + * The runtime change in vault animation must come through some form of + * prediction so the server and client are never out of sync + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + FVIAnimSet GetVaultAnimSet() const; + + /** + * Provide trace settings used when vaulting + * You can use CharacterMovementComponent's movement mode such as + * Falling, Swimming, etc to provide different results + * + * If you use a more extreme anti-cheat that requires the server to do + * full vaulting checks then the runtime change in trace settings must + * come through some form of prediction so the server and client are never + * out of sync + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + FVITraceSettings GetVaultTraceSettings() const; + + /** + * The vector used to determine the direction to vault in + * There is no need to normalize this vector; it will just end up being + * done twice + * + * The most responsive option is to use acceleration (input) if available + * and otherwise use the character's forward direction + * + * You could also use the velocity direction, however that could frustrate + * players because it may ignore or go against their intentions but + * should provide a much more realistic result + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + FVector GetVaultDirection() const; + + /** + * Logic to determine if we are currently able to vault + * UVIPawnVaultComponent::IsVaulting() should be compatible with GAS + * as it is based on state gameplay tags + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + bool CanVault() const; + + /** + * Called by GA_Vault when the vault ability starts + * For characters, they need to have their movement components placed + * into flying mode for root motion to work on Z axis (a natural requirement + * for vaulting) + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void StartVaultAbility(); + + /** + * Called when the local player starts vaulting to allow them to cache + * the Location and Direction for other purposes, in particular, FBIK + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void OnLocalPlayerVault(const FVector& Location, const FVector& Direction); + + /** + * Return the cached location and direction for vaulting + * On local player this is the data stored from OnLocalPlayerVault() + * On server and simulated proxy this is the data stored from ReplicateMotionMatch() + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void GetVaultLocationAndDirection(FVector& OutLocation, FVector& OutDirection) const; + + /** + * Called by GA_Vault to notify server that it should update a predicted + * property that simulated clients receive to set their motion matching + * sync point + * + * Net quantized to 1 decimal place + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + void ReplicateMotionMatch(const FVIRepMotionMatch& MotionMatch); + + /** + * Determine if the surface provided by the hit result is walkable or not + * For characters this is UCharacterMovementComponent::IsWalkable() + */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Vault) + bool IsWalkable(const FHitResult& HitResult) const; + + /** + * This is only used when AutoVaultSettings utilizes "Custom" and the Pawn is in custom movement mode + * This is where you check against the custom movement mode it is currently in to determine if it can auto vault in this mode + */ + UFUNCTION(BlueprintNativeEvent, Category = Vault) + bool CanAutoVaultInCustomMovementMode() const; +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnVaultComponent.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnVaultComponent.h new file mode 100644 index 00000000..0e440879 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/Pawn/VIPawnVaultComponent.h @@ -0,0 +1,423 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "GameplayTagContainer.h" +#include "VITypes.h" +#include "VIPawnVaultComponent.generated.h" + +class APawn; +class UGameplayAbility; +struct FPredictProjectilePathResult; + +UENUM(BlueprintType) +enum class EVIVaultInputRelease : uint8 +{ + VIR_Always UMETA(DisplayName = "Always", ToolTip = "Always release vault input after a single test; recommended setting for performance"), + VIR_OnSuccess UMETA(DisplayName = "On Success", ToolTip = "Always release vault input after successfully vaulting; can be expensive computationally"), + VIR_Never UMETA(DisplayName = "Never", ToolTip = "Never release vault input unless the player does so; can be expensive computationally"), +}; + +/** + * Used to set which movement modes auto vault works with + */ +UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) +enum class EVIAutoVault : uint8 +{ + VIAV_None = 0 UMETA(Hidden), + VIAV_Walking = 1 << 1 UMETA(DisplayName = "Walking"), + VIAV_Falling = 1 << 2 UMETA(DisplayName = "Falling"), + VIAV_Swimming = 1 << 3 UMETA(DisplayName = "Swimming"), + VIAV_Flying = 1 << 4 UMETA(DisplayName = "Flying"), + VIAV_Custom = 1 << 5 UMETA(DisplayName = "Custom", ToolTip = "Make sure to properly implement VIPawnInterface::CanAutoVaultInCustomMovementMode"), +}; +ENUM_CLASS_FLAGS(EVIAutoVault); + +UENUM(BlueprintType) +enum class EVIAntiCheatType : uint8 +{ + VIACT_None UMETA(DisplayName = "None", ToolTip = "No Anti-Cheat, always trust client. Suitable for co-op and non-competitive games"), + VIACT_Enabled UMETA(DisplayName = "Enabled", ToolTip = "Competitive anti-cheat, avoid using with high player count. Does full vault checks on server and offers tolerances within which to compare the client's data. If tolerances are too tight may de-sync the client and reject the vault"), + VIACT_Custom UMETA(DisplayName = "Custom", ToolTip = "Override ComputeCustomAntiCheat on the PawnVaultComponent to define behaviour that server verifies"), +}; + +/** + * Performs a full vault check to compare to client's (can get expensive) + * Should only be used in competitive environments without many players that vault + * This check is never needed for AI + */ +USTRUCT(BlueprintType) +struct FVIAntiCheatSettings +{ + GENERATED_BODY() + + FVIAntiCheatSettings() + : LocationErrorThreshold(10.f) + , DirectionErrorThreshold(0.2f) + , HeightErrorThreshold(4.f) + {} + + virtual ~FVIAntiCheatSettings() = default; + + /** + * How far apart in unreal units the client and server can be with their vault location + * This check doesn't work for vaulting over objects below half height + * Set to -1 to disable check + * + * Remember to allow for player collisions, packet loss, etc. that could prevent vaulting when not cheating + */ + UPROPERTY(EditDefaultsOnly, Category = "Vault|AntiCheat", meta = (ClampMin = "-1", UIMin = "-1")) + float LocationErrorThreshold; + + /** + * How far apart the client and server can be with their vault direction + * This check works in a range of -1 (opposite) to 1 (completely identical) + * Set to -1 to disable check + * + * Remember to allow for player collisions, packet loss, etc. that could prevent vaulting when not cheating + */ + UPROPERTY(EditDefaultsOnly, Category = "Vault|AntiCheat", meta = (ClampMin = "-1", UIMin = "-1")) + float DirectionErrorThreshold; + + /** + * How far apart in unreal units the client and server can be with their vault height + * Set to -1 to disable check + * + * Remember to allow for player collisions, packet loss, etc. that could prevent vaulting when not cheating + */ + UPROPERTY(EditDefaultsOnly, Category = "Vault|AntiCheat", meta = (ClampMin = "-1", UIMin = "-1")) + float HeightErrorThreshold; + + /** The results here are almost always identical due to prediction if network conditions are ideal (latency is OK, but packet loss or collisions can cause issues), low tolerances can be used */ + virtual bool ComputeAntiCheat(const FVIVaultInfo& ClientInfo, const FVIVaultInfo& ServerInfo, const APawn* const Pawn) const; +}; + +/** + * Responsible for all vaulting logic that doesn't belong in a character or pawn + * Owner must implement IVIPawnInterface + * Owner must override most functions in IVIPawnInterface + * Owner must implement IAbilitySystemInterface + * Owner must have an AbilitySystemComponent + * + * If owner is a pawn, you will need to create a blueprint and override GetCapsuleInfo() (read it's tooltip, it's important) + */ +UCLASS(Blueprintable, ClassGroup=(Custom), meta=(BlueprintSpawnableComponent, DisplayName = "PawnVaultComponent")) +class VAULTIT_API UVIPawnVaultComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + /** + * The GA_Vault GameplayAbility responsible for the action itself + */ + UPROPERTY(EditDefaultsOnly, Category = "Ability") + TSubclassOf VaultAbility; + + /** Tag used to initiate vaulting ability */ + UPROPERTY(EditDefaultsOnly, Category = "Ability") + FGameplayTag VaultAbilityTag; + + /** Tag used to check if vaulting is active */ + UPROPERTY(EditDefaultsOnly, Category = "Ability") + FGameplayTag VaultStateTag; + + /** Tag used to check if vaulting is active */ + UPROPERTY(EditDefaultsOnly, Category = "Ability") + FGameplayTag VaultRemovalTag; + + /** + * How to handle releasing of vault input + * Note that jump key will always auto release, this only applies to holding down vault key + * Allowing player to continue holding vault key as opposed to doing single checks each press + * can be computationally expensive as it does multiple traces every render tick + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + EVIVaultInputRelease AutoReleaseVaultInput; + + /** + * What to do when pressing the jump key + * Disable Vault from Jump: Jump key cannot vault + * Select Highest Point: Checks ahead to see if jump can reach as high as vault, and will vault if it can go higher + * Always Vault if Possible: Will always vault if it is possible to do so, otherwise jump + * Only Vault from Air: Only vault if in air and jump key wont otherwise do anything (eg. double jump) + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + EVIJumpKeyPriority JumpKeyPriority; + + /** + * If true, will trace geometry instead of collision mesh when looking for vaulting surface + * This is helpful if using a map that wasn't setup with collisions that work well with vaulting and you just want something usable + * But a production ready game should probably disable this + * + * This could result in vaulting onto something the character cannot actually move onto, as character movement does not use complex collision + */ + bool bVaultTraceComplex; + + /** + * If not using root motion animations using only motion warping may fail to reach the ledge, + * providing extra height will fix this but requires more blending + * + * When using root motion animations you should leave this at 0 + * + * Only works when vaulting onto a surface higher than us + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + float AdditionalVaultHeight; + + /** + * Used instead of AdditionalVaultHeight if falling + * If not using root motion animations using only motion warping may fail to reach the ledge, + * providing extra height will fix this but requires more blending + * + * When using root motion animations you should leave this at 0 + * + * Only works when vaulting onto a surface higher than us + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + float AdditionalVaultHeightFalling; + + /** + * Vault is allowed to occur when on ground + * The component itself does not use this setting, it is available for use by the owner when checking CanVault() + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + bool bCanVaultFromGround; + + /** + * Vault is allowed to occur when in falling state + * The component itself does not use this setting, it is available for use by the owner when checking CanVault() + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + bool bCanVaultFromFalling; + + /** + * Vault is allowed to occur when in swimming state + * The component itself does not use this setting, it is available for use by the owner when checking CanVault() + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + bool bCanVaultFromSwimming; + + /** + * WARNING: It is up to you to handle the logic for crouching, VaultIt does not support this by default + * Vault is allowed to occur when crouching + * The component itself does not use this setting, it is available for use by the owner when checking CanVault() + * Crouching has a different capsule size, you will either need to update CapsuleInfo or stop crouching when vaulting starts + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + bool bCanVaultFromCrouching; + + /** + * Optional auto vaulting + * This can be expensive as it is performed on tick (CheckVaultInput) + * However, it is not a full vault test, it only tests if something is in our way + * + * AI requires this to vault + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "EVIAutoVault"), Category = Vault) + uint8 AutoVaultStates; + + /** + * Used to skip AutoVault checks for AutoVaultFrameSkip amount of ticks + * If set to 0, will always check on tick + * If set to 1, will check every second tick + * If set to 2, will check every third tick + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Vault) + uint8 AutoVaultCheckSkip; + + /** + * Corrections from anti-cheat tend to be aggressive and cause rubber banding at most latencies, so be forgiving where possible + * Because vaulting is predicted there shouldn't be much deviation, so using slightly higher tolerances to allow for some deviation + * should suffice until you have packet loss, in which case de-syncing becomes quite normal + */ + UPROPERTY(EditDefaultsOnly, Category = "Vault|AntiCheat") + EVIAntiCheatType AntiCheatType; + +public: + /** + * Competitive anti-cheat. Does full vault checks and offers tolerances within which to compare the client's data. If tolerances are too tight may de-sync the client + * and reject the vault. + * Can get expensive with a lot of player characters, test cost with `stat VaultIt` + * Will always fail regardless if the ComputeVault result is false, consider using more lenient settings for the authority in VIPawnInterface::GetVaultTraceSettings() + * The results here are almost always identical due to prediction, low tolerances can be used, however collisions with other players may interfere + * + * Remember to allow for player collisions, packet loss, etc. that could prevent vaulting when not cheating + */ + UPROPERTY(EditDefaultsOnly, Category = "Vault|AntiCheat") + FVIAntiCheatSettings AntiCheatSettings; + +public: + UPROPERTY() + bool bVaultAbilityInitialized; + + /** When true, player wants to vault */ + UPROPERTY(BlueprintReadOnly, Category = Vault) + bool bPressedVault; + + UPROPERTY(BlueprintReadWrite, Category = Vault) + FVICapsuleInfo CapsuleInfo; + +protected: + /** If true, last jump key resulted in vault press and releasing jump should release vault */ + bool bLastJumpInputVaulted; + + uint8 AutoVaultSkippedTicks; + + /** Was used in determining if jump key should vault or not; no need to do the check twice */ + FVIVaultResult PendingVaultResult; + + UPROPERTY(BlueprintReadOnly, Transient, DuplicateTransient, Category = Pawn) + APawn* PawnOwner; + +public: + UPROPERTY() + UAbilitySystemComponent* ASC; + +public: + UVIPawnVaultComponent() + : AutoReleaseVaultInput(EVIVaultInputRelease::VIR_Always) + , JumpKeyPriority(EVIJumpKeyPriority::JKP_SelectHighestPoint) + , bVaultTraceComplex(false) + , bCanVaultFromGround(true) + , bCanVaultFromFalling(true) + , bCanVaultFromSwimming(false) + , bCanVaultFromCrouching(false) + , AutoVaultStates(0) + , AutoVaultCheckSkip(8) + , AntiCheatType(EVIAntiCheatType::VIACT_None) + , bVaultAbilityInitialized(false) + , bPressedVault(false) + , bLastJumpInputVaulted(false) + , AutoVaultSkippedTicks(0) + , PendingVaultResult(FVIVaultResult()) + { + PrimaryComponentTick.bStartWithTickEnabled = false; + PrimaryComponentTick.bCanEverTick = false; + } + + virtual void BeginPlay() override; + + /** + * Call this from the Jump function override if using character + * + * You only need to supply the parameters that are used by your choice of JumpKeyPriority + * @param GravityZ: Used by "Select Highest Point" + * @param bCanJump: Used by "Only Vault from Air" + * @param bIsFalling: Used by "Only Vault from Air" + * @return True if jumping or false if vaulting + */ + UFUNCTION(BlueprintCallable, Category = Vault) + virtual bool Jump(float GravityZ = 0.f, bool bCanJump = false, bool bIsFalling = false); + + /** + * Call this from StopJumping function override if using character + */ + UFUNCTION(BlueprintCallable, Category = Vault) + virtual void StopJumping(); + + /** + * Call this when vault input is pressed + */ + UFUNCTION(BlueprintCallable, Category = Vault) + void Vault(); + + /** + * Call this when vault input is released + */ + UFUNCTION(BlueprintCallable, Category = Vault) + void StopVault(); + + /** + * For characters this is called on CheckJumpInput() (see AVICharacterBase for example) + * For pawns it would be called on tick + * @param MovementMode: Used to identify AutoVault capability; if this isn't being used nothing needs to be passed + */ + UFUNCTION(BlueprintCallable, Category = Vault) + virtual void CheckVaultInput(float DeltaTime, TEnumAsByte MovementMode = MOVE_Walking); + + /** + * @return True if vaulting + * Should be compatible with GAS as it uses gameplay tags for vaulting state + */ + UFUNCTION(BlueprintPure, Category = Vault) + virtual bool IsVaulting() const; + + /** + * Compute the actual result of a vault attempt and return all information as well as whether it succeeded or not + * Always check CanVault() or similar functionality to ensure character or pawn is in a state where they're allowed to vault + * This performs multiple traces so use `stat VaultIt` to ensure you're not using too much CPU by over-using it + */ + UFUNCTION(BlueprintPure, Category = Vault) + FVIVaultResult ComputeVault() const; + + /** + * Convert the data from ComputeVault into usable data + */ + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = Vault) + FVIVaultInfo ComputeVaultInfoFromResult(const FVIVaultInfo& VaultResult) const; + + /** + * @return True if the trace(s) allows us to vault + */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Vault) + bool ComputeShouldAutoVault(); + +protected: + /** + * This is called on BeginPlay() to cache the default values from the owner + * If owner is a Character then it will simply use the capsule sizes + * If owner is a Pawn then you must subclass and override this + * + * If overriding this in blueprint, you should first call IsCapsuleInfoValid() and return parent function if it's true and use custom logic if false + * If overriding this in C++, you should call CapsuleInfo.IsValidCapsule() and return super if it's true and use custom logic if false + * + * If Pawn uses a box instead of a capsule, return the box HalfHeightt as the HalfHeight and the longest width as Radius + * + * If it has a valid CapsuleInfo it will always return it + * + * If capsule size changes eg. when crouching then you need to update the CapsuleInfo manually + * (eg. from the OnStartCrouching and OnStopCrouching) events + */ + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = Vault) + FVICapsuleInfo GetCapsuleInfo() const; + + UFUNCTION(BlueprintPure, Category = Vault) + bool IsCapsuleInfoValid() const { return CapsuleInfo.IsValidCapsule(); } + + // Anti-cheat +public: + /** + * Tests for cheating or invalid / incompatible data + * Incompatible data and rejection can result from de-sync and does not necessarily mean the player is cheating + * + * No need to test local or remote role or net mode; this is all done for you + * + * @return True if anti-cheat passed + */ + UFUNCTION(BlueprintPure, Category = "Vault|AntiCheat") + bool ComputeAntiCheatResult(const FVIVaultInfo& VaultInfo) const; + +protected: + /** + * Used by ComputeCustomAntiCheat to call built-in anti-cheat functions + * This allows extension of existing behaviour + * + * ServerVaultResult can be obtained from ComputeVault() + * + * @return True if anti-cheat passed + */ + UFUNCTION(BlueprintPure, Category = "Vault|AntiCheat") + bool ComputeDefaultAntiCheat(const FVIVaultInfo& ClientVaultInfo, const FVIVaultResult& ServerVaultResult) const { return AntiCheatSettings.ComputeAntiCheat(ClientVaultInfo, ServerVaultResult, PawnOwner); } + + /** + * Override to implement custom AntiCheat + * Note that performance metrics are not available for this function + */ + UFUNCTION(BlueprintNativeEvent, Category = "Vault|AntiCheat") + bool ComputeCustomAntiCheat(const FVIVaultInfo& ClientVaultInfo) const; + +protected: + bool PredictLandingLocation(FPredictProjectilePathResult& PredictResult, float HalfHeight, float Radius, float GravityZ) const; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VIBlueprintFunctionLibrary.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VIBlueprintFunctionLibrary.h new file mode 100644 index 00000000..0b78cb50 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VIBlueprintFunctionLibrary.h @@ -0,0 +1,231 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VITypes.h" +#include "VIBlueprintFunctionLibrary.generated.h" + +class ACharacter; +class AVICharacterBase; +struct FPredictProjectilePathResult; + +/** + * + */ +UCLASS(meta = (ScriptName = "VIBlueprintFunctionLibrary")) +class VAULTIT_API UVIBlueprintFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + static void MessageLogError(const FString& ErrorMsg, bool bOpenLog = true); + + UFUNCTION(BlueprintPure, Category = Vault) + static bool VaultAnimSetIsValid(const FVIAnimSet& AnimSet) { return AnimSet.IsValid(); } + + UFUNCTION(BlueprintPure, Category = Replication) + static bool IsRunningOnServer(const AActor* Actor) + { + if (Actor) + { + return (Actor->GetNetMode() == NM_DedicatedServer || Actor->GetNetMode() == NM_ListenServer); + } + return false; + } + + /** + * Get Max Jump Height + Max Vault Height + * The maximum height a character can achieve from the ground + * Assumes gravity, jump velocity, and vault height do not change during runtime and is not in a physics volume or otherwise modified + */ + UFUNCTION(BlueprintPure, Category = Pawn) + static float GetMaxHeightFromGround(const ACharacter* Character); + + UFUNCTION(BlueprintPure, Category = Pawn) + static float GetMaxVaultHeight(const APawn* Pawn); + + /** + * Get Max Jump Height + Max Vault Height + * The maximum height a pawn can achieve from the ground + * Assumes gravity, jump velocity, and vault height do not change during runtime and is not in a physics volume or otherwise modified + */ + UFUNCTION(BlueprintPure, Category = Pawn) + static float GetMaxHeightFromGroundForPawn(const APawn* Pawn, const float Gravity, const float JumpZVelocity); + + /** + * Get Max Jump Height + * When added to max vault height we get the maximum height a character can achieve from the ground + * Assumes gravity, jump velocity, and vault height do not change during runtime and is not in a physics volume or otherwise modified + */ + UFUNCTION(BlueprintPure, Category = Pawn) + static float GetMaxJumpHeightForCharacter(const ACharacter* Character); + + /** + * Get Max Jump Height + * When added to max vault height we get the maximum height a character can achieve from the ground + * Assumes gravity, jump velocity, and vault height do not change during runtime and is not in a physics volume or otherwise modified + */ + UFUNCTION(BlueprintPure, Category = Pawn) + static float GetMaxJumpHeight(const float Gravity, const float JumpZVelocity) + { + if (FMath::Abs(Gravity) > KINDA_SMALL_NUMBER) + { + return FMath::Square(JumpZVelocity) / (-2.f * Gravity); + } + else + { + return 0.f; + } + } + + /** + * @param InActor: Actor to check + * @param bWorldUpIsZ: If true does a much cheaper check when using Z as world up axis, if false will use vector + * projection for arbitrary orientation + * @return True if character is rising, false if falling + */ + UFUNCTION(BlueprintPure, Category = Pawn) + static bool ActorIsAscending(const AActor* InActor, bool bWorldUpIsZ); + + UFUNCTION(BlueprintPure, Category = Animation) + static float ComputeAnimationPlayRateFromDuration(const UAnimSequenceBase* Animation, float Duration = 1.f); + + /** + * Called by Anim Graph from VIAnimationInterface::SetBoneFBIK to either enable and update FBIK bone data or disable it + */ + UFUNCTION(BlueprintCallable, Category = FBIK) + static void ToggleBoneFBIK(const FName& BoneName, const FVector& NewLocation, bool bEnable, UPARAM(ref) TArray& Bones) + { + if (FVIBoneFBIKData* Bone = GetBoneForFBIK(BoneName, Bones)) + { + if (bEnable) + { + Bone->Enable(NewLocation); + } + else + { + Bone->Disable(); + } + } + } + + /** + * Called by Anim Graph to interpolate array of FBIK bone data + */ + UFUNCTION(BlueprintCallable, Category = FBIK) + static void InterpolateFBIK(float DeltaTime, UPARAM(ref) TArray& Bones) + { + for (FVIBoneFBIKData& Bone : Bones) + { + Bone.Update(DeltaTime); + } + } + + /** Get specific FBIK Bone Data */ + static FVIBoneFBIKData* GetBoneForFBIK(FName BoneName, TArray& Bones) + { + for (FVIBoneFBIKData& Bone : Bones) + { + if (Bone.BoneName.IsEqual(BoneName)) + { + return &Bone; + } + } + + return nullptr; + } + + /** Get specific FBIK Bone Data */ + static const FVIBoneFBIKData* GetBoneForFBIK(FName BoneName, const TArray& Bones) + { + for (const FVIBoneFBIKData& Bone : Bones) + { + if (Bone.BoneName.IsEqual(BoneName)) + { + return &Bone; + } + } + + return nullptr; + } + + /** + * Get specific FBIK Bone Data + * This is not checked whether it exists or not; you can add your own based on whether the resulting BoneName is None or not + */ + UFUNCTION(BlueprintPure, Category = FBIK, meta = (DisplayName = "Get Bone For FBIK")) + static FVIBoneFBIKData K2_GetBoneForFBIK(FName BoneName, const TArray& Bones) + { + for (const FVIBoneFBIKData& Bone : Bones) + { + if (Bone.BoneName.IsEqual(BoneName)) + { + return Bone; + } + } + + return FVIBoneFBIKData(); + } + + /** Returns the VaultInfo if it exists */ + UFUNCTION(BlueprintPure, Category = "Ability|TargetData") + static FVIVaultInfo GetVaultInfoFromTargetData(const FGameplayAbilityTargetDataHandle& TargetData); + + /** + * Predict where a Character or Pawn will land + */ + static bool PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, ACharacter* ForCharacter, const TArray>& ObjectTypes, bool bTraceComplex); + + /** + * Predict where a Character or Pawn will land + */ + static bool PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, AActor* ForActor, const TArray>& ObjectTypes, float HalfHeight, float Radius, const FVector& Gravity, bool bTraceComplex); + + /** + * Predict where a Character or Pawn will land + */ + static bool PredictLandingLocation(FPredictProjectilePathResult& OutPredictResult, AActor* ForActor, const TArray>& ObjectTypes, float HalfHeight, float Radius, float GravityZ, bool bTraceComplex); + + /** + * Predict the arc of a virtual capsule (character) affected by gravity with collision checks along the arc. + * Returns true if it hit something. + * + * @param PredictParams Input params to the trace (start location, velocity, time to simulate, etc). + * @param PredictResult Output result of the trace (Hit result, array of location/velocity/times for each trace step, etc). + * @return True if hit something along the path (if tracing with collision). + */ + static bool PredictCapsulePath(const UObject* WorldContextObject, float HalfHeight, const struct FPredictProjectilePathParams& PredictParams, struct FPredictProjectilePathResult& PredictResult, const FVector& UpVector); + + /** + * Always check CanVault() or similar functionality to ensure character is in a state where they're allowed to vault + */ + static FVIVaultResult ComputeVault(APawn* const Pawn, const FVector& InVaultDirection, const FVITraceSettings& TraceSettings, const FVICapsuleInfo& Capsule, bool bTraceComplex); + + /** + * Same as engine version but allows for rotation via quaternion + * Sweeps a capsule along the given line and returns the first hit encountered. + * This only finds objects that are of a type specified by ObjectTypes. + * + * @param WorldContext World context + * @param Start Start of line segment. + * @param End End of line segment. + * @param Rot Actor Quat + * @param Radius Radius of the capsule to sweep + * @param HalfHeight Distance from center of capsule to tip of hemisphere endcap. + * @param ObjectTypes Array of Object Types to trace + * @param bTraceComplex True to test against complex collision, false to test against simplified collision. + * @param OutHit Properties of the trace hit. + * @return True if there was a hit, false otherwise. + */ + static void CapsuleTraceSingleForObjects(const UObject* WorldContextObject, const FVector& Start, const FVector& End, const FQuat& Rot, float Radius, float HalfHeight, const TArray > & ObjectTypes, bool bTraceComplex, const TArray& ActorsToIgnore, bool bDrawDebug, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f); + + static void DrawDebugCapsuleTraceSingle(const UWorld* World, const FVector& Start, const FVector& End, const FQuat& Rot, float Radius, float HalfHeight, bool bDrawDebug, bool bHit, const FHitResult& OutHit, FLinearColor TraceColor, FLinearColor TraceHitColor, float DrawTime); + + /** + * When not using Z-Up this will convert a vector to a float that is only in the given direction + * It is the equivalent of doing Vector.Z (0,0,1) if Dir is the up vector or Vector.X (1,0,0) if Dir is the forward vector + */ + static float ComputeDirectionToFloat(const FVector& Vector, const FVector& Dir); +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VITypes.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VITypes.h new file mode 100644 index 00000000..eb1abcb0 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VITypes.h @@ -0,0 +1,403 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "Abilities/GameplayAbilityTargetTypes.h" +#include "GameplayTagsManager.h" +#include "VITypes.generated.h" + +DECLARE_STATS_GROUP(TEXT("VaultIt_Plugin"), STATGROUP_VaultIt, STATCAT_Advanced); + +/** + * Used to determine behaviour of jump key in relation to vaulting + */ +UENUM(BlueprintType) +enum class EVIJumpKeyPriority : uint8 +{ + JKP_DisableVault UMETA(DisplayName = "Disable Vault from Jump", ToolTip = "Jump key cannot vault"), + JKP_SelectHighestPoint UMETA(DisplayName = "Select Highest Point", ToolTip = "Checks ahead to see if jump can reach as high as vault, and will vault if it can go higher"), + JKP_AlwaysVault UMETA(DisplayName = "Always Vault if Possible", ToolTip = "Will always vault if it is possible to do so, otherwise jump"), + JKP_OnlyVaultFromAir UMETA(DisplayName = "Only Vault from Air", ToolTip = "Only vault if in air and jump key wont otherwise do anything (eg. double jump)"), + JKP_MAX = 255 UMETA(Hidden) +}; + +/** How gameplay effects will be replicated to clients */ +UENUM(BlueprintType) +enum class EVIGameplayEffectReplicationMode : uint8 +{ + /** Only replicate minimal gameplay effect info. Note: this does not work for Owned AbilitySystemComponents (Use Mixed instead). */ + Minimal, + /** Only replicate minimal gameplay effect info to simulated proxies but full info to owners and autonomous proxies */ + Mixed, + /** Replicate full gameplay info to all */ + Full, +}; + +USTRUCT(BlueprintType) +struct VAULTIT_API FVICapsuleInfo +{ + GENERATED_BODY() + + FVICapsuleInfo() + : HalfHeight(0.f) + , Radius(0.f) + {} + + FVICapsuleInfo(float InHalfHeight, float InRadius) + : HalfHeight(InHalfHeight) + , Radius(InRadius) + {} + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Vault) + float HalfHeight; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Vault) + float Radius; + + float GetHalfHeightWithoutHemisphere() const { return HalfHeight - Radius; } + + bool IsValidCapsule() const { return HalfHeight > 0.f && Radius > 0.f; } +}; + +/** + * Used to create a nested array of animations + * TMap where float is vault height + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIAnimations +{ + GENERATED_BODY() + + FVIAnimations() + {} + + UPROPERTY(EditDefaultsOnly, BlueprintReadonly, Category = Vault) + TArray Animations; + + bool IsValid() const { return Animations.Num() > 0; } +}; + +/** + * Used to create a nested array of animations + * TMap where float is vault height + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIAnimSet +{ + GENERATED_BODY() + + FVIAnimSet() + {} + + /** + * Key: Vault height + * Value: Array of animations that can be played at that height + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadonly, Category = Vault) + TMap Animations; + + bool IsValid() const { return Animations.Num() > 0; } +}; + +/** + * Settings to use when tracing for a potential vault attempt + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVITraceSettings +{ + GENERATED_BODY() + + FVITraceSettings() + : MaxLedgeHeight(250.f) + , MinLedgeHeight(45.f) + , ReachDistance(75.f) + , ForwardTraceRadius(30.f) + , DownwardTraceRadius(30.f) + , CollisionFloatHeight(2.4f) + , MaxObjectVelocity(0.f) + , ObjectChannels({ ECollisionChannel::ECC_WorldStatic, ECollisionChannel::ECC_WorldDynamic }) + , TraceProfile(TEXT("Vault")) + {} + + /** This should be near the highest vault animation you have - motion warping can assist a lot but not completely */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float MaxLedgeHeight; + + /** This should be the lowest vault animation you have and should probably not be identical to or lower than CMC max step height */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float MinLedgeHeight; + + /** How far ahead of the character can we grab when looking for a ledge */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float ReachDistance; + + /** Radius when checking if the surface ahead is walkable (no need to vault it) */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float ForwardTraceRadius; + + /** Radius when checking if we can stand on the surface ahead (and below) */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float DownwardTraceRadius; + + /** Characters float 2.4cm off the ground by default */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float CollisionFloatHeight; + + /** Object can not be vaulted onto if exceeding this velocity. Set to 0 to disable. */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + float MaxObjectVelocity; + + /** Channels for object types that we can vault on (Must be an object channel - eg. WorldStatic; trace channels will not work - eg. Visibility, Camera) */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + TArray> ObjectChannels; + + /** This should be a profile that tests for anything that would stop us moving to our vault location */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Vault|Settings") + FName TraceProfile; + + /** Helper for conversion of ObjectChannels */ + TArray> GetObjectTypes() const + { + TArray> Result; + for (const TEnumAsByte& CC : ObjectChannels) + { + Result.Add(UEngineTypes::ConvertToObjectType(CC)); + } + + return Result; + } +}; + +USTRUCT(BlueprintType) +struct VAULTIT_API FVIBoneFBIKData +{ + GENERATED_BODY() + +public: + FVIBoneFBIKData() + : BoneName(NAME_None) + , InterpRate(150.f) + , bEnabled(false) + , Location(FVector::ZeroVector) + , TargetLocation(FVector::ZeroVector) + , bReset(true) + {} + + FVIBoneFBIKData(const FName& InBoneName, float InInterpRate = 150.f) + : BoneName(InBoneName) + , InterpRate(InInterpRate) + , bEnabled(false) + , Location(FVector::ZeroVector) + , TargetLocation(FVector::ZeroVector) + , bReset(true) + {} + + /** Bone that is moved by FBIK */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Vault) + FName BoneName; + + /** Rate that bone moves to target location. 0 for no interpolation. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Vault) + float InterpRate; + + /** Whether this bone has it's FBIK enabled or not */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Vault) + bool bEnabled; + + /** Location used by anim graph */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Vault) + FVector Location; + + /** Location to interpolate to */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Vault) + FVector TargetLocation; + +protected: + UPROPERTY() + bool bReset; + +public: + void Update(float DeltaTime) + { + if (!bEnabled) + { + return; + } + + if (bReset || InterpRate <= 0.f) + { + Location = TargetLocation; + bReset = false; + } + else + { + Location = FMath::VInterpConstantTo(Location, TargetLocation, DeltaTime, InterpRate); + } + } + + /** Enable or update target location */ + void Enable(const FVector& NewLocation) + { + bEnabled = true; + TargetLocation = NewLocation; + + // Init Location + if (Location.IsNearlyZero()) + { + Location = TargetLocation; + } + } + + void Disable() + { + // Leave Location intact for blending out + bEnabled = false; + bReset = true; + TargetLocation = FVector::ZeroVector; + } +}; + +/** + * Used by simulated proxies to motion match + * Net quantized to 1 decimal point for bandwidth optimization + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIRepMotionMatch +{ + GENERATED_BODY() + + FVIRepMotionMatch() + : Location(FVector::ZeroVector) + , Direction(FVector::ZeroVector) + {} + + FVIRepMotionMatch(const FVector& InLocation, const FVector& InDirection) + : Location(InLocation) + , Direction(InDirection) + {} + + UPROPERTY(BlueprintReadWrite, Category = Vault) + FVector Location; + + UPROPERTY(BlueprintReadWrite, Category = Vault) + FVector Direction; + + bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess); +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithNetSerializer = true + }; +}; + +/** + * Vault info computed locally then sent to the server for use by GA_Vault + * Net quantized to 1 decimal point for bandwidth optimization + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIVaultInfo +{ + GENERATED_BODY() + + FVIVaultInfo() + : Location(FVector::ZeroVector) + , Direction(FVector::ZeroVector) + , Height(0.f) + , RandomSeed(0) + {} + + UPROPERTY(BlueprintReadOnly, Category = Vault) + FVector Location; + + UPROPERTY(BlueprintReadOnly, Category = Vault) + FVector Direction; + + UPROPERTY(BlueprintReadOnly, Category = Vault) + float Height; + + UPROPERTY(BlueprintReadOnly, Category = Vault) + uint8 RandomSeed; + + void SetHeight(float InHeight) + { + // 1 decimal point of precision + Height = RoundFloat(InHeight); + } + + static float RoundFloat(float InFloat) + { + // 1 decimal point of precision + return FMath::RoundToFloat(InFloat * 10.f) / 10.f; + } + + bool IsValid() const { return Height != 0.f; } +}; + +/** + * Result of the vault along with the VaultInfo + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIVaultResult : public FVIVaultInfo +{ + GENERATED_BODY() + + FVIVaultResult() + : bSuccess(false) + {} + + UPROPERTY(BlueprintReadOnly, Category = Vault) + bool bSuccess; +}; + +/** + * GameplayAbilityTargetData responsible for sending our VaultInfo to the GameplayAbility + */ +USTRUCT(BlueprintType) +struct VAULTIT_API FVIGameplayAbilityTargetData_VaultInfo : public FGameplayAbilityTargetData +{ + GENERATED_BODY() + +public: + UPROPERTY() + FVIVaultInfo VaultInfo; + + const FVIVaultInfo* GetVaultInfo() const + { + return &VaultInfo; + } + + // ------------------------------------- + + virtual bool HasOrigin() const override final { return false; } + virtual FTransform GetOrigin() const override final { return FTransform::Identity; } + + virtual bool HasEndPoint() const override final { return false; } + virtual FVector GetEndPoint() const override final { return FVector::ZeroVector; } + + // ------------------------------------- + + virtual UScriptStruct* GetScriptStruct() const override final + { + return FVIGameplayAbilityTargetData_VaultInfo::StaticStruct(); + } + + virtual FString ToString() const override final + { + return TEXT("FVIGameplayAbilityTargetData_VaultInfo"); + } + + bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess); +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithNetSerializer = true // For now this is REQUIRED for FGameplayAbilityTargetDataHandle net serialization to work + }; +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VaultIt.h b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VaultIt.h new file mode 100644 index 00000000..e701e564 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/Public/VaultIt.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FVaultItModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/VaultIt.Build.cs b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/VaultIt.Build.cs new file mode 100644 index 00000000..93230837 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/Source/VaultIt/VaultIt.Build.cs @@ -0,0 +1,32 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class VaultIt : ModuleRules +{ + public VaultIt(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "GameplayAbilities", + "GameplayTasks", + "GameplayTags", + "MotionWarping", + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "NetCore", + } + ); + } +} diff --git a/EndlessVendetta/Plugins/VaultIt/VaultIt.uplugin b/EndlessVendetta/Plugins/VaultIt/VaultIt.uplugin new file mode 100644 index 00000000..f86378a2 --- /dev/null +++ b/EndlessVendetta/Plugins/VaultIt/VaultIt.uplugin @@ -0,0 +1,40 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.1.0.0", + "FriendlyName": "VaultIt!", + "Description": "Multiplayer ready GAS-based vaulting with motion warping & full-body IK & net prediction of the highest caliber", + "Category": "Gameplay", + "CreatedBy": "Drowning Dragons", + "CreatedByURL": "", + "DocsURL": "https://github.com/DrowningDragons/VaultIt-Wiki/wiki", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/7d19b5622d6846edb9881e6c89bce05e", + "SupportURL": "https://discord.gg/rk3VRYv", + "EngineVersion": "5.1.0", + "CanContainContent": true, + "Installed": true, + "Modules": [ + { + "Name": "VaultIt", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64" + ] + } + ], + "Plugins": [ + { + "Name": "GameplayAbilities", + "Enabled": true + }, + { + "Name": "ControlRig", + "Enabled": true + }, + { + "Name": "MotionWarping", + "Enabled": true + } + ] +} \ No newline at end of file