From eb2f59eeaa212857c909dee006deb44b05fb3546 Mon Sep 17 00:00:00 2001 From: Vantha Date: Thu, 23 Jan 2025 21:47:26 +0100 Subject: [PATCH] Allow actors to override the entity's anchor type Optionally let actor templates define their own anchor types. These override the default value defined in the template. Useful for actors without their own entity template. Reviewed-By: Itms Reviewed-On: https://gitea.wildfiregames.com/0ad/0ad/pulls/7542 --- binaries/data/mods/mod/art/actors/actor.rng | 10 ++++ source/graphics/ObjectBase.cpp | 4 ++ source/graphics/ObjectBase.h | 4 +- .../simulation2/components/CCmpPosition.cpp | 50 ++++++++++++------- .../components/CCmpVisualActor.cpp | 7 ++- source/simulation2/components/ICmpPosition.h | 10 +++- .../components/tests/test_RangeManager.h | 3 +- .../components/tests/test_TerritoryManager.h | 1 + 8 files changed, 67 insertions(+), 22 deletions(-) diff --git a/binaries/data/mods/mod/art/actors/actor.rng b/binaries/data/mods/mod/art/actors/actor.rng index c4216e83ac..8a5d9dd486 100644 --- a/binaries/data/mods/mod/art/actors/actor.rng +++ b/binaries/data/mods/mod/art/actors/actor.rng @@ -214,6 +214,16 @@ + + + + upright + pitch + roll + pitch-roll + + + diff --git a/source/graphics/ObjectBase.cpp b/source/graphics/ObjectBase.cpp index 89650a6750..719b675a0f 100644 --- a/source/graphics/ObjectBase.cpp +++ b/source/graphics/ObjectBase.cpp @@ -60,6 +60,7 @@ CObjectBase::CObjectBase(CObjectManager& objectManager, CActorDef& actorDef, u8 : m_ObjectManager(objectManager), m_ActorDef(actorDef) { m_QualityLevel = qualityLevel; + m_Properties.m_AnchorType = ""; m_Properties.m_CastShadows = false; m_Properties.m_FloatOnWater = false; @@ -82,6 +83,7 @@ bool CObjectBase::Load(const CXeromyces& XeroFile, const XMBElement& root) // Define all the elements used in the XML file #define EL(x) int el_##x = XeroFile.GetElementID(#x) #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) + EL(anchor); EL(castshadow); EL(float); EL(group); @@ -143,6 +145,8 @@ bool CObjectBase::Load(const CXeromyces& XeroFile, const XMBElement& root) return false; } } + else if (child_name == el_anchor) + m_Properties.m_AnchorType = child.GetText(); else if (child_name == el_castshadow) m_Properties.m_CastShadows = true; else if (child_name == el_float) diff --git a/source/graphics/ObjectBase.h b/source/graphics/ObjectBase.h index fecda0cfd0..e0a23d18ca 100644 --- a/source/graphics/ObjectBase.h +++ b/source/graphics/ObjectBase.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -159,6 +159,8 @@ public: struct { + // whether and how to adapt the rotation to the terrrain slope below + CStr m_AnchorType; // cast shadows from this object bool m_CastShadows; // float on top of water diff --git a/source/simulation2/components/CCmpPosition.cpp b/source/simulation2/components/CCmpPosition.cpp index 66643ea66a..baeb14b869 100644 --- a/source/simulation2/components/CCmpPosition.cpp +++ b/source/simulation2/components/CCmpPosition.cpp @@ -59,13 +59,16 @@ public: // Template state: - enum + enum class AnchorType { + UNDEFINED = -1, // Used for m_ActorAnchorType only since it's optional. UPRIGHT = 0, PITCH = 1, PITCH_ROLL = 2, ROLL = 3, - } m_AnchorType; + }; + + AnchorType m_AnchorType; bool m_Floating; entity_pos_t m_FloatDepth; @@ -94,6 +97,7 @@ public: std::set m_Turrets; // Not serialized: + AnchorType m_ActorAnchorType; float m_InterpolatedRotX, m_InterpolatedRotY, m_InterpolatedRotZ; float m_LastInterpolatedRotX, m_LastInterpolatedRotZ; bool m_ActorFloating; @@ -135,15 +139,8 @@ public: void Init(const CParamNode& paramNode) override { - const std::string& anchor = paramNode.GetChild("Anchor").ToString(); - if (anchor == "pitch") - m_AnchorType = PITCH; - else if (anchor == "pitch-roll") - m_AnchorType = PITCH_ROLL; - else if (anchor == "roll") - m_AnchorType = ROLL; - else - m_AnchorType = UPRIGHT; + m_AnchorType = ParseAnchorString(paramNode.GetChild("Anchor").ToString()); + m_ActorAnchorType = AnchorType::UNDEFINED; m_InWorld = false; @@ -199,19 +196,19 @@ public: const char* anchor = "???"; switch (m_AnchorType) { - case PITCH: + case AnchorType::PITCH: anchor = "pitch"; break; - case PITCH_ROLL: + case AnchorType::PITCH_ROLL: anchor = "pitch-roll"; break; - case ROLL: + case AnchorType::ROLL: anchor = "roll"; break; - case UPRIGHT: // upright is the default + case AnchorType::UPRIGHT: // upright is the default default: anchor = "upright"; break; @@ -493,6 +490,11 @@ public: AdvertiseInterpolatedPositionChanges(); } + void SetActorAnchor(const CStr& anchor) override + { + m_ActorAnchorType = ParseAnchorString(anchor); + } + void SetConstructionProgress(fixed progress) override { m_ConstructionProgress = progress; @@ -854,6 +856,17 @@ public: private: + AnchorType ParseAnchorString(const CStr& anchor) + { + if (anchor == "pitch") + return AnchorType::PITCH; + if (anchor == "roll") + return AnchorType::ROLL; + if (anchor == "pitch-roll") + return AnchorType::PITCH_ROLL; + return AnchorType::UPRIGHT; + } + /* * Must be called whenever m_RotY or m_InterpolatedRotY change, * to determine whether we need to call Interpolate to make the unit rotate. @@ -935,7 +948,8 @@ private: return; } - if (m_AnchorType == UPRIGHT || !m_RotZ.IsZero() || !m_RotX.IsZero()) + AnchorType anchor = m_ActorAnchorType == AnchorType::UNDEFINED ? m_AnchorType : m_ActorAnchorType; + if (anchor == AnchorType::UPRIGHT || !m_RotZ.IsZero() || !m_RotX.IsZero()) { // set the visual rotations to the ones fixed by the interface m_InterpolatedRotX = m_RotX.ToFloat(); @@ -961,10 +975,10 @@ private: normal.Z = projected.Y; // project and calculate the angles - if (m_AnchorType == PITCH || m_AnchorType == PITCH_ROLL) + if (anchor == AnchorType::PITCH || anchor == AnchorType::PITCH_ROLL) m_InterpolatedRotX = -atan2(normal.Z, normal.Y); - if (m_AnchorType == ROLL || m_AnchorType == PITCH_ROLL) + if (anchor == AnchorType::ROLL || anchor == AnchorType::PITCH_ROLL) m_InterpolatedRotZ = atan2(normal.X, normal.Y); } }; diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index 1694ce79bf..8cb19c3206 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -464,7 +464,7 @@ public: if (!m_Unit || !m_Unit->GetAnimation() || !m_Unit->GetID()) return; - m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); + m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); } void SelectMovementAnimation(const std::string& name, fixed speed) override @@ -638,10 +638,15 @@ void CCmpVisualActor::InitModel() model.ToCModelDecal()->RemoveShadowsReceive(); } + CStr anchor = m_Unit->GetObject().m_Base->m_Properties.m_AnchorType; bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater; CmpPtr cmpPosition(GetEntityHandle()); if (cmpPosition) + { + if (!anchor.empty()) + cmpPosition->SetActorAnchor(anchor); cmpPosition->SetActorFloating(floating); + } if (!m_ModelTag.valid()) { diff --git a/source/simulation2/components/ICmpPosition.h b/source/simulation2/components/ICmpPosition.h index 3ed1337a2b..ec7e8f5f19 100644 --- a/source/simulation2/components/ICmpPosition.h +++ b/source/simulation2/components/ICmpPosition.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ #include "simulation2/helpers/Position.h" #include "maths/FixedVector3D.h" #include "maths/FixedVector2D.h" +#include "ps/CStr.h" #include @@ -158,6 +159,13 @@ public: */ virtual void SetActorFloating(bool flag) = 0; + + /** + * Set the entity's anchor type, in a non-network-synchronised visual-only way. + * (This is to support the 'anchor' flag in actor XMLs.) + */ + virtual void SetActorAnchor(const CStr& anchor) = 0; + /** * Set construction progress of the model, this affects the rendered position of the model. * 0.0 will be fully underground, 1.0 will be fully visible, 0.5 will be half underground. diff --git a/source/simulation2/components/tests/test_RangeManager.h b/source/simulation2/components/tests/test_RangeManager.h index 075e656d0e..7001102673 100644 --- a/source/simulation2/components/tests/test_RangeManager.h +++ b/source/simulation2/components/tests/test_RangeManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -60,6 +60,7 @@ public: bool CanFloat() const override { return false; } void SetFloating(bool UNUSED(flag)) override { } void SetActorFloating(bool UNUSED(flag)) override { } + void SetActorAnchor(const CStr& UNUSED(anchor)) override { } void SetConstructionProgress(fixed UNUSED(progress)) override { } CFixedVector3D GetPosition() const override { return m_Pos; } CFixedVector2D GetPosition2D() const override { return CFixedVector2D(m_Pos.X, m_Pos.Z); } diff --git a/source/simulation2/components/tests/test_TerritoryManager.h b/source/simulation2/components/tests/test_TerritoryManager.h index 5c85ded49b..d50b14d42c 100644 --- a/source/simulation2/components/tests/test_TerritoryManager.h +++ b/source/simulation2/components/tests/test_TerritoryManager.h @@ -123,6 +123,7 @@ public: bool CanFloat() const override { return false; } void SetFloating(bool) override {} void SetActorFloating(bool) override {} + void SetActorAnchor(const CStr&) override {} void SetConstructionProgress(fixed) override {} CFixedVector3D GetPosition() const override { return m_Pos; } CFixedVector2D GetPosition2D() const override { return CFixedVector2D(m_Pos.X, m_Pos.Z); }