Skip to content

Commit 7f5d96d

Browse files
committed
Add additional draw offset logic for attached and AE animations
1 parent 06e1076 commit 7f5d96d

File tree

12 files changed

+180
-7
lines changed

12 files changed

+180
-7
lines changed

docs/Fixed-or-Improved-Logics.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
5252

5353
- Vehicle to building deployers now keep their target when deploying with `DeployToFire`.
5454
- Effects like lasers are no longer drawn from wrong firing offset on weapons that use Burst.
55-
- Animations can now be offset on the X axis with `XDrawOffset`.
5655
- `IsSimpleDeployer` units now only play `DeploySound` and `UndeploySound` once, when done with (un)deploying instead of repeating it over duration of turning and/or `DeployingAnim`.
5756
- AITrigger can now recognize Building Upgrades as legal condition.
5857
- `EWGates` and `NSGates` now will link walls like `xxGateOne` and `xxGateTwo` do.
@@ -465,6 +464,23 @@ In `artmd.ini`:
465464
Crater.DestroyTiberium= ; boolean, default to [General] -> AnimCraterDestroyTiberium
466465
```
467466

467+
### Draw offset customization
468+
469+
- `XDrawOffset` can be used to adjust horizontal/X axis position of the animation.
470+
- `YDrawOffset.ApplyBracketHeight` makes Y axis position follow it's owner object's selection bracket height (for buildings, this is based on `Height` and `Foundation`, for others it is influenced by `PixelSelectionBracketDelta`) if it is attached to one.
471+
- By default this will only apply if the bracket position is negative e.g it is moved upwards from the object center. If `YDrawOffset.InvertBracketShift` is set to true, the opposite is true and negative shift is ignored.
472+
- The bracket-based shift can be further adjusted with offset from `YDrawOffset.BracketAdjust`, overridden by `YDrawOffset.BracketAdjust.Buildings` for buildings only.
473+
474+
In `artmd.ini`:
475+
```ini
476+
[SOMEANIM] ; AnimationType
477+
XDrawOffset=0,0 ; X,Y, pixels relative to default
478+
YDrawOffset.ApplyBracketHeight=false ; boolean
479+
YDrawOffset.InvertBracketShift=false ; boolean
480+
YDrawOffset.BracketAdjust=0,0 ; X,Y, pixels relative to default
481+
YDrawOffset.BracketAdjust.Buildings= ; X,Y, pixels relative to default
482+
```
483+
468484
### Fire animations spawned by Scorch & Flamer
469485

470486
- Tiberian Sun allowed `Scorch=true` and `Flamer=true` animations to spawn fire animations from `[AudioVisual] -> SmallFire` & `LargeFire`. This behaviour has been reimplemented and is fully customizable.

docs/New-or-Enhanced-Logics.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ This page describes all the engine features that are either new and introduced b
2828
- `Animation.TemporalAction` determines what happens to the animation when the attached object is under effect of `Temporal=true` Warhead.
2929
- `Animation.UseInvokerAsOwner` can be used to set the house and TechnoType that created the effect (e.g firer of the weapon that applied it) as the animation's owner & invoker instead of the object the effect is attached to.
3030
- `Animation.HideIfAttachedWith` contains list of other AttachEffectTypes that if attached to same techno as the current one, will hide this effect's animation.
31+
- `Animation.DrawOffsetN` (where N is integer starting from 0) can be used to define draw offset rules for the animation. These are parsed starting from index 0 until offset with value 0,0 is encountered.
32+
- `Animation.DrawOffsetN.RequiredTypes` contains list other AttachEffectTypes that need to be attached on the same techno as the current one for the draw offset rule to apply.
3133
- `CumulativeAnimations` can be used to declare a list of animations used for `Cumulative=true` types instead of `Animation`. An animation is picked from the list in order matching the number of active instances of the type on the object, with last listed animation used if number is higher than the number of listed animations. This animation is only displayed once and is transferred from the effect to another of same type (specifically one with longest remaining duration), if such exists, upon expiration or removal. Note that because `Cumulative.MaxCount` limits the number of effects of same type that can be applied this can cause animations to 'flicker' here as effects expire before new ones can be applied in some circumstances.
3234
- `CumulativeAnimations.RestartOnChange` determines if the animation playback is restarted when the type of animation changes, if not then playback resumes at frame at same position relative to the animation's length.
3335
- Attached effect can fire off a weapon when expired / removed / object dies by setting `ExpireWeapon`.
@@ -103,6 +105,8 @@ Animation.OfflineAction=Hides ; AttachedAnimFlag (None, Hid
103105
Animation.TemporalAction=None ; AttachedAnimFlag (None, Hides, Temporal, Paused or PausedTemporal)
104106
Animation.UseInvokerAsOwner=false ; boolean
105107
Animation.HideIfAttachedWith= ; List of AttachEffectTypes
108+
Animation.DrawOffsetN=0,0 ; X,Y, pixels relative to default
109+
Animation.DrawOffsetN.RequiredTypes= ; List of AttachEffectTypes
106110
CumulativeAnimations= ; List of AnimationTypes
107111
CumulativeAnimations.RestartOnChange=true ; boolean
108112
ExpireWeapon= ; WeaponType

src/Ext/Anim/Body.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ void AnimExt::ExtData::Serialize(T& Stm)
411411
.Process(this->DelayedFireRemoveOnNoDelay)
412412
.Process(this->IsAttachedEffectAnim)
413413
.Process(this->IsShieldIdleAnim)
414+
.Process(this->AEDrawOffset)
414415
;
415416
}
416417

src/Ext/Anim/Body.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class AnimExt
3131
bool DelayedFireRemoveOnNoDelay;
3232
bool IsAttachedEffectAnim;
3333
bool IsShieldIdleAnim;
34+
Point2D AEDrawOffset;
3435

3536
ExtData(AnimClass* OwnerObject) : Extension<AnimClass>(OwnerObject)
3637
, DeathUnitFacing { 0 }
@@ -45,6 +46,7 @@ class AnimExt
4546
, DelayedFireRemoveOnNoDelay { false }
4647
, IsAttachedEffectAnim { false }
4748
, IsShieldIdleAnim { false }
49+
, AEDrawOffset { Point2D::Empty }
4850
{ }
4951

5052
void SetInvoker(TechnoClass* pInvoker);

src/Ext/Anim/Hooks.cpp

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,57 @@ DEFINE_HOOK(0x424CF1, AnimClass_Start_DetachedReport, 0x6)
266266
}
267267

268268
// 0x422CD8 is in an alternate code path only used by anims with ID RING1, unused normally but covering it just because
269-
DEFINE_HOOK_AGAIN(0x422CD8, AnimClass_DrawIt_XDrawOffset, 0x6)
270-
DEFINE_HOOK(0x423122, AnimClass_DrawIt_XDrawOffset, 0x6)
269+
DEFINE_HOOK_AGAIN(0x422CD8, AnimClass_DrawIt_DrawOffset, 0x6)
270+
DEFINE_HOOK(0x423122, AnimClass_DrawIt_DrawOffset, 0x6)
271271
{
272272
GET(AnimClass* const, pThis, ESI);
273273
GET_STACK(Point2D*, pLocation, STACK_OFFSET(0x110, 0x4));
274274

275-
if (auto const pTypeExt = AnimTypeExt::ExtMap.TryFind(pThis->Type))
276-
pLocation->X += pTypeExt->XDrawOffset;
275+
auto const pTypeExt = AnimTypeExt::ExtMap.TryFind(pThis->Type);
276+
pLocation->X += pTypeExt->XDrawOffset;
277+
278+
if (pTypeExt->YDrawOffset_ApplyBracketHeight && pThis->OwnerObject && pThis->OwnerObject->AbstractFlags & AbstractFlags::Techno)
279+
{
280+
// Le magic number.
281+
constexpr int SHIELD_HEALTHBAR_OFFSET = -3;
282+
283+
auto const pTechno = static_cast<TechnoClass*>(pThis->OwnerObject);
284+
bool inverse = pTypeExt->YDrawOffset_InvertBracketShift;
285+
286+
if (auto const pBuilding = abstract_cast<BuildingClass*>(pTechno))
287+
{
288+
auto const pType = pBuilding->Type;
289+
290+
if ((pType->Height >= 0 && !inverse) || (pType->Height < 0 && inverse))
291+
{
292+
auto const pos = TechnoExt::GetBuildingSelectBracketPosition(pBuilding, BuildingSelectBracketPosition::Top);
293+
pLocation->Y = pos.Y + pTypeExt->YDrawOffset_BracketAdjust_Buildings.Get(pTypeExt->YDrawOffset_BracketAdjust);
294+
}
295+
}
296+
else
297+
{
298+
auto const pType = pTechno->GetTechnoType();
299+
300+
if ((pType->PixelSelectionBracketDelta <= 0 && !inverse) || (pType->PixelSelectionBracketDelta > 0 && inverse))
301+
{
302+
auto const pos = TechnoExt::GetFootSelectBracketPosition(pTechno, Anchor(HorizontalPosition::Left, VerticalPosition::Top));
303+
pLocation->Y = pos.Y + pType->PixelSelectionBracketDelta + pTypeExt->YDrawOffset_BracketAdjust;
304+
}
305+
}
306+
307+
if (auto const pShield = TechnoExt::ExtMap.Find(pTechno)->Shield.get())
308+
{
309+
auto const pShieldType = pShield->GetType();
310+
311+
if (pShield->IsAvailable() && !pShield->IsBrokenAndNonRespawning() && (pShield->GetHealthRatio() > 0.0 || !pShieldType->Pips_HideIfNoStrength))
312+
{
313+
if ((pShieldType->BracketDelta <= 0 && !inverse) || (pShieldType->BracketDelta > 0 && inverse))
314+
pLocation->Y += pShieldType->BracketDelta + SHIELD_HEALTHBAR_OFFSET;
315+
}
316+
}
317+
}
318+
319+
*pLocation += AnimExt::ExtMap.Find(pThis)->AEDrawOffset;
277320

278321
return 0;
279322
}

src/Ext/AnimType/Body.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ void AnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI)
8787

8888
this->Palette.LoadFromINI(pINI, pID, "CustomPalette");
8989
this->XDrawOffset.Read(exINI, pID, "XDrawOffset");
90+
this->YDrawOffset_ApplyBracketHeight.Read(exINI, pID, "YDrawOffset.ApplyBracketHeight");
91+
this->YDrawOffset_InvertBracketShift.Read(exINI, pID, "YDrawOffset.InvertBracketShift");
92+
this->YDrawOffset_BracketAdjust.Read(exINI, pID, "YDrawOffset.BracketAdjust");
93+
this->YDrawOffset_BracketAdjust_Buildings.Read(exINI, pID, "YDrawOffset.BracketAdjust.Buildings");
9094
this->HideIfNoOre_Threshold.Read(exINI, pID, "HideIfNoOre.Threshold");
9195
this->Layer_UseObjectLayer.Read(exINI, pID, "Layer.UseObjectLayer");
9296
this->AttachedAnimPosition.Read(exINI, pID, "AttachedAnimPosition");
@@ -146,6 +150,10 @@ void AnimTypeExt::ExtData::Serialize(T& Stm)
146150
.Process(this->Palette)
147151
.Process(this->CreateUnitType)
148152
.Process(this->XDrawOffset)
153+
.Process(this->YDrawOffset_ApplyBracketHeight)
154+
.Process(this->YDrawOffset_InvertBracketShift)
155+
.Process(this->YDrawOffset_BracketAdjust)
156+
.Process(this->YDrawOffset_BracketAdjust_Buildings)
149157
.Process(this->HideIfNoOre_Threshold)
150158
.Process(this->Layer_UseObjectLayer)
151159
.Process(this->AttachedAnimPosition)

src/Ext/AnimType/Body.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class AnimTypeExt
2929
CustomPalette Palette;
3030
std::unique_ptr<CreateUnitTypeClass> CreateUnitType;
3131
Valueable<int> XDrawOffset;
32+
Valueable<bool> YDrawOffset_ApplyBracketHeight;
33+
Valueable<bool> YDrawOffset_InvertBracketShift;
34+
Valueable<int> YDrawOffset_BracketAdjust;
35+
Nullable<int> YDrawOffset_BracketAdjust_Buildings;
3236
Valueable<int> HideIfNoOre_Threshold;
3337
Nullable<bool> Layer_UseObjectLayer;
3438
Valueable<AttachedAnimPosition> AttachedAnimPosition;
@@ -68,6 +72,8 @@ class AnimTypeExt
6872
, Palette { CustomPalette::PaletteMode::Temperate }
6973
, CreateUnitType { nullptr }
7074
, XDrawOffset { 0 }
75+
, YDrawOffset_ApplyBracketHeight { false }
76+
, YDrawOffset_InvertBracketShift { false }
7177
, HideIfNoOre_Threshold { 0 }
7278
, Layer_UseObjectLayer {}
7379
, AttachedAnimPosition { AttachedAnimPosition::Default }

src/Ext/Techno/Body.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ bool TechnoExt::IsTypeImmune(TechnoClass* pThis, TechnoClass* pSource)
489489
/// <param name="pInvoker">Invoker Techno used for same source check.</param>
490490
/// <param name="pSource">Source AbstractClass instance used for same source check.</param>
491491
/// <returns>True if techno has active AttachEffects that satisfy the source, false if not.</returns>
492-
bool TechnoExt::ExtData::HasAttachedEffects(std::vector<AttachEffectTypeClass*> attachEffectTypes, bool requireAll, bool ignoreSameSource,
492+
bool TechnoExt::ExtData::HasAttachedEffects(std::vector<AttachEffectTypeClass*> const& attachEffectTypes, bool requireAll, bool ignoreSameSource,
493493
TechnoClass* pInvoker, AbstractClass* pSource, std::vector<int> const* minCounts, std::vector<int> const* maxCounts) const
494494
{
495495
unsigned int foundCount = 0;

src/Ext/Techno/Body.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class TechnoExt
200200
void InitializeLaserTrails();
201201
void InitializeAttachEffects();
202202
void UpdateSelfOwnedAttachEffects();
203-
bool HasAttachedEffects(std::vector<AttachEffectTypeClass*> attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector<int> const* minCounts, std::vector<int> const* maxCounts) const;
203+
bool HasAttachedEffects(std::vector<AttachEffectTypeClass*> const& attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector<int> const* minCounts, std::vector<int> const* maxCounts) const;
204204
int GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource = false, TechnoClass* pInvoker = nullptr, AbstractClass* pSource = nullptr) const;
205205
void InitializeDisplayInfo();
206206
void ApplyMindControlRangeLimit();

src/New/Entity/AttachEffectClass.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,19 @@ void AttachEffectClass::AnimCheck()
310310
this->CreateAnim();
311311
}
312312
}
313+
314+
if (this->Animation && this->Type->Animation_DrawOffsets.size() > 0)
315+
{
316+
auto const pAnimExt = AnimExt::ExtMap.Find(this->Animation);
317+
auto const pTechnoExt = TechnoExt::ExtMap.Find(this->Techno);
318+
pAnimExt->AEDrawOffset = Point2D::Empty;
319+
320+
for (auto const& drawOffset : this->Type->Animation_DrawOffsets)
321+
{
322+
if (drawOffset.RequiredTypes.size() < 1 || pTechnoExt->HasAttachedEffects(drawOffset.RequiredTypes, false, false, nullptr, nullptr, nullptr, nullptr))
323+
pAnimExt->AEDrawOffset += drawOffset.Offset;
324+
}
325+
}
313326
}
314327

315328
void AttachEffectClass::OnlineCheck()

0 commit comments

Comments
 (0)