diff --git a/CREDITS.md b/CREDITS.md index 491db45ce1..b224866594 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -562,6 +562,8 @@ This page lists all the individual contributions to the project by their author. - Display banner improvement and doc - Damage multiplier for health percentage - Linked superweapons tweak + - Randomized anims for several behaviors + - Fixed customized `WarpAway` anim's wrong definition - **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes - **handama** - AI script action to `16005 Jump Back To Previous Script` - **TaranDahl (航味麻酱)**: diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 172126a98f..54aca7c5c8 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -407,7 +407,7 @@ AttachedAnimPosition=default ; Attached animation position enumeration (default| - `ExplodeOnWater` can be set to true to make the animation explode on impact with water. `ExpireAnim` will be played and `Warhead` is detonated or used to deal damage / generate light flash. - `Warhead.Detonate`, if set to true, makes the `Warhead` fully detonate instead of simply being used to deal damage and generate light flash if it has `Bright=true`. -- `WakeAnim` contains a wake animation to play if `ExplodeOnWater` is not set and the animation impacts with water. Defaults to `[General] -> Wake` if `IsMeteor` is not set to true, otherwise no animation. +- `WakeAnim` contains wake animations to play if `ExplodeOnWater` is not set and the animation impacts with water. Defaults to `[General] -> Wake` if `IsMeteor` is not set to true, otherwise no animation. If more than one animation is listed, a random one is selected. - `SplashAnims` contains list of splash animations used if `ExplodeOnWater` is not set and the animation impacts with water. - If `SplashAnims.PickRandom` is set to true, picks a random animation from `SplashAnims` to use on each impact with water. Otherwise last listed animation from `SplashAnims` is used. - `ExtraShadow` can be set to false to disable the display of shadows on the ground. @@ -417,8 +417,8 @@ In `artmd.ini`: [SOMEANIM] ; AnimationType ExplodeOnWater=false ; boolean Warhead.Detonate=false ; boolean -WakeAnim= ; AnimationType -SplashAnims= ; List of AnimationTypes, default to [CombatDamage] -> SplashList +WakeAnim= ; list of Animation +SplashAnims= ; List of Animation, default to [CombatDamage] -> SplashList SplashAnims.PickRandom=false ; boolean ExtraShadow=true ; boolean ``` @@ -1034,14 +1034,15 @@ TargetZoneScanType=same ; target zone scan enumeration (same|any|inrange) *Chrono Legionnaire and Ronco using different teleportation settings in [YR: New War](https://www.moddb.com/mods/yuris-revenge-new-war)* - You can now specify Teleport/Chrono Locomotor settings per TechnoType to override default rules values. Unfilled values default to values in `[General]`. -- Only applicable to Techno that have Teleport/Chrono Locomotor attached. +- Applicable to Techno that have Teleport/Chrono Locomotor attached, or being chronowarped by chronosphere. +- If more than one animation is listed in `WarpOut`, `WarpIn` or `WarpAway`, a random one is selected. In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType -WarpOut= ; Anim (played when Techno warping out) -WarpIn= ; Anim (played when Techno warping in) -WarpAway= ; Anim (played when Techno chronowarped by chronosphere) +WarpOut= ; list of Animation (played when Techno warping out), default to [General] WarpOut +WarpIn= ; list of Animation (played when Techno warping in), default to [General] WarpIn +WarpAway= ; list of Animation (played when Techno being erased by `Temporal=yes` warhead), default to [General] WarpAway ChronoTrigger= ; boolean, if yes then delay varies by distance, if no it is a constant ChronoDistanceFactor= ; integer, amount to divide the distance to destination by to get the warped out delay ChronoMinimumDelay= ; integer, the minimum delay for teleporting, no matter how short the distance diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index b4b346cd7f..c97e2845a3 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -339,8 +339,8 @@ IdleAnimDamaged.ConditionYellow= ; AnimationType IdleAnimDamaged.ConditionRed= ; AnimationType IdleAnim.OfflineAction=Hides ; AttachedAnimFlag (None, Hides, Temporal, Paused or PausedTemporal) IdleAnim.TemporalAction=Hides ; AttachedAnimFlag (None, Hides, Temporal, Paused or PausedTemporal) -BreakAnim= ; AnimationType -HitAnim= ; AnimationType +BreakAnim= ; list of Animation +HitAnim= ; list of Animation HitFlash=false ; boolean HitFlash.FixedSize= ; integer HitFlash.Red=true ; boolean @@ -365,8 +365,8 @@ ShieldType=SOMESHIELDTYPE ; ShieldType; none by default [SOMEWARHEAD] ; WarheadType Shield.Penetrate=false ; boolean Shield.Break=false ; boolean -Shield.BreakAnim= ; AnimationType -Shield.HitAnim= ; AnimationType +Shield.BreakAnim= ; list of Animation +Shield.HitAnim= ; list of Animation Shield.SkipHitAnim=false ; boolean Shield.HitFlash=true ; boolean Shield.BreakWeapon= ; WeaponType @@ -426,8 +426,8 @@ Shield.InheritStateOnReplace=false ; boolean - `Bouncer=true` and `IsMeteor=true` animations can exhibit irregular behaviour when used as `IdleAnim` and should be avoided. - `IdleAnim.OfflineAction` indicates what happens to the animation when the shield is in a low power state. - `IdleAnim.TemporalAction` indicates what happens to the animation when the shield is attacked by temporal weapons. -- `BreakAnim`, if set, will be played when the shield has been broken. -- `HitAnim`, if set, will be played when the shield is attacked, similar to `WeaponNullifyAnim` for Iron Curtain. +- `BreakAnim`, if set, will be played when the shield has been broken. If more than one animation is listed, a random one is selected. +- `HitAnim`, if set, will be played when the shield is attacked, similar to `WeaponNullifyAnim` for Iron Curtain. If more than one animation is listed, a random one is selected. - `HitFlash`, if set to true, makes it so that a light flash is generated when the shield is attacked by a Warhead unless it has `Shield.HitFlash=false`. Size of the flash is determined by damage dealt, unless `HitFlash.FixedSize` is set to a number, in which case that value is used instead (range of values that produces visible effect are increments of 4 from 81 to 252, anything higher or below does not have effect). Color can be customized via `HitFlash.Red/Green/Blue`. If `HitFlash.Black` is set to true, the generated flash will be black regardless of other color settings. - `BreakWeapon`, if set, will be fired at the TechnoType once the shield breaks. - `AbsorbPercent` controls the percentage of damage that will be absorbed by the shield. Defaults to 1.0, meaning full damage absorption. @@ -449,8 +449,8 @@ Shield.InheritStateOnReplace=false ; boolean - Warheads have new options that interact with shields. Note that all of these that do not by their very nature require ability to target the shield (such as modifiers like `Shield.Break` or removing / attaching) still require Warhead `Verses` to affect the target unless `EffectsRequireVerses` is set to false on the Warhead. - `Shield.Penetrate` allows the warhead ignore the shield and always deal full damage to the TechnoType itself. It also allows targeting the TechnoType as if shield doesn't exist. - `Shield.Break` allows the warhead to always break shields of TechnoTypes. This is done before damage is dealt. - - `Shield.BreakAnim` will be displayed instead of ShieldType `BreakAnim` if the shield is broken by the Warhead, either through damage or `Shield.Break`. - - `Shield.HitAnim` will be displayed instead of ShieldType `HitAnim` if set when Warhead hits the shield. + - `Shield.BreakAnim` will be displayed instead of ShieldType `BreakAnim` if the shield is broken by the Warhead, either through damage or `Shield.Break`. If more than one animation is listed, a random one is selected. + - `Shield.HitAnim` will be displayed instead of ShieldType `HitAnim` if set when Warhead hits the shield. If more than one animation is listed, a random one is selected. - If `Shield.SkipHitAnim` is set to true, no hit anim is shown when the Warhead damages the shield whatsoever. - `Shield.BreakWeapon` will be fired instead of ShieldType `BreakWeapon` if the shield is broken by the Warhead, either through damage or `Shield.Break`. - `Shield.AbsorbPercent` overrides the `AbsorbPercent` value set in the ShieldType that is being damaged. @@ -504,7 +504,7 @@ Note that the AircraftTypes had to be defined under [AircraftTypes]. - `CreateUnit.AlwaysSpawnOnGround`, if set to true, ensures the unit will be created on the cell at ground level even if animation is in air. If set to false, jumpjet units spawned on ground will take off automatically after being spawned regardless. - `CreateUnit.SpawnParachutedInAir`, if set to true, makes it so that the unit is created with a parachute if it is spawned in air. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true. - `CreateUnit.ConsiderPathfinding`, if set to true, will consider whether or not the cell where the animation is located is occupied by other objects or impassable to the unit being created and will attempt to find a nearby cell that is not. Otherwise the unit will be created at the animation's location despite these obstacles if possible. - - `CreateUnit.SpawnAnim` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation. + - `CreateUnit.SpawnAnim` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation. If more than one animation is listed, a random one is selected. - `CreateUnit.SpawnHeight` can be set to override the animation's height when determining where to spawn the created unit if set to positive value. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true. In `artmd.ini`: @@ -523,7 +523,7 @@ CreateUnit.InheritTurretFacings=false ; boolean CreateUnit.AlwaysSpawnOnGround=false ; boolean CreateUnit.SpawnParachutedInAir=false ; boolean CreateUnit.ConsiderPathfinding=false ; boolean -CreateUnit.SpawnAnim= ; AnimationType +CreateUnit.SpawnAnim= ; list of Animation CreateUnit.SpawnHeight=-1 ; integer, height in leptons ``` @@ -1296,7 +1296,7 @@ AttackMove.IgnoreWeaponCheck=false ; boolean - If `Spawner.AttackImmediately` is set to true, spawned aircraft will assume attack mission immediately after being spawned instead of waiting for the remaining aircraft to spawn first. - `Spawner.UseTurretFacing`, if set, makes spawned aircraft face the same way as turret does upon being created if the spawner has a turret. - `Spawner.RecycleRange` defines the range (in cell) that the spawned is considered close enough to the spawner to be recycled. -- `Spawner.RecycleAnim` can be used to play an anim on the spawned location when it is recycled. +- `Spawner.RecycleAnim` can be used to play an anim on the spawned location when it is recycled. If more than one animation is listed, a random one is selected. - `Spawner.RecycleCoord` defines the relative position to the carrier that the spawned aircraft will head to. - `Spawner.RecycleOnTurret` defines if the FLH is relative to the turret rather than the body. @@ -1309,7 +1309,7 @@ Spawner.DelayFrames= ; integer, game frames Spawner.AttackImmediately=false ; boolean Spawner.UseTurretFacing=false ; boolean Spawner.RecycleRange=-1 ; float, range in cells -Spawner.RecycleAnim= ; Animation +Spawner.RecycleAnim= ; list of Animation Spawner.RecycleCoord=0,0,0 ; integer - Forward,Lateral,Height Spawner.RecycleOnTurret=false ; boolean ``` @@ -1332,7 +1332,7 @@ If you set recycle FLH, it is best to set a recycle range of at least `0.5` at t - `PassengerDeletion.SoylentMultiplier` is a direct multiplier applied to the refunded amount of credits. - `PassengerDeletion.SoylentAllowedHouses` determines which houses passengers can belong to be eligible for refunding. - `PassengerDeletion.DisplaySoylent` can be set to true to display the amount of credits refunded on the transport. `PassengerDeletion.DisplaySoylentToHouses` determines which houses can see this and `PassengerDeletion.DisplaySoylentOffset` can be used to adjust the display offset. - - `PassengerDeletion.ReportSound` and `PassengerDeletion.Anim` can be used to specify a sound and animation to play when a passenger is erased, respectively. + - `PassengerDeletion.ReportSound` and `PassengerDeletion.Anim` can be used to specify a sound and animation to play when a passenger is erased, respectively. If more than one animation is listed, a random one is selected. - If `PassengerDeletion.UnderEMP` is set to true, the deletion will be processed when the transport is under EMP or deactivated. In `rulesmd.ini`: @@ -1352,7 +1352,7 @@ PassengerDeletion.DisplaySoylent=false ; boolean PassengerDeletion.DisplaySoylentToHouses=All ; Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) PassengerDeletion.DisplaySoylentOffset=0,0 ; X,Y, pixels relative to default PassengerDeletion.ReportSound= ; Sound entry -PassengerDeletion.Anim= ; AnimationType +PassengerDeletion.Anim= ; list of Animation PassengerDeletion.UnderEMP=false ; boolean ``` @@ -1643,14 +1643,14 @@ Both `InitialStrength` and `InitialStrength.Cloning` never surpass the type's `S - `kill`: The object will be destroyed normally. - `vanish`: The object will be directly removed from the game peacefully instead of actually getting killed. - `sell`: If the object is a **building** with buildup, it will be sold instead of destroyed. -- If this option is not set, the self-destruction logic will not be enabled. `AutoDeath.VanishAnimation` can be set to animation to play at object's location if `vanish` behaviour is chosen. +- If this option is not set, the self-destruction logic will not be enabled. `AutoDeath.VanishAnimation` can be set to animation to play at object's location if `vanish` behaviour is chosen. If more than one animation is listed, a random one is selected. - This logic also supports buildings delivered by [LimboDelivery](#limbodelivery). However in this case, all `AutoDeath.Behavior` values produce identical result where the building is simply deleted. In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType AutoDeath.Behavior= ; enumeration (kill | vanish | sell), default not set -AutoDeath.VanishAnimation= ; AnimationType +AutoDeath.VanishAnimation= ; list of Animation AutoDeath.OnAmmoDepletion=no ; boolean AutoDeath.AfterDelay=0 ; positive integer AutoDeath.TechnosDontExist= ; List of TechnoTypes @@ -1785,19 +1785,19 @@ Promote.IncludeSpawns=false ; boolean ### Promotion animation - You can now specify an animation on the unit or structure promotion. - - `Promote.VeteranAnimation` is used when unit or structure is promoted to veteran. If this is set for a TechnoType, it'll override the global setting in `[AudioVisual]`. - - `Promote.EliteAnimation` is used when unit or structure is promoted to elite. If this is set for a TechnoType, it'll override the global setting in `[AudioVisual]`. + - `Promote.VeteranAnimation` is used when unit or structure is promoted to veteran. If this is set for a TechnoType, it'll override the global setting in `[AudioVisual]`. If more than one animation is listed, a random one is selected. + - `Promote.EliteAnimation` is used when unit or structure is promoted to elite. If this is set for a TechnoType, it'll override the global setting in `[AudioVisual]`. If more than one animation is listed, a random one is selected. - If `Promote.EliteAnimation` is not defined, `Promote.VeteranAnimation` will play instead when unit or structure is promoted to elite. In `rulesmd.ini`: ```ini [AudioVisual] -Promote.VeteranAnimation= ; AnimationType -Promote.EliteAnimation= ; AnimationType +Promote.VeteranAnimation= ; list of Animation +Promote.EliteAnimation= ; list of Animation -[SOMETECHNO] ; TechnoType -Promote.VeteranAnimation= ; AnimationType, default to Promote.VeteranAnimation in [AudioVisual] -Promote.EliteAnimation= ; AnimationType, default to Promote.EliteAnimation in [AudioVisual] +[SOMETECHNO] +Promote.VeteranAnimation= ; list of Animation, default to Promote.VeteranAnimation in [AudioVisual] +Promote.EliteAnimation= ; list of Animation, default to Promote.EliteAnimation in [AudioVisual] ``` ### Raise alert when technos are taking damage @@ -1981,11 +1981,12 @@ WarpOutWeapon= ; WeaponType ### Destroy animation & sound - You can now specify a destroy animation and sound for a TerrainType that are played when it is destroyed. + - If more than one animation is listed in `DestroyAnim`, a random one is selected. In `rulesmd.ini`: ```ini [SOMETERRAINTYPE] ; TerrainType -DestroyAnim= ; AnimationType +DestroyAnim= ; list of Animation DestroySound= ; Sound entry ``` diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 1356a2d560..5335abaecb 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -441,6 +441,7 @@ New: - Health bar permanently displayed (by FlyStar) - [`IsSimpleDeployer` facing customization & directional deploy animations](Fixed-or-Improved-Logics.md#issimpleDeployer-facing-and-animation-customization) (by Starkku) - [Ammo-based deploy customizations for vehicles expanded to non-IsSimpleDeployer deploy functions](New-or-Enhancerd-Logics.md#Automatic-deploy-and-blocking-deploying-based on-ammo) (by Starkku) +- Randomized anims for several behaviors (by Ollerus) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) @@ -467,6 +468,7 @@ Phobos fixes: - Fixed a bug that sometimes caused weapon/warhead detonations from features such as `ExtraWarheads`, animation damage or `Crit.Warhead` to unintentionally move from its intended position (by Starkku) - Fixed the bug that armor multiplier of new attacheffect will have extra take effect once if restricted warheads (by NetsuNegi) - Fixed an issue that units' `LaserTrails` will always lags behind by one frame (by CrimRecya) +- Fixed customized `WarpAway` anim's wrong definition (by Ollerus) Fixes / interactions with other extensions: - `Convert.Deploy` displays 'no deploy' cursor if the new type is not allowed to move to the cell due to `SpeedType` etc. (by Starkku) diff --git a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po index 4dfdf7dc1d..405a7b68d2 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po @@ -2199,9 +2199,10 @@ msgid "" "`WakeAnim` contains a wake animation to play if `ExplodeOnWater` is not " "set and the animation impacts with water. Defaults to `[General] -> Wake`" " if `IsMeteor` is not set to true, otherwise no animation." +" If more than one animation is listed, a random one is selected." msgstr "" -"`WakeAnim` 包含一个在 `ExplodeOnWater` 没有设置且动画与水面接触时使用的水波动画。如果 `IsMeteor` 未设置为" -" true 则默认为 `[General] -> Wake`,否则无动画。" +"`WakeAnim` 包含一组在 `ExplodeOnWater` 没有设置且动画与水面接触时使用的水波动画。如果 `IsMeteor` 未设置为" +" true 则默认为 `[General] -> Wake`,否则无动画。如果列出了多个动画,则将随机选择一个。" #: ../../Fixed-or-Improved-Logics.md:404 msgid "" @@ -2229,7 +2230,7 @@ msgid "" "[SOMEANIM] ; AnimationType\n" "ExplodeOnWater=false ; boolean\n" "Warhead.Detonate=false ; boolean\n" -"WakeAnim= ; AnimationType\n" +"WakeAnim= ; list of Animation\n" "SplashAnims= ; List of AnimationTypes, default to " "[CombatDamage] -> SplashList\n" "SplashAnims.PickRandom=false ; boolean\n" @@ -2238,7 +2239,7 @@ msgstr "" "[SOMEANIM] ; AnimationType\n" "ExplodeOnWater=false ; boolean\n" "Warhead.Detonate=false ; boolean\n" -"WakeAnim= ; AnimationType\n" +"WakeAnim= ; list of Animation\n" "SplashAnims= ; List of AnimationTypes, default to " "[CombatDamage] -> SplashList\n" "SplashAnims.PickRandom=false ; boolean\n" @@ -4015,16 +4016,20 @@ msgid "" msgstr "现在你可以为每个科技类型指定其超时空运动方式设置以覆盖默认规则的值。未填充值则默认使用 `[General]` 中的值。" #: ../../Fixed-or-Improved-Logics.md:1030 -msgid "Only applicable to Techno that have Teleport/Chrono Locomotor attached." -msgstr "仅对使用超时空运动方式的科技类型有效。" +msgid"" +"Only applicable to Techno that have Teleport/Chrono Locomotor attached, " +"or being chronowarped by chronosphere." +msgstr "对使用超时空运动方式或者被超时空传送超武移动的科技类型有效。" #: ../../Fixed-or-Improved-Logics.md:1033 msgid "" "[SOMETECHNO] ; TechnoType\n" -"WarpOut= ; Anim (played when Techno warping out)\n" -"WarpIn= ; Anim (played when Techno warping in)\n" -"WarpAway= ; Anim (played when Techno chronowarped by " -"chronosphere)\n" +"WarpOut= ; list of Animation (played when Techno warping out), default " +"to [General] WarpOut\n" +"WarpIn= ; list of Animation (played when Techno warping in), default " +"to [General] WarpIn\n" +"WarpAway= ; list of Animation (played when Techno being erased by " +"`Temporal=yes` warhead), default to [General] WarpAway\n" "ChronoTrigger= ; boolean, if yes then delay varies by distance, " "if no it is a constant\n" "ChronoDistanceFactor= ; integer, amount to divide the distance to " @@ -4036,10 +4041,12 @@ msgid "" "ChronoDelay= ; integer, delay after teleport for chronosphere\n" msgstr "" "[SOMETECHNO] ; TechnoType\n" -"WarpOut= ; Anim (played when Techno warping out)\n" -"WarpIn= ; Anim (played when Techno warping in)\n" -"WarpAway= ; Anim (played when Techno chronowarped by " -"chronosphere)\n" +"WarpOut= ; list of Animation (played when Techno warping out), default " +"to [General] WarpOut\n" +"WarpIn= ; list of Animation (played when Techno warping in), default " +"to [General] WarpIn\n" +"WarpAway= ; list of Animation (played when Techno being erased by " +"`Temporal=yes` warhead), default to [General] WarpAway\n" "ChronoTrigger= ; boolean, if yes then delay varies by distance, " "if no it is a constant\n" "ChronoDistanceFactor= ; integer, amount to divide the distance to " diff --git a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po index 50700cc06a..1b12d16dbb 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po @@ -1410,8 +1410,8 @@ msgid "" "Hides, Temporal, Paused or PausedTemporal)\n" "IdleAnim.TemporalAction=Hides ; AttachedAnimFlag (None, " "Hides, Temporal, Paused or PausedTemporal)\n" -"BreakAnim= ; AnimationType\n" -"HitAnim= ; AnimationType\n" +"BreakAnim= ; list of Animation\n" +"HitAnim= ; list of Animation\n" "HitFlash=false ; boolean\n" "HitFlash.FixedSize= ; integer\n" "HitFlash.Red=true ; boolean\n" @@ -1438,8 +1438,8 @@ msgid "" "[SOMEWARHEAD] ; WarheadType\n" "Shield.Penetrate=false ; boolean\n" "Shield.Break=false ; boolean\n" -"Shield.BreakAnim= ; AnimationType\n" -"Shield.HitAnim= ; AnimationType\n" +"Shield.BreakAnim= ; list of Animation\n" +"Shield.HitAnim= ; list of Animation\n" "Shield.SkipHitAnim=false ; boolean\n" "Shield.HitFlash=true ; boolean\n" "Shield.BreakWeapon= ; WeaponType\n" @@ -1537,8 +1537,8 @@ msgstr "" "Hides, Temporal, Paused or PausedTemporal)\n" "IdleAnim.TemporalAction=Hides ; AttachedAnimFlag (None, " "Hides, Temporal, Paused or PausedTemporal)\n" -"BreakAnim= ; AnimationType\n" -"HitAnim= ; AnimationType\n" +"BreakAnim= ; list of Animation\n" +"HitAnim= ; list of Animation\n" "HitFlash=false ; boolean\n" "HitFlash.FixedSize= ; integer\n" "HitFlash.Red=true ; boolean\n" @@ -1565,8 +1565,8 @@ msgstr "" "[SOMEWARHEAD] ; WarheadType\n" "Shield.Penetrate=false ; boolean\n" "Shield.Break=false ; boolean\n" -"Shield.BreakAnim= ; AnimationType\n" -"Shield.HitAnim= ; AnimationType\n" +"Shield.BreakAnim= ; list of Animation\n" +"Shield.HitAnim= ; list of Animation\n" "Shield.SkipHitAnim=false ; boolean\n" "Shield.HitFlash=true ; boolean\n" "Shield.BreakWeapon= ; WeaponType\n" @@ -1814,14 +1814,17 @@ msgid "" msgstr "`IdleAnim.TemporalAction` 指定被时空武器冻结时护盾动画的行为。" #: ../../New-or-Enhanced-Logics.md:429 -msgid "`BreakAnim`, if set, will be played when the shield has been broken." -msgstr "`BreakAnim` 如果设置则会在护盾被击碎时播放。" +msgid "" +"`BreakAnim`, if set, will be played when the shield has been broken." +" If more than one animation is listed, a random one is selected." +msgstr "`BreakAnim` 如果设置则会在护盾被击碎时播放。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:430 msgid "" "`HitAnim`, if set, will be played when the shield is attacked, similar to" " `WeaponNullifyAnim` for Iron Curtain." -msgstr "`HitAnim` 如果设置则会在护盾受到攻击时播放,类似于 `WeaponNullifyAnim` 用于铁幕。" +" If more than one animation is listed, a random one is selected." +msgstr "`HitAnim` 如果设置则会在护盾受到攻击时播放,类似于 `WeaponNullifyAnim` 用于铁幕。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:431 msgid "" @@ -2015,14 +2018,15 @@ msgstr "`Shield.Break` 允许弹头总是会击碎科技类型上的这些护盾 msgid "" "`Shield.BreakAnim` will be displayed instead of ShieldType `BreakAnim` if" " the shield is broken by the Warhead, either through damage or " -"`Shield.Break`." -msgstr "`Shield.BreakAnim` 用于设置弹头击中护盾时替代护盾 `BreakAnim` 播放的动画。" +"`Shield.Break`." If more than one animation is listed, a random one is selected." +msgstr "`Shield.BreakAnim` 用于设置弹头击中护盾时替代护盾 `BreakAnim` 播放的动画。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:453 msgid "" "`Shield.HitAnim` will be displayed instead of ShieldType `HitAnim` if set" " when Warhead hits the shield." -msgstr "`Shield.HitAnim` 用于设置弹头击中护盾时替代护盾 `HitAnim` 播放的动画。" +" If more than one animation is listed, a random one is selected." +msgstr "`Shield.HitAnim` 用于设置弹头击中护盾时替代护盾 `HitAnim` 播放的动画。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:454 msgid "" @@ -2351,7 +2355,8 @@ msgid "" "`CreateUnit.SpawnAnim` can be used to play another animation at created " "unit's location after it has appeared. This animation has same owner and " "invoker as the parent animation." -msgstr "`CreateUnit.SpawnAnim` 可用于在创建的单位出现后在其位置播放另一个动画。此动画拥有与父动画相同的所有者和调用者。" +" If more than one animation is listed, a random one is selected." +msgstr "`CreateUnit.SpawnAnim` 可用于在创建的单位出现后在其位置播放另一个动画。此动画拥有与父动画相同的所有者和调用者。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:508 msgid "" @@ -2380,7 +2385,7 @@ msgid "" "CreateUnit.AlwaysSpawnOnGround=false ; boolean\n" "CreateUnit.SpawnParachutedInAir=false ; boolean\n" "CreateUnit.ConsiderPathfinding=false ; boolean\n" -"CreateUnit.SpawnAnim= ; AnimationType\n" +"CreateUnit.SpawnAnim= ; list of Animation\n" "CreateUnit.SpawnHeight=-1 ; integer, height in leptons\n" msgstr "" "[SOMEANIM] ; AnimationType\n" @@ -2399,7 +2404,7 @@ msgstr "" "CreateUnit.AlwaysSpawnOnGround=false ; boolean\n" "CreateUnit.SpawnParachutedInAir=false ; boolean\n" "CreateUnit.ConsiderPathfinding=false ; boolean\n" -"CreateUnit.SpawnAnim= ; AnimationType\n" +"CreateUnit.SpawnAnim= ; list of Animation\n" "CreateUnit.SpawnHeight=-1 ; integer, height in leptons\n" #: ../../New-or-Enhanced-Logics.md:531 @@ -5162,8 +5167,8 @@ msgstr "`Spawner.RecycleRange` 决定子机在距离母舰多少格以内可以 #: ../../New-or-Enhanced-Logics.md:1299 msgid "" "`Spawner.RecycleAnim` can be used to play an anim on the spawned location" -" when it is recycled." -msgstr "`Spawner.RecycleAnim` 用于设置回收时在子机位置播放的动画。" +" when it is recycled." If more than one animation is listed, a random one is selected." +msgstr "`Spawner.RecycleAnim` 用于设置回收时在子机位置播放的动画。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1300 msgid "" @@ -5186,7 +5191,7 @@ msgid "" "Spawner.AttackImmediately=false ; boolean\n" "Spawner.UseTurretFacing=false ; boolean\n" "Spawner.RecycleRange=-1 ; float, range in cells\n" -"Spawner.RecycleAnim= ; Animation\n" +"Spawner.RecycleAnim= ; list of Animation\n" "Spawner.RecycleCoord=0,0,0 ; integer - Forward,Lateral,Height\n" "Spawner.RecycleOnTurret=false ; boolean\n" msgstr "" @@ -5197,7 +5202,7 @@ msgstr "" "Spawner.AttackImmediately=false ; boolean\n" "Spawner.UseTurretFacing=false ; boolean\n" "Spawner.RecycleRange=-1 ; float, range in cells\n" -"Spawner.RecycleAnim= ; Animation\n" +"Spawner.RecycleAnim= ; list of Animation\n" "Spawner.RecycleCoord=0,0,0 ; integer - Forward,Lateral,Height\n" "Spawner.RecycleOnTurret=false ; boolean\n" @@ -5303,10 +5308,10 @@ msgstr "" msgid "" "`PassengerDeletion.ReportSound` and `PassengerDeletion.Anim` can be used " "to specify a sound and animation to play when a passenger is erased, " -"respectively." +"respectively. If more than one animation is listed, a random one is selected." msgstr "" "`PassengerDeletion.ReportSound` 和 `PassengerDeletion.Anim` " -"可用于分别指定在删除乘客时播放的声音和动画。" +"可用于分别指定在删除乘客时播放的声音和动画。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1336 msgid "" @@ -5337,7 +5342,7 @@ msgid "" "PassengerDeletion.DisplaySoylentOffset=0,0 ; X,Y, pixels relative to" " default\n" "PassengerDeletion.ReportSound= ; Sound entry\n" -"PassengerDeletion.Anim= ; AnimationType\n" +"PassengerDeletion.Anim= ; list of Animation\n" "PassengerDeletion.UnderEMP=false ; boolean\n" msgstr "" "[SOMETECHNO] ; TechnoType\n" @@ -5361,7 +5366,7 @@ msgstr "" "PassengerDeletion.DisplaySoylentOffset=0,0 ; X,Y, pixels relative to" " default\n" "PassengerDeletion.ReportSound= ; Sound entry\n" -"PassengerDeletion.Anim= ; AnimationType\n" +"PassengerDeletion.Anim= ; list of Animation\n" "PassengerDeletion.UnderEMP=false ; boolean\n" #: ../../New-or-Enhanced-Logics.md:1359 @@ -6318,9 +6323,10 @@ msgid "" "If this option is not set, the self-destruction logic will not be " "enabled. `AutoDeath.VanishAnimation` can be set to animation to play at " "object's location if `vanish` behaviour is chosen." +" If more than one animation is listed, a random one is selected." msgstr "" "如果未设置此项那么自毁逻辑将不会启用。如果选择 `vanish` 行为,那么可以使用 `AutoDeath.VanishAnimation` " -"设置自毁时播放的动画。" +"设置自毁时播放的动画。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1647 msgid "" @@ -6337,7 +6343,7 @@ msgid "" "[SOMETECHNO] ; TechnoType\n" "AutoDeath.Behavior= ; enumeration (kill | " "vanish | sell), default not set\n" -"AutoDeath.VanishAnimation= ; AnimationType\n" +"AutoDeath.VanishAnimation= ; list of Animation\n" "AutoDeath.OnAmmoDepletion=no ; boolean\n" "AutoDeath.AfterDelay=0 ; positive integer\n" "AutoDeath.TechnosDontExist= ; List of TechnoTypes\n" @@ -6354,7 +6360,7 @@ msgstr "" "[SOMETECHNO] ; TechnoType\n" "AutoDeath.Behavior= ; enumeration (kill | " "vanish | sell), default not set\n" -"AutoDeath.VanishAnimation= ; AnimationType\n" +"AutoDeath.VanishAnimation= ; list of Animation\n" "AutoDeath.OnAmmoDepletion=no ; boolean\n" "AutoDeath.AfterDelay=0 ; positive integer\n" "AutoDeath.TechnosDontExist= ; List of TechnoTypes\n" @@ -6675,17 +6681,19 @@ msgstr "现在你可以在单位或建筑升级时播放指定动画。" msgid "" "`Promote.VeteranAnimation` is used when unit or structure is promoted to " "veteran. If this is set for a TechnoType, it'll override the global " -"setting in `[AudioVisual]`." +"setting in `[AudioVisual]` If more than one animation is listed, a random one is selected." msgstr "" "`Promote.VeteranAnimation` 用于单位或建筑升级为老兵时。若为单位设置本句则覆盖全局 `[AudioVisual]` " -"中的设置。" +"中的设置。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1789 msgid "" "`Promote.EliteAnimation` is used when unit or structure is promoted to " "elite. If this is set for a TechnoType, it'll override the global setting" -" in `[AudioVisual]`." -msgstr "`Promote.EliteAnimation` 用于单位或建筑升级为精英时。若为单位设置本句则覆盖全局 `[AudioVisual]` 中的设置。" +" in `[AudioVisual]`. If more than one animation is listed, a random one is selected." +msgstr +"`Promote.EliteAnimation` 用于单位或建筑升级为精英时。若为单位设置本句则覆盖全局 `[AudioVisual]` 中的设置。" +"如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1790 msgid "" @@ -6696,23 +6704,23 @@ msgstr "如果未定义 `Promote.EliteAnimation` 则使用 `Promote.VeteranAnima #: ../../New-or-Enhanced-Logics.md:1793 msgid "" "[AudioVisual]\n" -"Promote.VeteranAnimation= ; AnimationType\n" -"Promote.EliteAnimation= ; AnimationType\n" +"Promote.VeteranAnimation= ; list of Animation\n" +"Promote.EliteAnimation= ; list of Animation\n" "\n" -"[SOMETECHNO] ; TechnoType\n" -"Promote.VeteranAnimation= ; AnimationType, default to " +"[SOMETECHNO]\n" +"Promote.VeteranAnimation= ; list of Animation, default to " "Promote.VeteranAnimation in [AudioVisual]\n" -"Promote.EliteAnimation= ; AnimationType, default to " +"Promote.EliteAnimation= ; list of Animation, default to " "Promote.EliteAnimation in [AudioVisual]\n" msgstr "" "[AudioVisual]\n" -"Promote.VeteranAnimation= ; AnimationType\n" -"Promote.EliteAnimation= ; AnimationType\n" +"Promote.VeteranAnimation= ; list of Animation\n" +"Promote.EliteAnimation= ; list of Animation\n" "\n" -"[SOMETECHNO] ; TechnoType\n" -"Promote.VeteranAnimation= ; AnimationType, default to " +"[SOMETECHNO]\n" +"Promote.VeteranAnimation= ; list of Animation, default to " "Promote.VeteranAnimation in [AudioVisual]\n" -"Promote.EliteAnimation= ; AnimationType, default to " +"Promote.EliteAnimation= ; list of Animation, default to " "Promote.EliteAnimation in [AudioVisual]\n" #: ../../New-or-Enhanced-Logics.md:1803 @@ -7291,16 +7299,17 @@ msgstr "摧毁动画与音效" msgid "" "You can now specify a destroy animation and sound for a TerrainType that " "are played when it is destroyed." -msgstr "现在你可以指定当地形对象被摧毁时播放播放的摧毁动画与音效。" +" If more than one animation is listed, a random one is selected." +msgstr "现在你可以指定当地形对象被摧毁时播放播放的摧毁动画与音效。如果列出了多个动画,则将随机选择一个。" #: ../../New-or-Enhanced-Logics.md:1986 msgid "" "[SOMETERRAINTYPE] ; TerrainType\n" -"DestroyAnim= ; AnimationType\n" +"DestroyAnim= ; list of Animation\n" "DestroySound= ; Sound entry\n" msgstr "" "[SOMETERRAINTYPE] ; TerrainType\n" -"DestroyAnim= ; AnimationType\n" +"DestroyAnim= ; list of Animation\n" "DestroySound= ; Sound entry\n" #: ../../New-or-Enhanced-Logics.md:1992 diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index d7a53d58a3..5c37f4401a 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -203,9 +203,10 @@ void AnimExt::ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool res } } -void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, +void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, const std::vector& pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom) { + bool customWakeAnim = false; AnimTypeClass* pWakeAnimToUse = nullptr; AnimTypeClass* pSplashAnimToUse = nullptr; @@ -235,8 +236,8 @@ void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWak if (!isMeteor) pWakeAnimToUse = RulesClass::Instance->Wake; - if (pWakeAnim) - pWakeAnimToUse = pWakeAnim; + if (pWakeAnim.size() > 0) + customWakeAnim = true; if (!splashAnims.empty()) { @@ -249,7 +250,11 @@ void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWak } } - if (pWakeAnimToUse) + if (customWakeAnim) + { + AnimExt::CreateRandomAnim(pWakeAnim, nLocation, nullptr, pOwner); + } + else if (pWakeAnimToUse) { auto const pWakeAnimCreated = GameCreate(pWakeAnimToUse, nLocation, 0, 1, 0x600u, false); AnimExt::SetAnimOwnerHouseKind(pWakeAnimCreated, pOwner, nullptr, false, true); diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index f74388370e..72f3726100 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -85,7 +85,7 @@ class AnimExt static HouseClass* GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner = nullptr); static void VeinAttackAI(AnimClass* pAnim); static void ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool resetLoops, bool restart); - static void HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, + static void HandleDebrisImpact(AnimTypeClass* pExpireAnim, const std::vector& pWakeAnim, Iterator splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage, CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom); static void SpawnFireAnims(AnimClass* pThis); diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index 746c8e5183..b628398c01 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -39,7 +39,7 @@ class AnimTypeExt Valueable Damage_ApplyFirepowerMult; Valueable ExplodeOnWater; Valueable Warhead_Detonate; - Valueable WakeAnim; + ValueableVector WakeAnim; NullableVector SplashAnims; Valueable SplashAnims_PickRandom; Valueable AttachedSystem; diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 2584a189dc..190cd8b2e7 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -170,8 +170,8 @@ class RulesExt Nullable DrawInsignia_AdjustPos_BuildingsAnchor; Valueable DrawInsignia_AdjustPos_Units; Valueable DrawInsignia_UsePixelSelectionBracketDelta; - Valueable Promote_VeteranAnimation; - Valueable Promote_EliteAnimation; + ValueableVector Promote_VeteranAnimation; + ValueableVector Promote_EliteAnimation; Valueable JumpjetClimbPredictHeight; Valueable JumpjetClimbWithoutCutOut; diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 045c050906..da6be50161 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -238,12 +238,11 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) // Self-destruction must be enabled const auto howToDie = pTypeExt->AutoDeath_Behavior.Get(); - const auto pVanishAnim = pTypeExt->AutoDeath_VanishAnimation; // Death if no ammo if (pType->Ammo > 0 && pThis->Ammo <= 0 && pTypeExt->AutoDeath_OnAmmoDepletion) { - TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo); + TechnoExt::KillSelf(pThis, howToDie, pTypeExt->AutoDeath_VanishAnimation, isInLimbo); return true; } @@ -256,7 +255,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) } else if (this->AutoDeathTimer.Completed()) { - TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo); + TechnoExt::KillSelf(pThis, howToDie, pTypeExt->AutoDeath_VanishAnimation, isInLimbo); return true; } } @@ -290,7 +289,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) { if (!existTechnoTypes(pTypeExt->AutoDeath_TechnosDontExist, pTypeExt->AutoDeath_TechnosDontExist_Houses, !pTypeExt->AutoDeath_TechnosDontExist_Any, pTypeExt->AutoDeath_TechnosDontExist_AllowLimboed)) { - TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo); + TechnoExt::KillSelf(pThis, howToDie, pTypeExt->AutoDeath_VanishAnimation, isInLimbo); return true; } @@ -301,7 +300,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) { if (existTechnoTypes(pTypeExt->AutoDeath_TechnosExist, pTypeExt->AutoDeath_TechnosExist_Houses, pTypeExt->AutoDeath_TechnosExist_Any, pTypeExt->AutoDeath_TechnosExist_AllowLimboed)) { - TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo); + TechnoExt::KillSelf(pThis, howToDie, pTypeExt->AutoDeath_VanishAnimation, isInLimbo); return true; } @@ -394,13 +393,7 @@ void TechnoExt::ExtData::EatPassengers() if (pDelType->ReportSound >= 0) VocClass::PlayAt(pDelType->ReportSound.Get(), pThis->GetCoords(), nullptr); - if (const auto pAnimType = pDelType->Anim.Get()) - { - auto const pAnim = GameCreate(pAnimType, pThis->Location); - pAnim->SetOwnerObject(pThis); - AnimExt::SetAnimOwnerHouseKind(pAnim, pOwner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pThis); - } + AnimExt::CreateRandomAnim(pDelType->Anim, pThis->Location, pThis, nullptr, true, true); // Check if there is money refund if (pDelType->Soylent @@ -1527,7 +1520,7 @@ void TechnoExt::ExtData::ApplyMindControlRangeLimit() } } -void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, AnimTypeClass* pVanishAnimation, bool isInLimbo) +void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, const std::vector& pVanishAnimation, bool isInLimbo) { if (isInLimbo) { @@ -1581,12 +1574,7 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, Anim case AutoDeathBehavior::Vanish: { - if (pVanishAnimation) - { - auto const pAnim = GameCreate(pVanishAnimation, pThis->GetCoords()); - AnimExt::SetAnimOwnerHouseKind(pAnim, pThis->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pThis); - } + AnimExt::CreateRandomAnim(pVanishAnimation, pThis->GetCoords(), pThis, nullptr, true); pThis->KillPassengers(pThis); pThis->Stun(); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a6cb55eb3e..3734979dd4 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -241,7 +241,7 @@ class TechnoExt static CoordStruct GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& FLHFound); static void ChangeOwnerMissionFix(FootClass* pThis); - static void KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, AnimTypeClass* pVanishAnimation, bool isInLimbo = false); + static void KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, const std::vector& pVanishAnimation, bool isInLimbo = false); static void ObjectKilledBy(TechnoClass* pThis, TechnoClass* pKiller); static void UpdateSharedAmmo(TechnoClass* pThis); static double GetCurrentSpeedMultiplier(FootClass* pThis); diff --git a/src/Ext/Techno/Hooks.Misc.cpp b/src/Ext/Techno/Hooks.Misc.cpp index 3686a59ac9..546243f977 100644 --- a/src/Ext/Techno/Hooks.Misc.cpp +++ b/src/Ext/Techno/Hooks.Misc.cpp @@ -236,15 +236,7 @@ DEFINE_HOOK(0x6B77B4, SpawnManagerClass_Update_RecycleSpawned, 0x7) if (shouldRecycleSpawned()) { - if (pCarrierTypeExt->Spawner_RecycleAnim) - { - auto const pRecycleAnim = GameCreate(pCarrierTypeExt->Spawner_RecycleAnim, spawnerCrd); - auto const pAnimExt = AnimExt::ExtMap.Find(pRecycleAnim); - auto const pSpawnOwner = pSpawner->Owner; - pAnimExt->SetInvoker(pSpawner); - AnimExt::SetAnimOwnerHouseKind(pRecycleAnim, pSpawnOwner, pSpawnOwner, false, true); - } - + AnimExt::CreateRandomAnim(pCarrierTypeExt->Spawner_RecycleAnim, spawnerCrd, pSpawner, pSpawner->Owner, true); pSpawner->SetLocation(pCarrier->GetCoords()); return Recycle; } diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 8625656023..75007b1560 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -148,27 +148,18 @@ DEFINE_HOOK(0x6F9FA9, TechnoClass_AI_PromoteAnim, 0x6) auto aresProcess = [pType]() { return (pType->Turret) ? 0x6F9FB7 : 0x6FA054; }; auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - auto const pVeteranAnim = pTypeExt->Promote_VeteranAnimation.Get(RulesExt::Global()->Promote_VeteranAnimation); - auto const pEliteAnim = pTypeExt->Promote_EliteAnimation.Get(RulesExt::Global()->Promote_EliteAnimation); + auto const pVeteranAnim = !pTypeExt->Promote_VeteranAnimation.empty() ? pTypeExt->Promote_VeteranAnimation : RulesExt::Global()->Promote_VeteranAnimation; + auto const pEliteAnim = !pTypeExt->Promote_EliteAnimation.empty() ? pTypeExt->Promote_EliteAnimation : RulesExt::Global()->Promote_EliteAnimation; - if (!pVeteranAnim && !pEliteAnim) + if (pVeteranAnim.empty() && pEliteAnim.empty()) return aresProcess(); if (pThis->CurrentRanking != pThis->Veterancy.GetRemainingLevel() && pThis->CurrentRanking != Rank::Invalid && (pThis->Veterancy.GetRemainingLevel() != Rank::Rookie)) { - AnimClass* promAnim = nullptr; - - if (pThis->Veterancy.GetRemainingLevel() == Rank::Veteran && pVeteranAnim) - promAnim = GameCreate(pVeteranAnim, pThis->GetCenterCoords()); - else if (pEliteAnim) - promAnim = GameCreate(pEliteAnim, pThis->GetCenterCoords()); - - if (promAnim) - { - promAnim->SetOwnerObject(pThis); - AnimExt::SetAnimOwnerHouseKind(promAnim, pThis->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(promAnim)->SetInvoker(pThis); - } + if (pThis->Veterancy.GetRemainingLevel() == Rank::Veteran && !pVeteranAnim.empty()) + AnimExt::CreateRandomAnim(pVeteranAnim, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true); + else if (!pEliteAnim.empty()) + AnimExt::CreateRandomAnim(pEliteAnim, pThis->GetCenterCoords(), pThis, pThis->Owner, true, true); } return aresProcess(); @@ -1299,3 +1290,25 @@ DEFINE_HOOK(0x7010C1, TechnoClass_CanShowDeployCursor_UnitsAndAircraft, 0x5) return 0; } + +// Handle customized WarpAway +DEFINE_HOOK(0x71A8BD, TemporalClass_Update_WarpAwayAnim, 0x5) +{ + GET(TemporalClass*, pThis, ESI); + + // Target must exist here + auto const pTarget = pThis->Target; + auto const pExt = TechnoExt::ExtMap.Find(pTarget)->TypeExtData; + + if (pExt->WarpAway.size() > 0) + { + AnimExt::CreateRandomAnim(pExt->WarpAway, pTarget->Location, nullptr, pTarget->Owner); + } + else if (auto const pWarpAway = RulesClass::Instance->WarpAway) + { + auto const pAnim = GameCreate(pWarpAway, pTarget->Location); + AnimExt::SetAnimOwnerHouseKind(pAnim, pTarget->Owner, nullptr, false, true); + } + + return 0x71A90E; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 260d52b713..306016e501 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -493,12 +493,7 @@ TechnoClass* TechnoTypeExt::CreateUnit(CreateUnitTypeClass* pCreateUnit, DirType if (secondaryFacing) pTechno->SecondaryFacing.SetCurrent(DirStruct(*secondaryFacing)); - if (pCreateUnit->SpawnAnim) - { - auto const pAnim = GameCreate(pCreateUnit->SpawnAnim, location); - AnimExt::SetAnimOwnerHouseKind(pAnim, pInvokerHouse, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pInvoker, pInvokerHouse); - } + AnimExt::CreateRandomAnim(pCreateUnit->SpawnAnim, location, pInvoker, pInvokerHouse, true); if (!pTechno->InLimbo) { diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index cec8af62a7..b603ab0aab 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -84,7 +84,7 @@ class TechnoTypeExt Valueable Ammo_DeployUnlockMaximumAmount; Nullable AutoDeath_Behavior; - Valueable AutoDeath_VanishAnimation; + ValueableVector AutoDeath_VanishAnimation; Valueable AutoDeath_OnAmmoDepletion; Valueable AutoDeath_AfterDelay; ValueableVector AutoDeath_TechnosDontExist; @@ -111,9 +111,9 @@ class TechnoTypeExt NullableIdx VoiceCreated; NullableIdx VoicePickup; // Used by carryalls instead of VoiceMove if set. - Nullable WarpOut; - Nullable WarpIn; - Nullable WarpAway; + ValueableVector WarpOut; + ValueableVector WarpIn; + ValueableVector WarpAway; Nullable ChronoTrigger; Nullable ChronoDistanceFactor; Nullable ChronoMinimumDelay; @@ -338,7 +338,7 @@ class TechnoTypeExt ValueableVector Spawns_Queue; Valueable Spawner_RecycleRange; - Valueable Spawner_RecycleAnim; + ValueableVector Spawner_RecycleAnim; Valueable Spawner_RecycleCoord; Valueable Spawner_RecycleOnTurret; @@ -349,8 +349,8 @@ class TechnoTypeExt Nullable ProneSpeed; Nullable DamagedSpeed; - Nullable Promote_VeteranAnimation; - Nullable Promote_EliteAnimation; + ValueableVector Promote_VeteranAnimation; + ValueableVector Promote_EliteAnimation; Nullable RadarInvisibleToHouse; diff --git a/src/Ext/TechnoType/Hooks.Teleport.cpp b/src/Ext/TechnoType/Hooks.Teleport.cpp index 46ce953da1..f3ae69bd10 100644 --- a/src/Ext/TechnoType/Hooks.Teleport.cpp +++ b/src/Ext/TechnoType/Hooks.Teleport.cpp @@ -19,7 +19,11 @@ DEFINE_HOOK(0x7193F6, TeleportLocomotionClass_ILocomotion_Process_WarpoutAnim, 0 { GET_LOCO(ESI); - if (auto const pWarpOut = pExt->WarpOut.Get(RulesClass::Instance->WarpOut)) + if (pExt->WarpOut.size() > 0) + { + AnimExt::CreateRandomAnim(pExt->WarpOut, pLinked->Location, nullptr, pLinked->Owner); + } + else if (auto const pWarpOut = RulesClass::Instance->WarpOut) { auto const pAnim = GameCreate(pWarpOut, pLinked->Location); AnimExt::SetAnimOwnerHouseKind(pAnim, pLinked->Owner, nullptr, false, true); @@ -76,7 +80,11 @@ DEFINE_HOOK(0x719742, TeleportLocomotionClass_ILocomotion_Process_WarpInAnim, 0x { GET_LOCO(ESI); - if (auto const pWarpIn = pExt->WarpIn.Get(RulesClass::Instance->WarpIn)) + if (pExt->WarpIn.size() > 0) + { + AnimExt::CreateRandomAnim(pExt->WarpIn, pLinked->Location, nullptr, pLinked->Owner); + } + else if (auto const pWarpIn = RulesClass::Instance->WarpIn) { auto const pAnim = GameCreate(pWarpIn, pLinked->Location); AnimExt::SetAnimOwnerHouseKind(pAnim, pLinked->Owner, nullptr, false, true); @@ -94,19 +102,40 @@ DEFINE_HOOK(0x719742, TeleportLocomotionClass_ILocomotion_Process_WarpInAnim, 0x return 0x719796; } -DEFINE_HOOK(0x719827, TeleportLocomotionClass_ILocomotion_Process_WarpAway, 0x5) +DEFINE_HOOK(0x719827, TeleportLocomotionClass_ILocomotion_Process_WarpOutChronoshiftAnim, 0x5) { GET_LOCO(ESI); - if (auto const pWarpAway = pExt->WarpAway.Get(RulesClass::Instance->WarpOut)) + if (pExt->WarpOut.size() > 0) + { + AnimExt::CreateRandomAnim(pExt->WarpOut, pLinked->Location, nullptr, pLinked->Owner); + } + else if (auto const pWarpOut = RulesClass::Instance->WarpOut) { - auto const pAnim = GameCreate(pWarpAway, pLinked->Location); + auto const pAnim = GameCreate(pWarpOut, pLinked->Location); AnimExt::SetAnimOwnerHouseKind(pAnim, pLinked->Owner, nullptr, false, true); } return 0x719878; } +DEFINE_HOOK(0x719B1E, TeleportLocomotionClass_ILocomotion_Process_WarpInChronoshiftAnim, 0x5) +{ + GET_LOCO(ESI); + + if (pExt->WarpIn.size() > 0) + { + AnimExt::CreateRandomAnim(pExt->WarpIn, pLinked->Location, nullptr, pLinked->Owner); + } + else if (auto const pWarpIn = RulesClass::Instance->WarpIn) + { + auto const pAnim = GameCreate(pWarpIn, pLinked->Location); + AnimExt::SetAnimOwnerHouseKind(pAnim, pLinked->Owner, nullptr, false, true); + } + + return 0x719B81; +} + DEFINE_HOOK(0x719973, TeleportLocomotionClass_ILocomotion_Process_ChronoDelay, 0x5) { GET_LOCO(ESI); diff --git a/src/Ext/TerrainType/Body.cpp b/src/Ext/TerrainType/Body.cpp index ceee2db165..b8d72c7f93 100644 --- a/src/Ext/TerrainType/Body.cpp +++ b/src/Ext/TerrainType/Body.cpp @@ -5,6 +5,7 @@ #include #include +#include #include TerrainTypeExt::ExtContainer TerrainTypeExt::ExtMap; @@ -22,9 +23,7 @@ int TerrainTypeExt::ExtData::GetCellsPerAnim() void TerrainTypeExt::ExtData::PlayDestroyEffects(const CoordStruct& coords) { VocClass::PlayIndexAtPos(this->DestroySound, coords); - - if (auto const pAnimType = this->DestroyAnim) - GameCreate(pAnimType, coords); + AnimExt::CreateRandomAnim(this->DestroyAnim, coords); } void TerrainTypeExt::Remove(TerrainClass* pTerrain) diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index 95a13afccb..4bb586ca98 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -21,7 +21,7 @@ class TerrainTypeExt Valueable SpawnsTiberium_Range; Valueable> SpawnsTiberium_GrowthStage; Valueable> SpawnsTiberium_CellsPerAnim; - Valueable DestroyAnim; + ValueableVector DestroyAnim; ValueableIdx DestroySound; Nullable MinimapColor; Valueable IsPassable; diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 6db1df34fa..3ae8246e99 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -25,7 +25,7 @@ class VoxelAnimTypeExt ValueableIdxVector LaserTrail_Types; Valueable ExplodeOnWater; Valueable Warhead_Detonate; - Valueable WakeAnim; + ValueableVector WakeAnim; NullableVector SplashAnims; Valueable SplashAnims_PickRandom; Valueable Trailer_SpawnDelay; diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index d411c9236e..75ae4d918f 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -74,8 +74,8 @@ class WarheadTypeExt Valueable Shield_Penetrate; Valueable Shield_Break; - Valueable Shield_BreakAnim; - Valueable Shield_HitAnim; + ValueableVector Shield_BreakAnim; + ValueableVector Shield_HitAnim; Valueable Shield_SkipHitAnim; Valueable Shield_HitFlash; Nullable Shield_BreakWeapon; diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 295808168d..9ab4c4955d 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -345,20 +345,13 @@ void ShieldClass::ResponseAttack() } } -void ShieldClass::WeaponNullifyAnim(AnimTypeClass* pHitAnim) +void ShieldClass::WeaponNullifyAnim(const std::vector& pHitAnim) { if (this->AreAnimsHidden) return; const auto pTechno = this->Techno; - const auto pAnimType = pHitAnim ? pHitAnim : this->Type->HitAnim; - - if (pAnimType) - { - auto const pAnim = GameCreate(pAnimType, pTechno->GetCoords()); - AnimExt::SetAnimOwnerHouseKind(pAnim, pTechno->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pTechno); - } + AnimExt::CreateRandomAnim((pHitAnim.empty() ? this->Type->HitAnim : pHitAnim), pTechno->GetCoords(), pTechno, nullptr, true, true); } bool ShieldClass::CanBeTargeted(WeaponTypeClass* pWeapon) const @@ -724,7 +717,8 @@ void ShieldClass::SelfHealing() } else if (health <= 0) { - this->BreakShield(); + std::vector nothing; + this->BreakShield(nothing); } } } @@ -741,7 +735,7 @@ int ShieldClass::GetPercentageAmount(double iStatus) return (int)std::trunc(iStatus); } -void ShieldClass::BreakShield(AnimTypeClass* pBreakAnim, WeaponTypeClass* pBreakWeapon) +void ShieldClass::BreakShield(const std::vector& pBreakAnim, WeaponTypeClass* pBreakWeapon) { this->HP = 0; auto const pType = this->Type; @@ -754,17 +748,7 @@ void ShieldClass::BreakShield(AnimTypeClass* pBreakAnim, WeaponTypeClass* pBreak this->KillAnim(); if (!this->AreAnimsHidden) - { - const auto pAnimType = pBreakAnim ? pBreakAnim : pType->BreakAnim; - - if (pAnimType) - { - auto const pAnim = GameCreate(pAnimType, pTechno->Location); - pAnim->SetOwnerObject(pTechno); - AnimExt::SetAnimOwnerHouseKind(pAnim, pTechno->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pTechno); - } - } + AnimExt::CreateRandomAnim(pBreakAnim.empty() ? pType->BreakAnim : pBreakAnim, pTechno->Location, pTechno, nullptr, true, true); const auto pWeaponType = pBreakWeapon ? pBreakWeapon : pType->BreakWeapon; this->LastBreakFrame = Unsorted::CurrentFrame; diff --git a/src/New/Entity/ShieldClass.h b/src/New/Entity/ShieldClass.h index 75e091ed79..f9ae31f477 100644 --- a/src/New/Entity/ShieldClass.h +++ b/src/New/Entity/ShieldClass.h @@ -20,7 +20,7 @@ class ShieldClass int ReceiveDamage(args_ReceiveDamage* args); bool CanBeTargeted(WeaponTypeClass* pWeapon) const; bool CanBePenetrated(WarheadTypeClass* pWarhead) const; - void BreakShield(AnimTypeClass* pBreakAnim = nullptr, WeaponTypeClass* pBreakWeapon = nullptr); + void BreakShield(const std::vector& pBreakAnim, WeaponTypeClass* pBreakWeapon = nullptr); void SetRespawn(int duration, double amount, int rate, bool resetTimer); void SetSelfHealing(int duration, double amount, int rate, bool restartInCombat, int restartInCombatDelay, bool resetTimer); @@ -92,7 +92,7 @@ class ShieldClass void UpdateIdleAnim(); AnimTypeClass* GetIdleAnimType(); - void WeaponNullifyAnim(AnimTypeClass* pHitAnim = nullptr); + void WeaponNullifyAnim(const std::vector& pHitAnim); void ResponseAttack(); void CloakCheck(); diff --git a/src/New/Type/Affiliated/CreateUnitTypeClass.h b/src/New/Type/Affiliated/CreateUnitTypeClass.h index 4abf66b645..643905adbd 100644 --- a/src/New/Type/Affiliated/CreateUnitTypeClass.h +++ b/src/New/Type/Affiliated/CreateUnitTypeClass.h @@ -19,7 +19,7 @@ class CreateUnitTypeClass Valueable AlwaysSpawnOnGround { false }; Valueable SpawnParachutedInAir { false }; Valueable ConsiderPathfinding { false }; - Valueable SpawnAnim { nullptr }; + ValueableVector SpawnAnim {}; Valueable SpawnHeight { -1 }; CreateUnitTypeClass() = default; diff --git a/src/New/Type/Affiliated/PassengerDeletionTypeClass.h b/src/New/Type/Affiliated/PassengerDeletionTypeClass.h index d35a62fcdf..32a29cd89a 100644 --- a/src/New/Type/Affiliated/PassengerDeletionTypeClass.h +++ b/src/New/Type/Affiliated/PassengerDeletionTypeClass.h @@ -28,7 +28,7 @@ class PassengerDeletionTypeClass Valueable DisplaySoylentToHouses; Valueable DisplaySoylentOffset; ValueableIdx ReportSound; - Valueable Anim; + ValueableVector Anim; Valueable UnderEMP; void LoadFromINI(CCINIClass* pINI, const char* pSection); diff --git a/src/New/Type/ShieldTypeClass.h b/src/New/Type/ShieldTypeClass.h index 3da9c70373..0f5bdca776 100644 --- a/src/New/Type/ShieldTypeClass.h +++ b/src/New/Type/ShieldTypeClass.h @@ -32,8 +32,8 @@ class ShieldTypeClass final : public Enumerable Valueable IdleAnim_TemporalAction; Damageable IdleAnim; Damageable IdleAnimDamaged; - Valueable BreakAnim; - Valueable HitAnim; + ValueableVector BreakAnim; + ValueableVector HitAnim; Valueable HitFlash; Nullable HitFlash_FixedSize; Valueable HitFlash_Red;