1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
22 #include "graphics/Model.h"
23 #include "graphics/ObjectBase.h"
24 #include "graphics/ObjectEntry.h"
25 #include "graphics/ObjectManager.h"
26 #include "graphics/SkeletonAnim.h"
27 #include "graphics/SkeletonAnimDef.h"
28 #include "graphics/UnitAnimation.h"
29 #include "ps/CLogger.h"
31 CUnit::CUnit(CObjectManager
& objectManager
, const CActorDef
& actor
, const entity_id_t id
, const uint32_t seed
)
32 : m_ObjectManager(objectManager
), m_Actor(actor
), m_ID(id
), m_Seed(seed
), m_Animation(nullptr)
35 * When entity selections change, we might end up with a different layout in terms of variants/groups,
36 * which means the random key calculation might end up with different results for the same seed.
37 * This is bad, as it means entities randomly change appearence when changing e.g. animation.
38 * To fix this, we'll initially pick a random and complete specification based on our seed,
39 * and then pass that as the lowest priority selections. Thus, if the actor files are properly specified,
40 * we can ensure that the entities will look the same no matter what happens.
42 SetActorSelections(m_Actor
.PickSelectionsAtRandom(m_Seed
)); // Calls ReloadObject().
50 CUnit
* CUnit::Create(const CStrW
& actorName
, const entity_id_t id
, const uint32_t seed
, CObjectManager
& objectManager
)
52 auto [success
, actor
] = objectManager
.FindActorDef(actorName
);
56 CUnit
* unit
= new CUnit(objectManager
, actor
, id
, seed
);
65 void CUnit::UpdateModel(float frameTime
)
68 m_Animation
->Update(frameTime
*1000.0f
);
71 void CUnit::SetEntitySelection(const CStr
& key
, const CStr
& selection
)
73 CStr selection_lc
= selection
.LowerCase();
75 if (m_EntitySelections
[key
] == selection_lc
)
77 m_EntitySelections
[key
] = selection_lc
;
82 void CUnit::SetEntitySelection(const std::map
<CStr
, CStr
>& selections
)
84 for (const std::pair
<const CStr
, CStr
>& s
: selections
)
85 m_EntitySelections
[s
.first
] = s
.second
.LowerCase();
90 void CUnit::SetActorSelections(const std::set
<CStr
>& selections
)
92 m_ActorSelections
= selections
;
96 void CUnit::ReloadObject()
98 std::set
<CStr
> entitySelections
;
99 for (const std::pair
<const CStr
, CStr
>& selection
: m_EntitySelections
)
100 entitySelections
.insert(selection
.second
);
101 std::vector
<std::set
<CStr
>> selections
;
102 selections
.push_back(entitySelections
);
103 selections
.push_back(m_ActorSelections
);
105 // randomly select any remain selections necessary to completely identify a variation (e.g., the new selection
106 // made might define some additional props that require a random variant choice). Also, FindObjectVariation
107 // expects the selectors passed to it to be complete.
108 // see http://trac.wildfiregames.com/ticket/979
110 // If these selections give a different object, change this unit to use it
111 // Use the entity ID as randomization seed (same as when the unit was first created)
112 CObjectEntry
* newObject
= m_ObjectManager
.FindObjectVariation(&m_Actor
, selections
, m_Seed
);
115 LOGERROR("Error loading object variation (actor: %s)", m_Actor
.GetPathname().string8());
116 // Don't delete the unit, don't override our current (valid) state.
122 m_Object
= newObject
;
123 m_Model
= newObject
->m_Model
->Clone();
124 if (m_Model
->ToCModel())
125 m_Animation
= new CUnitAnimation(m_ID
, m_Model
->ToCModel(), m_Object
);
127 else if (m_Object
&& newObject
!= m_Object
)
129 // Clone the new object's base (non-instance) model
130 std::unique_ptr
<CModelAbstract
> newModel
= newObject
->m_Model
->Clone();
132 // Copy the old instance-specific settings from the old model to the new instance
133 newModel
->SetTransform(m_Model
->GetTransform());
134 newModel
->SetPlayerID(m_Model
->GetPlayerID());
135 if (newModel
->ToCModel() && m_Model
->ToCModel())
137 newModel
->ToCModel()->CopyAnimationFrom(m_Model
->ToCModel());
139 // Copy flags that belong to this model instance (not those defined by the actor XML)
140 int instanceFlags
= (MODELFLAG_SILHOUETTE_DISPLAY
|MODELFLAG_SILHOUETTE_OCCLUDER
|MODELFLAG_IGNORE_LOS
) & m_Model
->ToCModel()->GetFlags();
141 newModel
->ToCModel()->AddFlagsRec(instanceFlags
);
144 m_Model
= std::move(newModel
);
145 m_Object
= newObject
;
147 if (m_Model
->ToCModel())
150 m_Animation
->ReloadUnit(m_Model
->ToCModel(), m_Object
); // TODO: maybe this should try to preserve animation state?
152 m_Animation
= new CUnitAnimation(m_ID
, m_Model
->ToCModel(), m_Object
);
156 SAFE_DELETE(m_Animation
);