forked from 0ad/0ad
Refactored actor variation system, and added support for entity-level selections (controlled by the current animation).
Avoided tooltip error message. Avoided noisy warnings when textures fail to load. This was SVN commit r3653.
This commit is contained in:
parent
6eda8c2209
commit
d3f57744d9
@ -205,7 +205,8 @@ int CMapReader::ApplyData()
|
||||
// loaded on demand)
|
||||
}
|
||||
|
||||
CUnit* unit = g_UnitMan.CreateUnit(m_ObjectTypes.at(m_Objects[i].m_ObjectIndex), NULL);
|
||||
std::set<CStrW> selections; // TODO: read from file
|
||||
CUnit* unit = g_UnitMan.CreateUnit(m_ObjectTypes.at(m_Objects[i].m_ObjectIndex), NULL, selections);
|
||||
|
||||
if (unit)
|
||||
{
|
||||
@ -428,7 +429,9 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to load entity template '%ls'", TemplateName.c_str());
|
||||
else
|
||||
{
|
||||
HEntity ent = g_EntityManager.create(base, Position, Orientation);
|
||||
std::set<CStrW> selections; // TODO: read from file
|
||||
|
||||
HEntity ent = g_EntityManager.create(base, Position, Orientation, selections);
|
||||
|
||||
if (! ent)
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", TemplateName.c_str());
|
||||
@ -492,7 +495,9 @@ int CXMLReader::ReadNonEntities(XMBElement parent, double end_time)
|
||||
debug_warn("Invalid XML data - DTD shouldn't allow this");
|
||||
}
|
||||
|
||||
CUnit* unit = g_UnitMan.CreateUnit(ActorName, NULL);
|
||||
std::set<CStrW> selections; // TODO: read from file
|
||||
|
||||
CUnit* unit = g_UnitMan.CreateUnit(ActorName, NULL, selections);
|
||||
|
||||
if (unit)
|
||||
{
|
||||
|
@ -133,7 +133,7 @@ static CVector3D SkinPoint(const CVector3D& pos,const SVertexBlend& blend,
|
||||
void CModel::CalcBounds()
|
||||
{
|
||||
// Need to calculate the object bounds first, if that hasn't already been done
|
||||
if (! m_Anim)
|
||||
if (! (m_Anim && m_Anim->m_AnimDef))
|
||||
CalcObjectBounds();
|
||||
else
|
||||
{
|
||||
@ -238,7 +238,7 @@ CSkeletonAnim* CModel::BuildAnimation(const char* filename, const char* name, fl
|
||||
// Update: update this model by the given time, in seconds
|
||||
void CModel::Update(float time)
|
||||
{
|
||||
if (m_Anim && m_BoneMatrices) {
|
||||
if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices) {
|
||||
// adjust for animation speed
|
||||
float animtime=time*m_AnimSpeed;
|
||||
|
||||
@ -251,13 +251,10 @@ void CModel::Update(float time)
|
||||
|
||||
float duration=m_Anim->m_AnimDef->GetDuration();
|
||||
if (m_AnimTime > duration) {
|
||||
if( m_Flags & MODELFLAG_NOLOOPANIMATION )
|
||||
{
|
||||
SetAnimation( m_NextAnim );
|
||||
}
|
||||
if (m_Flags & MODELFLAG_NOLOOPANIMATION)
|
||||
SetAnimation(m_NextAnim);
|
||||
else
|
||||
m_AnimTime=(float) fmod(m_AnimTime,duration);
|
||||
|
||||
m_AnimTime = (float) fmod(m_AnimTime, duration);
|
||||
}
|
||||
|
||||
// mark vertices as dirty
|
||||
@ -366,18 +363,25 @@ bool CModel::SetAnimation(CSkeletonAnim* anim, bool once, float speed, CSkeleton
|
||||
|
||||
if (anim) {
|
||||
m_Flags &= ~MODELFLAG_NOLOOPANIMATION;
|
||||
|
||||
if (once)
|
||||
{
|
||||
m_Flags |= MODELFLAG_NOLOOPANIMATION;
|
||||
m_NextAnim = next;
|
||||
}
|
||||
|
||||
if (!m_BoneMatrices) {
|
||||
if (!m_BoneMatrices && anim->m_AnimDef) {
|
||||
// not boned, can't animate
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones()) {
|
||||
if (m_BoneMatrices && !anim->m_AnimDef) {
|
||||
// boned, but animation isn't valid
|
||||
// (e.g. the default (static) idle animation on an animated unit)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anim->m_AnimDef && anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones()) {
|
||||
// mismatch between model's skeleton and animation's skeleton
|
||||
LOG(ERROR, LOG_CATEGORY, "Mismatch between model's skeleton and animation's skeleton (%d model bones != %d animation keys)",
|
||||
m_pModelDef->GetNumBones(), anim->m_AnimDef->GetNumKeys());
|
||||
|
@ -55,7 +55,8 @@ public:
|
||||
void SetTexture(const CTexture& tex) { m_Texture=tex; }
|
||||
// set the model's material
|
||||
void SetMaterial(const CMaterial &material);
|
||||
// set the model's player ID, recursively through props
|
||||
// set the model's player ID, recursively through props. CUnit::SetPlayerID
|
||||
// should normally be used instead.
|
||||
void SetPlayerID(int id);
|
||||
// set the model's player colour
|
||||
void SetPlayerColor(CColor& colour);
|
||||
@ -69,7 +70,7 @@ public:
|
||||
CColor GetShadingColor() { return m_ShadingColor; }
|
||||
|
||||
// set the given animation as the current animation on this model
|
||||
bool SetAnimation(CSkeletonAnim* anim, bool once = false, float speed = 1000.0f, CSkeletonAnim* next = NULL );
|
||||
bool SetAnimation(CSkeletonAnim* anim, bool once = false, float speed = 1000.0f, CSkeletonAnim* next = NULL);
|
||||
|
||||
// get the currently playing animation, if any
|
||||
CSkeletonAnim* GetAnimation() { return m_Anim; }
|
||||
|
@ -209,17 +209,19 @@ bool CObjectBase::Load(const char* filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CObjectBase::CalculateVariation(std::set<CStr>& strings, variation_key& choices)
|
||||
std::vector<u8> CObjectBase::CalculateVariationKey(const std::vector<std::set<CStrW> >& selections)
|
||||
{
|
||||
// Calculate a complete list of choices, one per group. In each group,
|
||||
// if one of the variants has a name matching a string in 'strings', use
|
||||
// that one. If more than one matches, choose randomly from those matching
|
||||
// ones. If none match, choose randomly from all variants.
|
||||
//
|
||||
// When choosing randomly, make use of each variant's frequency. If all
|
||||
// variants have frequency 0, treat them as if they were 1.
|
||||
// Calculate a complete list of choices, one per group, based on the
|
||||
// supposedly-complete selections (i.e. not making random choices at this
|
||||
// stage).
|
||||
// In each group, if one of the variants has a name matching a string in the
|
||||
// first 'selections', set use that one.
|
||||
// Otherwise, try with the next (lower priority) selections set, and repeat.
|
||||
// Otherwise, choose the first variant (arbitrarily).
|
||||
|
||||
choices.clear();
|
||||
std::vector<u8> choices;
|
||||
|
||||
std::map<CStr, CStr> chosenProps;
|
||||
|
||||
for (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_Variants.begin();
|
||||
grp != m_Variants.end();
|
||||
@ -230,103 +232,175 @@ void CObjectBase::CalculateVariation(std::set<CStr>& strings, variation_key& cho
|
||||
if (grp->size() == 0)
|
||||
continue;
|
||||
|
||||
int match = -1; // -1 => none found yet
|
||||
|
||||
// If there's only a single variant, choose that one
|
||||
if (grp->size() == 1)
|
||||
{
|
||||
choices.push_back(0);
|
||||
continue;
|
||||
match = 0;
|
||||
}
|
||||
|
||||
// Determine the variants that match the provided strings:
|
||||
|
||||
std::vector<u8> matches;
|
||||
typedef std::vector<u8>::const_iterator Iter;
|
||||
|
||||
debug_assert(grp->size() < 256); // else they won't fit in the vector
|
||||
|
||||
for (uint i = 0; i < grp->size(); ++i)
|
||||
if (strings.count((*grp)[i].m_VariantName))
|
||||
matches.push_back((u8)i); // "protected" by debug_assert
|
||||
|
||||
// If there's only one match, choose that one
|
||||
if (matches.size() == 1)
|
||||
else
|
||||
{
|
||||
choices.push_back(matches[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, choose randomly from the others.
|
||||
// Determine the first variant that matches the provided strings,
|
||||
// starting with the highest priority selections set:
|
||||
|
||||
// If none matched the specified strings, choose from all the variants
|
||||
if (matches.size() == 0)
|
||||
for (uint i = 0; i < grp->size(); ++i)
|
||||
matches.push_back((u8)i); // "protected" by debug_assert
|
||||
|
||||
// Sum the frequencies:
|
||||
int totalFreq = 0;
|
||||
for (Iter it = matches.begin(); it != matches.end(); ++it)
|
||||
totalFreq += (*grp)[*it].m_Frequency;
|
||||
|
||||
// Someone might be silly and set all variants to have freq==0, in
|
||||
// which case we just pretend they're all 1
|
||||
bool allZero = false;
|
||||
if (totalFreq == 0)
|
||||
{
|
||||
totalFreq = (int)matches.size();
|
||||
allZero = true;
|
||||
}
|
||||
|
||||
// Choose a random number in the interval [0..totalFreq).
|
||||
// (It shouldn't be necessary to use a network-synchronised RNG,
|
||||
// since actors are meant to have purely visual manifestations.)
|
||||
int randNum = rand(0, totalFreq);
|
||||
|
||||
// and use that to choose one of the variants
|
||||
for (Iter it = matches.begin(); it != matches.end(); ++it)
|
||||
{
|
||||
randNum -= (allZero ? 1 : (*grp)[*it].m_Frequency);
|
||||
if (randNum < 0)
|
||||
for (std::vector<std::set<CStrW> >::const_iterator selset = selections.begin(); selset < selections.end(); ++selset)
|
||||
{
|
||||
choices.push_back(*it);
|
||||
break;
|
||||
debug_assert(grp->size() < 256); // else they won't fit in 'choices'
|
||||
|
||||
for (size_t i = 0; i < grp->size(); ++i)
|
||||
{
|
||||
if (selset->count((*grp)[i].m_VariantName))
|
||||
{
|
||||
match = (u8)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop after finding the first match
|
||||
if (match != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
// If no match, just choose the first
|
||||
if (match == -1)
|
||||
match = 0;
|
||||
}
|
||||
|
||||
debug_assert(randNum < 0);
|
||||
// This should always happen; otherwise it
|
||||
// wouldn't have chosen any of the variants.
|
||||
}
|
||||
choices.push_back(match);
|
||||
|
||||
debug_assert(choices.size() == m_Variants.size());
|
||||
|
||||
|
||||
// Also, make choices for all props:
|
||||
|
||||
// Work out which props have been chosen
|
||||
std::map<CStr, CStr> chosenProps;
|
||||
CObjectBase::variation_key::const_iterator choice_it = choices.begin();
|
||||
for (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_Variants.begin();
|
||||
grp != m_Variants.end();
|
||||
++grp)
|
||||
{
|
||||
CObjectBase::Variant& var (grp->at(*(choice_it++)));
|
||||
// Remember which props were chosen. (Later-defined props override
|
||||
// earlier props at the same prop point.)
|
||||
CObjectBase::Variant& var ((*grp)[match]);
|
||||
for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
|
||||
{
|
||||
chosenProps[it->m_PropPointName] = it->m_ModelName;
|
||||
}
|
||||
}
|
||||
|
||||
// Load each prop, and call CalculateVariation on them:
|
||||
// Load each prop, and add their CalculateVariationKey to our key:
|
||||
for (std::map<CStr, CStr>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)
|
||||
{
|
||||
CObjectBase* prop = g_ObjMan.FindObjectBase(it->second);
|
||||
if (prop)
|
||||
{
|
||||
variation_key propChoices;
|
||||
prop->CalculateVariation(strings, propChoices);
|
||||
std::vector<u8> propChoices = prop->CalculateVariationKey(selections);
|
||||
choices.insert(choices.end(), propChoices.begin(), propChoices.end());
|
||||
}
|
||||
}
|
||||
|
||||
// (TODO: This seems rather fragile, e.g. if props fail to load)
|
||||
return choices;
|
||||
}
|
||||
|
||||
std::set<CStrW> CObjectBase::CalculateRandomVariation(const std::set<CStrW>& initialSelections)
|
||||
{
|
||||
std::set<CStrW> selections = initialSelections;
|
||||
|
||||
std::map<CStr, CStr> chosenProps;
|
||||
|
||||
// Calculate a complete list of selections, so there is at least one
|
||||
// (and in most cases only one) per group.
|
||||
// In each group, if one of the variants has a name matching a string in
|
||||
// 'selections', use that one.
|
||||
// If more than one matches, choose randomly from those matching ones.
|
||||
// If none match, choose randomly from all variants.
|
||||
//
|
||||
// When choosing randomly, make use of each variant's frequency. If all
|
||||
// variants have frequency 0, treat them as if they were 1.
|
||||
|
||||
for (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_Variants.begin();
|
||||
grp != m_Variants.end();
|
||||
++grp)
|
||||
{
|
||||
// Ignore groups with nothing inside. (A warning will have been
|
||||
// emitted by the loading code.)
|
||||
if (grp->size() == 0)
|
||||
continue;
|
||||
|
||||
int match = -1; // -1 => none found yet
|
||||
|
||||
// If there's only a single variant, choose that one
|
||||
if (grp->size() == 1)
|
||||
{
|
||||
match = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if a variant (or several, but we only care about the first)
|
||||
// is already matched by the selections we've made
|
||||
|
||||
for (size_t i = 0; i < grp->size(); ++i)
|
||||
{
|
||||
if (selections.count((*grp)[i].m_VariantName))
|
||||
{
|
||||
match = (int)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there was one, we don't need to do anything now because there's
|
||||
// already something to choose. Otherwise, choose randomly from the others.
|
||||
if (match == -1)
|
||||
{
|
||||
// Sum the frequencies
|
||||
int totalFreq = 0;
|
||||
for (size_t i = 0; i < grp->size(); ++i)
|
||||
totalFreq += (*grp)[i].m_Frequency;
|
||||
|
||||
// Someone might be silly and set all variants to have freq==0, in
|
||||
// which case we just pretend they're all 1
|
||||
bool allZero = (totalFreq == 0);
|
||||
if (allZero) totalFreq = (int)grp->size();
|
||||
|
||||
// Choose a random number in the interval [0..totalFreq).
|
||||
// (It shouldn't be necessary to use a network-synchronised RNG,
|
||||
// since actors are meant to have purely visual manifestations.)
|
||||
int randNum = rand(0, totalFreq);
|
||||
|
||||
// and use that to choose one of the variants
|
||||
for (size_t i = 0; i < grp->size(); ++i)
|
||||
{
|
||||
randNum -= (allZero ? 1 : (*grp)[i].m_Frequency);
|
||||
if (randNum < 0)
|
||||
{
|
||||
selections.insert((*grp)[i].m_VariantName);
|
||||
// (If this change to 'selections' interferes with earlier
|
||||
// choices, then we'll get some non-fatal inconsistencies
|
||||
// that just break the randomness. But that shouldn't
|
||||
// happen, much.)
|
||||
match = (int)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug_assert(randNum < 0);
|
||||
// This should always succeed; otherwise it
|
||||
// wouldn't have chosen any of the variants.
|
||||
}
|
||||
}
|
||||
|
||||
// Remember which props were chosen. (Later-defined props override
|
||||
// earlier props at the same prop point.)
|
||||
CObjectBase::Variant& var ((*grp)[match]);
|
||||
for (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)
|
||||
{
|
||||
chosenProps[it->m_PropPointName] = it->m_ModelName;
|
||||
}
|
||||
}
|
||||
|
||||
// Load each prop, and add their required selections to ours:
|
||||
for (std::map<CStr, CStr>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)
|
||||
{
|
||||
CObjectBase* prop = g_ObjMan.FindObjectBase(it->second);
|
||||
if (prop)
|
||||
{
|
||||
std::set<CStrW> propSelections = prop->CalculateRandomVariation(selections);
|
||||
std::set<CStrW> newSelections;
|
||||
std::set_union(propSelections.begin(), propSelections.end(),
|
||||
selections.begin(), selections.end(),
|
||||
std::inserter(newSelections, newSelections.begin()));
|
||||
selections.swap(newSelections);
|
||||
}
|
||||
}
|
||||
|
||||
return selections;
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ public:
|
||||
|
||||
std::vector< std::vector<Variant> > m_Variants;
|
||||
|
||||
typedef std::vector<u8> variation_key;
|
||||
std::vector<u8> CalculateVariationKey(const std::vector<std::set<CStrW> >& selections);
|
||||
|
||||
void CalculateVariation(std::set<CStr>& strings, variation_key& variation);
|
||||
std::set<CStrW> CalculateRandomVariation(const std::set<CStrW>& initialSelections);
|
||||
|
||||
bool Load(const char* filename);
|
||||
|
||||
|
@ -36,47 +36,61 @@ CObjectEntry::~CObjectEntry()
|
||||
delete m_Model;
|
||||
}
|
||||
|
||||
bool CObjectEntry::BuildRandomVariant(const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it)
|
||||
{
|
||||
// vars_it is passed by reference so that the caller's iterator
|
||||
// can be incremented by the appropriate amount, to point to the
|
||||
// next object's set of variant choices (for propped models).
|
||||
|
||||
bool CObjectEntry::BuildVariation(const std::vector<std::set<CStrW> >& selections)
|
||||
{
|
||||
CStr chosenTexture;
|
||||
CStr chosenModel;
|
||||
CStr chosenColor;
|
||||
std::map<CStr, CObjectBase::Prop> chosenProps;
|
||||
std::multimap<CStr, CObjectBase::Anim> chosenAnims;
|
||||
|
||||
// For each group in m_Base->m_Variants, take whichever variant is specified
|
||||
// by 'vars', and then store its data into the 'chosen' variables. If data
|
||||
// is specified more than once, the last value overrides all previous ones.
|
||||
|
||||
for (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_Base->m_Variants.begin();
|
||||
grp != m_Base->m_Variants.end();
|
||||
++grp)
|
||||
{
|
||||
if (vars_it == vars.end())
|
||||
// Ignore groups with nothing inside. (A warning will have been
|
||||
// emitted by the loading code.)
|
||||
if (grp->size() == 0)
|
||||
continue;
|
||||
|
||||
int match = -1; // -1 => none found yet
|
||||
|
||||
// If there's only a single variant, choose that one
|
||||
if (grp->size() == 1)
|
||||
{
|
||||
debug_warn("BuildRandomVariant is using too many vars");
|
||||
return false;
|
||||
match = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine the first variant that matches the provided strings,
|
||||
// starting with the highest priority selections set:
|
||||
|
||||
for (std::vector<std::set<CStrW> >::const_iterator selset = selections.begin(); selset < selections.end(); ++selset)
|
||||
{
|
||||
debug_assert(grp->size() < 256); // else they won't fit in 'choices'
|
||||
|
||||
for (size_t i = 0; i < grp->size(); ++i)
|
||||
{
|
||||
if (selset->count((*grp)[i].m_VariantName))
|
||||
{
|
||||
match = (u8)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop after finding the first match
|
||||
if (match != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
// If no match, just choose the first
|
||||
if (match == -1)
|
||||
match = 0;
|
||||
}
|
||||
|
||||
// Get the correct variant
|
||||
u8 var_id = *vars_it++;
|
||||
if (var_id >= grp->size())
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "Internal error (BuildRandomVariant: %d not in 0..%d)", var_id, grp->size()-1);
|
||||
// Carry on as best we can, by using some arbitrary variant (rather
|
||||
// than choosing none, else we might end up with no model or texture)
|
||||
if (grp->size())
|
||||
var_id = 0;
|
||||
else
|
||||
// ... unless there aren't any variants in this group, in which
|
||||
// case just give up and try the next group
|
||||
continue;
|
||||
}
|
||||
CObjectBase::Variant& var ((*grp)[var_id]);
|
||||
// Get the matched variant
|
||||
CObjectBase::Variant& var ((*grp)[match]);
|
||||
|
||||
// Apply its data:
|
||||
|
||||
@ -124,12 +138,9 @@ bool CObjectEntry::BuildRandomVariant(const CObjectBase::variation_key& vars, CO
|
||||
|
||||
for (std::map<CStr, CObjectBase::Prop>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)
|
||||
props.push_back(it->second);
|
||||
// TODO: This is all wrong, since it breaks the order (which vars_it relies on)
|
||||
|
||||
|
||||
// Build the model:
|
||||
|
||||
|
||||
// get the root directory of this object
|
||||
CStr dirname = g_ObjMan.m_ObjectTypes[m_Type].m_Name;
|
||||
|
||||
@ -176,9 +187,26 @@ bool CObjectEntry::BuildRandomVariant(const CObjectBase::variation_key& vars, CO
|
||||
}
|
||||
}
|
||||
|
||||
// start up idling
|
||||
if (! m_Model->SetAnimation(GetRandomAnimation("idle")))
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to set idle animation in model \"%s\"", modelfilename);
|
||||
// ensure there's always an idle animation
|
||||
if (m_Animations.find("idle") == m_Animations.end())
|
||||
{
|
||||
CSkeletonAnim* anim = new CSkeletonAnim();
|
||||
anim->m_Name = "idle";
|
||||
anim->m_AnimDef = NULL;
|
||||
anim->m_Speed = 0.f;
|
||||
anim->m_ActionPos = 0.f;
|
||||
anim->m_ActionPos2 = 0.f;
|
||||
m_Animations.insert(std::make_pair("idle", anim));
|
||||
|
||||
// Ignore errors, since they're probably saying this is a non-animated model
|
||||
m_Model->SetAnimation(anim);
|
||||
}
|
||||
else
|
||||
{
|
||||
// start up idling
|
||||
if (! m_Model->SetAnimation(GetRandomAnimation("idle")))
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to set idle animation in model \"%s\"", modelfilename);
|
||||
}
|
||||
|
||||
// build props - TODO, RC - need to fix up bounds here
|
||||
// TODO: Make sure random variations get handled correctly when a prop fails
|
||||
@ -186,7 +214,7 @@ bool CObjectEntry::BuildRandomVariant(const CObjectBase::variation_key& vars, CO
|
||||
{
|
||||
const CObjectBase::Prop& prop = props[p];
|
||||
|
||||
CObjectEntry* oe = g_ObjMan.FindObjectVariation(prop.m_ModelName, vars, vars_it);
|
||||
CObjectEntry* oe = g_ObjMan.FindObjectVariation(prop.m_ModelName, selections);
|
||||
if (!oe)
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to build prop model \"%s\" on actor \"%s\"", (const char*)prop.m_ModelName, (const char*)m_Base->m_ShortName);
|
||||
@ -265,7 +293,6 @@ bool CObjectEntry::BuildRandomVariant(const CObjectBase::variation_key& vars, CO
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName)
|
||||
{
|
||||
SkeletonAnimMap::iterator lower = m_Animations.lower_bound(animationName);
|
||||
@ -279,7 +306,7 @@ CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName)
|
||||
else
|
||||
{
|
||||
// TODO: Do we care about network synchronisation of random animations?
|
||||
int id = rand() % (int)count;
|
||||
int id = rand(0, (int)count);
|
||||
std::advance(lower, id);
|
||||
return lower->second;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
CObjectEntry(int type, CObjectBase* base);
|
||||
~CObjectEntry();
|
||||
|
||||
bool BuildRandomVariant(const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it);
|
||||
bool BuildVariation(const std::vector<std::set<CStrW> >& selections);
|
||||
|
||||
// Base actor. Contains all the things that don't change between
|
||||
// different variations of the actor.
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "Model.h"
|
||||
#include "Unit.h"
|
||||
#include "Matrix3D.h"
|
||||
#include "ps/Profile.h"
|
||||
|
||||
#define LOG_CATEGORY "graphics"
|
||||
|
||||
@ -77,37 +78,28 @@ CObjectBase* CObjectManager::FindObjectBase(const char* objectname)
|
||||
|
||||
CObjectEntry* CObjectManager::FindObject(const char* objname)
|
||||
{
|
||||
CObjectBase* base = FindObjectBase(objname);
|
||||
|
||||
if (! base)
|
||||
return NULL;
|
||||
|
||||
std::set<CStr> choices;
|
||||
// TODO: Fill in these choices from somewhere, e.g.:
|
||||
//choices.insert("whatever");
|
||||
|
||||
CObjectBase::variation_key var;
|
||||
base->CalculateVariation(choices, var);
|
||||
|
||||
CObjectBase::variation_key::const_iterator vars_it=var.begin();
|
||||
return FindObjectVariation(base, var, vars_it);
|
||||
std::vector<std::set<CStrW> > selections; // TODO - should this really be empty?
|
||||
return FindObjectVariation(objname, selections);
|
||||
}
|
||||
|
||||
CObjectEntry* CObjectManager::FindObjectVariation(const char* objname, const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it)
|
||||
CObjectEntry* CObjectManager::FindObjectVariation(const char* objname, const std::vector<std::set<CStrW> >& selections)
|
||||
{
|
||||
CObjectBase* base = FindObjectBase(objname);
|
||||
|
||||
if (! base)
|
||||
return NULL;
|
||||
|
||||
return FindObjectVariation(base, vars, vars_it);
|
||||
return FindObjectVariation(base, selections);
|
||||
}
|
||||
|
||||
CObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it)
|
||||
CObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const std::vector<std::set<CStrW> >& selections)
|
||||
{
|
||||
PROFILE( "object variation loading" );
|
||||
|
||||
// Look to see whether this particular variation has already been loaded
|
||||
|
||||
ObjectKey key (base->m_Name, vars);
|
||||
std::vector<u8> choices = base->CalculateVariationKey(selections);
|
||||
ObjectKey key (base->m_Name, choices);
|
||||
|
||||
std::map<ObjectKey, CObjectEntry*>::iterator it = m_ObjectTypes[0].m_Objects.find(key);
|
||||
if (it != m_ObjectTypes[0].m_Objects.end())
|
||||
@ -117,7 +109,7 @@ CObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const CObje
|
||||
|
||||
CObjectEntry* obj = new CObjectEntry(0, base); // TODO: type ?
|
||||
|
||||
if (! obj->BuildRandomVariant(vars, vars_it))
|
||||
if (! obj->BuildVariation(selections))
|
||||
{
|
||||
DeleteObject(obj);
|
||||
return NULL;
|
||||
@ -185,8 +177,6 @@ void CObjectManager::UnloadObjects()
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// For ScEd:
|
||||
|
||||
static void GetObjectName_ThunkCb(const char* path, const DirEnt* UNUSED(ent), void* context)
|
||||
{
|
||||
@ -194,80 +184,15 @@ static void GetObjectName_ThunkCb(const char* path, const DirEnt* UNUSED(ent), v
|
||||
CStr name (path);
|
||||
names->push_back(name.AfterFirst("actors/"));
|
||||
}
|
||||
|
||||
void CObjectManager::GetAllObjectNames(std::vector<CStr>& names)
|
||||
{
|
||||
VFSUtil::EnumDirEnts("art/actors/", VFSUtil::RECURSIVE, "*.xml",
|
||||
GetObjectName_ThunkCb, &names);
|
||||
}
|
||||
|
||||
void CObjectManager::GetPropObjectNames(std::vector<CStr>& names)
|
||||
{
|
||||
VFSUtil::EnumDirEnts("art/actors/props/", VFSUtil::RECURSIVE, "*.xml",
|
||||
GetObjectName_ThunkCb, &names);
|
||||
}
|
||||
|
||||
struct CObjectThing_Entity : public CObjectThing
|
||||
{
|
||||
CObjectThing_Entity(CBaseEntity* b) : base(b), ent(NULL), obj(g_ObjMan.FindObject((CStr)b->m_actorName)) {}
|
||||
~CObjectThing_Entity() {}
|
||||
CBaseEntity* base;
|
||||
CEntity* ent;
|
||||
CObjectEntry* obj;
|
||||
void Create(CMatrix3D& transform, int playerID)
|
||||
{
|
||||
CVector3D orient = transform.GetIn();
|
||||
CVector3D position = transform.GetTranslation();
|
||||
ent = g_EntityManager.create(base, position, atan2(-orient.X, -orient.Z));
|
||||
ent->SetPlayer(g_Game->GetPlayer(playerID));
|
||||
}
|
||||
void SetTransform(CMatrix3D& transform)
|
||||
{
|
||||
CVector3D orient = transform.GetIn();
|
||||
CVector3D position = transform.GetTranslation();
|
||||
|
||||
// This looks quite yucky, but nothing else seems to actually work:
|
||||
ent->m_position =
|
||||
ent->m_position_previous =
|
||||
ent->m_graphics_position = position;
|
||||
ent->teleport();
|
||||
ent->m_orientation =
|
||||
ent->m_orientation_previous =
|
||||
ent->m_graphics_orientation = atan2(-orient.X, -orient.Z);
|
||||
ent->reorient();
|
||||
}
|
||||
CObjectEntry* GetObjectEntry()
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
struct CObjectThing_Object : public CObjectThing
|
||||
{
|
||||
CObjectThing_Object(CObjectEntry* o) : obj(o) {}
|
||||
~CObjectThing_Object() {}
|
||||
CObjectEntry* obj;
|
||||
CUnit* unit;
|
||||
void Create(CMatrix3D& transform, int UNUSED(playerID))
|
||||
{
|
||||
unit = new CUnit(obj, obj->m_Model->Clone());
|
||||
unit->GetModel()->SetTransform(transform);
|
||||
g_UnitMan.AddUnit(unit);
|
||||
}
|
||||
void SetTransform(CMatrix3D& transform)
|
||||
{
|
||||
unit->GetModel()->SetTransform(transform);
|
||||
}
|
||||
CObjectEntry* GetObjectEntry()
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
void CObjectManager::SetSelectedEntity(CBaseEntity* thing)
|
||||
{
|
||||
delete m_SelectedThing;
|
||||
m_SelectedThing = (thing ? new CObjectThing_Entity(thing) : NULL);
|
||||
}
|
||||
void CObjectManager::SetSelectedObject(CObjectEntry* thing)
|
||||
{
|
||||
delete m_SelectedThing;
|
||||
m_SelectedThing = (thing ? new CObjectThing_Object(thing) : NULL);
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ class CObjectManager : public Singleton<CObjectManager>
|
||||
public:
|
||||
struct ObjectKey
|
||||
{
|
||||
ObjectKey(const CStr& name, const CObjectBase::variation_key& var)
|
||||
ObjectKey(const CStr& name, const std::vector<u8>& var)
|
||||
: ActorName(name), ActorVariation(var) {}
|
||||
|
||||
CStr ActorName;
|
||||
CObjectBase::variation_key ActorVariation;
|
||||
std::vector<u8> ActorVariation;
|
||||
|
||||
};
|
||||
|
||||
@ -68,17 +68,13 @@ public:
|
||||
|
||||
CObjectBase* FindObjectBase(const char* objname);
|
||||
|
||||
CObjectEntry* FindObjectVariation(const char* objname, const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it);
|
||||
CObjectEntry* FindObjectVariation(CObjectBase* base, const CObjectBase::variation_key& vars, CObjectBase::variation_key::const_iterator& vars_it);
|
||||
CObjectEntry* FindObjectVariation(const char* objname, const std::vector<std::set<CStrW> >& selections);
|
||||
CObjectEntry* FindObjectVariation(CObjectBase* base, const std::vector<std::set<CStrW> >& selections);
|
||||
|
||||
// Get all names, quite slowly. (Intended only for ScEd.)
|
||||
void GetAllObjectNames(std::vector<CStr>& names);
|
||||
void GetPropObjectNames(std::vector<CStr>& names);
|
||||
|
||||
//CBaseEntity* m_SelectedEntity;
|
||||
void SetSelectedEntity(CBaseEntity* thing);
|
||||
void SetSelectedObject(CObjectEntry* thing);
|
||||
|
||||
std::vector<SObjectType> m_ObjectTypes;
|
||||
};
|
||||
|
||||
|
@ -70,7 +70,8 @@ CSkeletonAnimDef* CSkeletonAnimDef::Load(const char* filename)
|
||||
// unpack the data
|
||||
CSkeletonAnimDef* anim=new CSkeletonAnimDef;
|
||||
try {
|
||||
unpacker.UnpackString(anim->m_Name);
|
||||
CStr name; // unused - just here to maintain compatibility with the animation files
|
||||
unpacker.UnpackString(name);
|
||||
unpacker.UnpackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
|
||||
unpacker.UnpackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
|
||||
unpacker.UnpackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
|
||||
@ -91,7 +92,7 @@ void CSkeletonAnimDef::Save(const char* filename,const CSkeletonAnimDef* anim)
|
||||
CFilePacker packer(FILE_VERSION, "PSSA");
|
||||
|
||||
// pack up all the data
|
||||
packer.PackString(CStr(anim->m_Name));
|
||||
packer.PackString("");
|
||||
packer.PackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));
|
||||
packer.PackRaw(&anim->m_NumKeys,sizeof(anim->m_NumKeys));
|
||||
packer.PackRaw(&anim->m_NumFrames,sizeof(anim->m_NumFrames));
|
||||
|
@ -69,8 +69,6 @@ public:
|
||||
static void Save(const char* filename, const CSkeletonAnimDef* anim);
|
||||
|
||||
public:
|
||||
// name of the animation
|
||||
CStr m_Name; // TODO: this doesn't seem to be used, so it's just a waste of memory...
|
||||
// frame time - time between successive frames, in ms
|
||||
float m_FrameTime;
|
||||
// number of keys in each frame - should match number of bones in the skeleton
|
||||
|
@ -3,46 +3,60 @@
|
||||
#include "Unit.h"
|
||||
#include "Model.h"
|
||||
#include "ObjectEntry.h"
|
||||
#include "ObjectManager.h"
|
||||
#include "SkeletonAnimDef.h"
|
||||
|
||||
CUnit::~CUnit() {
|
||||
CUnit::CUnit(CObjectEntry* object, CEntity* entity, const std::set<CStrW>& actorSelections)
|
||||
: m_Object(object), m_Model(object->m_Model->Clone()), m_Entity(entity),
|
||||
m_ID(-1), m_ActorSelections(actorSelections)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CUnit::~CUnit()
|
||||
{
|
||||
delete m_Model;
|
||||
}
|
||||
|
||||
void CUnit::ShowAmmunition()
|
||||
{
|
||||
if( !m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint )
|
||||
if (!m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint)
|
||||
return;
|
||||
m_Model->AddProp( m_Object->m_AmmunitionPoint, m_Object->m_AmmunitionModel->Clone() );
|
||||
m_Model->AddProp(m_Object->m_AmmunitionPoint, m_Object->m_AmmunitionModel->Clone());
|
||||
}
|
||||
|
||||
void CUnit::HideAmmunition()
|
||||
{
|
||||
if( !m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint )
|
||||
if (!m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint)
|
||||
return;
|
||||
// Find out what the usual prop is:
|
||||
|
||||
// Find out what the usual prop is:
|
||||
std::vector<CModel::Prop>& props = m_Object->m_Model->GetProps();
|
||||
std::vector<CModel::Prop>::iterator it;
|
||||
for( it = props.begin(); it != props.end(); ++it )
|
||||
if( it->m_Point == m_Object->m_AmmunitionPoint )
|
||||
for (it = props.begin(); it != props.end(); ++it)
|
||||
{
|
||||
if (it->m_Point == m_Object->m_AmmunitionPoint)
|
||||
{
|
||||
m_Model->AddProp( m_Object->m_AmmunitionPoint, it->m_Model->Clone() );
|
||||
m_Model->AddProp(m_Object->m_AmmunitionPoint, it->m_Model->Clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No usual prop.
|
||||
m_Model->RemoveProp( m_Object->m_AmmunitionPoint );
|
||||
m_Model->RemoveProp(m_Object->m_AmmunitionPoint);
|
||||
}
|
||||
|
||||
bool CUnit::SetRandomAnimation(const CStr& name, bool once)
|
||||
bool CUnit::SetRandomAnimation(const CStr& name, bool once, float speed)
|
||||
{
|
||||
CSkeletonAnim* anim = GetRandomAnimation(name);
|
||||
if (anim)
|
||||
{
|
||||
m_Model->SetAnimation(anim, once);
|
||||
m_Model->SetAnimation(anim, once, speed ? speed*anim->m_AnimDef->GetDuration() : 1000.f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO - report an error?
|
||||
// This shouldn't happen, since GetRandomAnimation tries to always
|
||||
// return something valid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -50,14 +64,61 @@ bool CUnit::SetRandomAnimation(const CStr& name, bool once)
|
||||
CSkeletonAnim* CUnit::GetRandomAnimation(const CStr& name)
|
||||
{
|
||||
CSkeletonAnim* anim = m_Object->GetRandomAnimation(name);
|
||||
|
||||
// Fall back to 'idle', if no matching animation is found
|
||||
if (anim == NULL && name != "idle")
|
||||
anim = m_Object->GetRandomAnimation("idle");
|
||||
|
||||
// Every object should have an idle animation (even if it's a dummy static one)
|
||||
debug_assert(anim != NULL);
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
bool CUnit::IsPlayingAnimation(const CStr& name)
|
||||
{
|
||||
return (m_Model->GetAnimation()->m_Name == name);
|
||||
return (m_Model->GetAnimation() && m_Model->GetAnimation()->m_Name == name);
|
||||
}
|
||||
|
||||
|
||||
void CUnit::SetPlayerID(int id)
|
||||
{
|
||||
m_PlayerID = id;
|
||||
m_Model->SetPlayerID(m_PlayerID);
|
||||
}
|
||||
|
||||
void CUnit::SetEntitySelection(const CStrW& selection)
|
||||
{
|
||||
// If we've already selected this, don't do anything
|
||||
if (m_EntitySelections.find(selection) != m_EntitySelections.end())
|
||||
return;
|
||||
|
||||
// Just allow one selection at a time
|
||||
m_EntitySelections.clear();
|
||||
m_EntitySelections.insert(selection);
|
||||
|
||||
ReloadObject();
|
||||
}
|
||||
|
||||
void CUnit::ReloadObject()
|
||||
{
|
||||
std::vector<std::set<CStrW> > selections;
|
||||
// TODO: push world selections (seasons, etc) (and reload whenever they're changed)
|
||||
selections.push_back(m_EntitySelections);
|
||||
selections.push_back(m_ActorSelections);
|
||||
|
||||
// If these selections give a different object, change this unit to use it
|
||||
CObjectEntry* newObject = g_ObjMan.FindObjectVariation(m_Object->m_Base, selections);
|
||||
if (newObject != m_Object)
|
||||
{
|
||||
CModel* newModel = newObject->m_Model->Clone();
|
||||
// Copy old settings to the new model
|
||||
newModel->SetPlayerID(m_PlayerID);
|
||||
newModel->SetTransform(m_Model->GetTransform());
|
||||
// TODO: preserve selection of animation, anim offset, etc?
|
||||
|
||||
delete m_Model;
|
||||
m_Model = newModel;
|
||||
m_Object = newObject;
|
||||
}
|
||||
}
|
||||
|
@ -14,17 +14,7 @@ class CStr8;
|
||||
class CUnit
|
||||
{
|
||||
public:
|
||||
// constructor - unit invalid without a model and object
|
||||
CUnit(CObjectEntry* object, CModel* model)
|
||||
: m_Object(object), m_Model(model), m_Entity(NULL), m_ID(-1)
|
||||
{
|
||||
debug_assert(object && model);
|
||||
}
|
||||
CUnit(CObjectEntry* object, CModel* model, CEntity* entity)
|
||||
: m_Object(object), m_Model(model), m_Entity(entity), m_ID(-1)
|
||||
{
|
||||
debug_assert(object && model);
|
||||
}
|
||||
CUnit(CObjectEntry* object, CEntity* entity, const std::set<CStrW>& actorSelections);
|
||||
|
||||
// destructor
|
||||
~CUnit();
|
||||
@ -43,16 +33,23 @@ public:
|
||||
|
||||
// Sets the animation a random one matching 'name'. If none is found,
|
||||
// sets to idle instead.
|
||||
bool SetRandomAnimation(const CStr8& name, bool once = false);
|
||||
bool SetRandomAnimation(const CStr8& name, bool once = false, float speed = 0.0f);
|
||||
|
||||
// Returns the animation a random one matching 'name'. If none is found,
|
||||
// Returns a random animation matching 'name'. If none is found,
|
||||
// returns idle instead.
|
||||
CSkeletonAnim* GetRandomAnimation(const CStr8& name);
|
||||
|
||||
// Sets the entity-selection, and updates the unit to use the new
|
||||
// actor variation.
|
||||
void SetEntitySelection(const CStrW& selection);
|
||||
|
||||
// Returns whether the currently active animation is one of the ones
|
||||
// matching 'name'.
|
||||
bool IsPlayingAnimation(const CStr8& name);
|
||||
|
||||
// Set player ID of this unit
|
||||
void SetPlayerID(int id);
|
||||
|
||||
int GetID() const { return m_ID; }
|
||||
void SetID(int id) { m_ID = id; }
|
||||
|
||||
@ -63,9 +60,19 @@ private:
|
||||
CModel* m_Model;
|
||||
// the entity that this actor represents, if any
|
||||
CEntity* m_Entity;
|
||||
// player id of this unit (only used for graphical effects)
|
||||
int m_PlayerID;
|
||||
|
||||
// unique (per map) ID number for units created in the editor, as a
|
||||
// permanent way of referencing them. -1 for non-editor units.
|
||||
int m_ID;
|
||||
|
||||
// actor-level selections for this unit
|
||||
std::set<CStrW> m_ActorSelections;
|
||||
// entity-level selections for this unit
|
||||
std::set<CStrW> m_EntitySelections;
|
||||
|
||||
void ReloadObject();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -119,14 +119,24 @@ CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) con
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CreateUnit: create a new unit and add it to the world
|
||||
CUnit* CUnitManager::CreateUnit(const CStr& actorName, CEntity* entity)
|
||||
CUnit* CUnitManager::CreateUnit(const CStr& actorName, CEntity* entity, const std::set<CStrW>& selections)
|
||||
{
|
||||
CObjectEntry* obj = g_ObjMan.FindObject(actorName);
|
||||
CObjectBase* base = g_ObjMan.FindObjectBase(actorName);
|
||||
|
||||
if (! base)
|
||||
return NULL;
|
||||
|
||||
std::set<CStrW> actorSelections = base->CalculateRandomVariation(selections);
|
||||
|
||||
std::vector<std::set<CStrW> > selectionsVec;
|
||||
selectionsVec.push_back(actorSelections);
|
||||
|
||||
CObjectEntry* obj = g_ObjMan.FindObjectVariation(base, selectionsVec);
|
||||
|
||||
if (! obj)
|
||||
return NULL;
|
||||
|
||||
CUnit* unit = new CUnit(obj, obj->m_Model->Clone(), entity);
|
||||
CUnit* unit = new CUnit(obj, entity, actorSelections);
|
||||
AddUnit(unit);
|
||||
return unit;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
void DeleteAll();
|
||||
|
||||
// creates a new unit and adds it to the world
|
||||
CUnit* CreateUnit(const CStr& actorName, CEntity* entity);
|
||||
CUnit* CreateUnit(const CStr& actorName, CEntity* entity, const std::set<CStrW>& selections);
|
||||
|
||||
// return the units
|
||||
const std::vector<CUnit*>& GetUnits() const { return m_Units; }
|
||||
|
@ -89,10 +89,14 @@ static bool GetTooltip(IGUIObject* obj, CStr &style)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowTooltip(IGUIObject* obj, CPos pos, CStr& style, CGUI* gui)
|
||||
void GUITooltip::ShowTooltip(IGUIObject* obj, CPos pos, const CStr& style, CGUI* gui)
|
||||
{
|
||||
debug_assert(obj);
|
||||
|
||||
// Ignore attempts to use tooltip ""
|
||||
if (style.Length() == 0)
|
||||
return;
|
||||
|
||||
// Get the object referenced by 'tooltip_style'
|
||||
IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style);
|
||||
if (! tooltipobj)
|
||||
@ -144,8 +148,12 @@ void ShowTooltip(IGUIObject* obj, CPos pos, CStr& style, CGUI* gui)
|
||||
usedobj->HandleMessage(SGUIMessage(GUIM_SETTINGS_UPDATED, "caption"));
|
||||
}
|
||||
|
||||
void HideTooltip(CStr& style, CGUI* gui)
|
||||
void GUITooltip::HideTooltip(const CStr& style, CGUI* gui)
|
||||
{
|
||||
// Ignore attempts to use tooltip ""
|
||||
if (style.Length() == 0)
|
||||
return;
|
||||
|
||||
IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style);
|
||||
if (! tooltipobj)
|
||||
{
|
||||
|
@ -15,6 +15,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static void ShowTooltip(IGUIObject* obj, CPos pos, const CStr& style, CGUI* gui);
|
||||
static void HideTooltip(const CStr& style, CGUI* gui);
|
||||
|
||||
int m_State;
|
||||
|
||||
IGUIObject* m_PreviousObject;
|
||||
|
@ -130,10 +130,7 @@ class IGUIObject
|
||||
friend class CGUI;
|
||||
friend class CInternalCGUIAccessorBase;
|
||||
friend class IGUIScrollBar;
|
||||
|
||||
// Allow ShowTooltip/HideTooltip (GUITooltip.cpp) to access HandleMessage
|
||||
friend void ShowTooltip(IGUIObject*, CPos, CStr&, CGUI*);
|
||||
friend void HideTooltip(CStr&, CGUI*);
|
||||
friend class GUITooltip;
|
||||
|
||||
// Allow getProperty to access things like GetParent()
|
||||
friend JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp);
|
||||
|
@ -1179,8 +1179,10 @@ bool CBuildingPlacer::activate(CStrW& templateName)
|
||||
|
||||
// m_actor
|
||||
CStr actorName ( base->m_actorName ); // convert CStrW->CStr8
|
||||
m_actor = g_UnitMan.CreateUnit( actorName, 0 );
|
||||
m_actor->GetModel()->SetPlayerID(g_Game->GetLocalPlayer()->GetPlayerID());
|
||||
|
||||
std::set<CStrW> selections;
|
||||
m_actor = g_UnitMan.CreateUnit( actorName, 0, selections );
|
||||
m_actor->SetPlayerID(g_Game->GetLocalPlayer()->GetPlayerID());
|
||||
|
||||
// m_bounds
|
||||
if( base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
|
||||
|
@ -294,7 +294,7 @@ void WriteBigScreenshot(const char* extension, int tiles)
|
||||
}
|
||||
}
|
||||
|
||||
// Restor the buffer settings
|
||||
// Restore the buffer settings
|
||||
glDrawBuffer(oldDrawBuffer);
|
||||
glReadBuffer(oldReadBuffer);
|
||||
|
||||
|
@ -1076,6 +1076,11 @@ void CRenderer::BindTexture(int unit,GLuint tex)
|
||||
void CRenderer::SetTexture(int unit,CTexture* texture)
|
||||
{
|
||||
Handle h = texture? texture->GetHandle() : 0;
|
||||
|
||||
// Errored textures will give a handle of -1
|
||||
if (h == -1)
|
||||
h = 0;
|
||||
|
||||
ogl_tex_bind(h, unit);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ extern int g_xres, g_yres;
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, CStrW building )
|
||||
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building )
|
||||
{
|
||||
m_position = position;
|
||||
m_orientation = orientation;
|
||||
@ -94,7 +94,8 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, CStr
|
||||
|
||||
m_base = base;
|
||||
|
||||
loadBase();
|
||||
m_actorSelections = actorSelections;
|
||||
loadBase();
|
||||
|
||||
if( m_bounds )
|
||||
m_bounds->setPosition( m_position.X, m_position.Z );
|
||||
@ -166,7 +167,8 @@ void CEntity::loadBase()
|
||||
}
|
||||
|
||||
CStr actorName ( m_base->m_actorName ); // convert CStrW->CStr8
|
||||
m_actor = g_UnitMan.CreateUnit( actorName, this );
|
||||
|
||||
m_actor = g_UnitMan.CreateUnit( actorName, this, m_actorSelections );
|
||||
|
||||
// Set up our instance data
|
||||
|
||||
@ -217,7 +219,7 @@ void CEntity::SetPlayer(CPlayer *pPlayer)
|
||||
|
||||
// Store the ID of the player in the associated model
|
||||
if( m_actor )
|
||||
m_actor->GetModel()->SetPlayerID( m_player->GetPlayerID() );
|
||||
m_actor->SetPlayerID( m_player->GetPlayerID() );
|
||||
}
|
||||
|
||||
void CEntity::updateActorTransforms()
|
||||
@ -449,10 +451,16 @@ void CEntity::update( size_t timestep )
|
||||
if( m_extant )
|
||||
{
|
||||
if( ( m_lastState != -1 ) || !m_actor->GetModel()->GetAnimation() )
|
||||
{
|
||||
m_actor->SetEntitySelection( L"idle" );
|
||||
m_actor->SetRandomAnimation( "idle" );
|
||||
}
|
||||
}
|
||||
else if( !m_actor->GetModel()->GetAnimation() )
|
||||
{
|
||||
m_actor->SetEntitySelection( L"corpse" );
|
||||
m_actor->SetRandomAnimation( "corpse" );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lastState != -1 )
|
||||
@ -709,7 +717,7 @@ void CEntity::teleport()
|
||||
void CEntity::playerChanged()
|
||||
{
|
||||
if( m_actor )
|
||||
m_actor->GetModel()->SetPlayerID( m_player->GetPlayerID() );
|
||||
m_actor->SetPlayerID( m_player->GetPlayerID() );
|
||||
}
|
||||
|
||||
void CEntity::checkSelection()
|
||||
@ -1164,7 +1172,8 @@ JSBool CEntity::Construct( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsva
|
||||
}
|
||||
}
|
||||
|
||||
HEntity handle = g_EntityManager.create( baseEntity, position, orientation );
|
||||
std::set<CStrW> selections; // TODO: let scripts specify selections?
|
||||
HEntity handle = g_EntityManager.create( baseEntity, position, orientation, selections );
|
||||
|
||||
*rval = ToJSVal<CEntity>( *handle );
|
||||
return( JS_TRUE );
|
||||
@ -1346,7 +1355,10 @@ bool CEntity::Kill( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(arg
|
||||
g_EntityManager.SetDeath(true);
|
||||
|
||||
if( m_actor )
|
||||
{
|
||||
m_actor->SetEntitySelection( L"death" );
|
||||
m_actor->SetRandomAnimation( "death", true );
|
||||
}
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ public:
|
||||
CScriptObject m_EventHandlers[EVENT_LAST];
|
||||
|
||||
CUnit* m_actor;
|
||||
std::set<CStrW> m_actorSelections;
|
||||
|
||||
// State transition in the FSM (animations should be reset)
|
||||
bool m_transition;
|
||||
@ -184,7 +185,7 @@ public:
|
||||
CEntity* m_currentListener;
|
||||
|
||||
private:
|
||||
CEntity( CBaseEntity* base, CVector3D position, float orientation, CStrW building = L"" );
|
||||
CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building = L"" );
|
||||
|
||||
uint processGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
|
||||
|
||||
@ -201,6 +202,8 @@ private:
|
||||
|
||||
bool processPatrol( CEntityOrder* current, size_t timestep_milli );
|
||||
|
||||
float processChooseMovement( float distance );
|
||||
|
||||
public:
|
||||
~CEntity();
|
||||
|
||||
|
@ -68,7 +68,7 @@ void CEntityManager::deleteAll()
|
||||
m_extant = true;
|
||||
}
|
||||
|
||||
HEntity CEntityManager::create( CBaseEntity* base, CVector3D position, float orientation )
|
||||
HEntity CEntityManager::create( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections )
|
||||
{
|
||||
debug_assert( base );
|
||||
if( !base )
|
||||
@ -77,24 +77,24 @@ HEntity CEntityManager::create( CBaseEntity* base, CVector3D position, float ori
|
||||
while( m_entities[m_nextalloc].m_refcount )
|
||||
{
|
||||
m_nextalloc++;
|
||||
if(m_nextalloc == MAX_HANDLES)
|
||||
if(m_nextalloc >= MAX_HANDLES)
|
||||
{
|
||||
debug_warn("Ran out of entity handles!");
|
||||
return HEntity();
|
||||
}
|
||||
}
|
||||
|
||||
m_entities[m_nextalloc].m_entity = new CEntity( base, position, orientation );
|
||||
m_entities[m_nextalloc].m_entity = new CEntity( base, position, orientation, actorSelections );
|
||||
if( m_collisionPatches)
|
||||
m_entities[m_nextalloc].m_entity->updateCollisionPatch();
|
||||
m_entities[m_nextalloc].m_entity->me = HEntity( m_nextalloc );
|
||||
return( HEntity( m_nextalloc++ ) );
|
||||
}
|
||||
|
||||
HEntity CEntityManager::create( const CStrW templateName, CVector3D position, float orientation )
|
||||
HEntity CEntityManager::create( const CStrW templateName, CVector3D position, float orientation, const std::set<CStrW>& actorSelections )
|
||||
{
|
||||
CBaseEntity* templateObj = g_EntityTemplateCollection.getTemplate( templateName );
|
||||
return( create( templateObj, position, orientation ) );
|
||||
return( create( templateObj, position, orientation, actorSelections ) );
|
||||
}
|
||||
|
||||
HEntity CEntityManager::createFoundation( CStrW templateName, CVector3D position, float orientation )
|
||||
@ -103,8 +103,11 @@ HEntity CEntityManager::createFoundation( CStrW templateName, CVector3D position
|
||||
debug_assert( base );
|
||||
if( !base )
|
||||
return HEntity();
|
||||
|
||||
std::set<CStrW> selections;
|
||||
|
||||
if( base->m_foundation == L"" )
|
||||
return create( base, position, orientation ); // Entity has no foundation, so just create it
|
||||
return create( base, position, orientation, selections ); // Entity has no foundation, so just create it
|
||||
|
||||
CBaseEntity* foundation = g_EntityTemplateCollection.getTemplate( base->m_foundation );
|
||||
debug_assert( foundation );
|
||||
@ -114,14 +117,14 @@ HEntity CEntityManager::createFoundation( CStrW templateName, CVector3D position
|
||||
while( m_entities[m_nextalloc].m_refcount )
|
||||
{
|
||||
m_nextalloc++;
|
||||
if(m_nextalloc == MAX_HANDLES)
|
||||
if(m_nextalloc >= MAX_HANDLES)
|
||||
{
|
||||
debug_warn("Ran out of entity handles!");
|
||||
return HEntity();
|
||||
}
|
||||
}
|
||||
|
||||
m_entities[m_nextalloc].m_entity = new CEntity( foundation, position, orientation, templateName );
|
||||
m_entities[m_nextalloc].m_entity = new CEntity( foundation, position, orientation, selections, templateName );
|
||||
if( m_collisionPatches)
|
||||
m_entities[m_nextalloc].m_entity->updateCollisionPatch();
|
||||
m_entities[m_nextalloc].m_entity->me = HEntity( m_nextalloc );
|
||||
|
@ -48,8 +48,8 @@ public:
|
||||
CEntityManager();
|
||||
~CEntityManager();
|
||||
|
||||
HEntity create( CBaseEntity* base, CVector3D position, float orientation );
|
||||
HEntity create( CStrW templateName, CVector3D position, float orientation );
|
||||
HEntity create( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections );
|
||||
HEntity create( CStrW templateName, CVector3D position, float orientation, const std::set<CStrW>& actorSelections );
|
||||
|
||||
HEntity createFoundation( CStrW templateName, CVector3D position, float orientation );
|
||||
|
||||
|
@ -29,6 +29,45 @@ enum EGotoSituation
|
||||
WOULD_LEAVE_MAP
|
||||
};
|
||||
|
||||
float CEntity::processChooseMovement( float distance )
|
||||
{
|
||||
// Should we run or walk
|
||||
if (m_shouldRun && m_staminaCurr > 0 && distance < m_run.m_MaxRange &&
|
||||
( distance > m_run.m_MinRange || m_isRunning ) )
|
||||
{
|
||||
if ( m_actor )
|
||||
{
|
||||
if ( !m_actor->IsPlayingAnimation( "run" ) )
|
||||
{
|
||||
m_actor->SetEntitySelection( L"run" );
|
||||
m_actor->SetRandomAnimation( "run", false, m_run.m_Speed );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( rand( 0, 1000 ) / 1000.0f );
|
||||
m_isRunning = true;
|
||||
}
|
||||
}
|
||||
return m_run.m_Speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_actor )
|
||||
{
|
||||
// Should we update animation?
|
||||
if ( !m_actor->IsPlayingAnimation( "walk" ) )
|
||||
{
|
||||
m_actor->SetEntitySelection( L"walk" );
|
||||
m_actor->SetRandomAnimation( "walk", false, m_speed );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( rand( 0, 1000 ) / 1000.0f );
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
return m_speed;
|
||||
}
|
||||
}
|
||||
|
||||
// Does all the shared processing for line-of-sight gotos
|
||||
|
||||
uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, HEntity& collide )
|
||||
@ -47,48 +86,8 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
|
||||
// Curve smoothing.
|
||||
// Here there be trig.
|
||||
|
||||
float scale;
|
||||
float scale = processChooseMovement( len ) * timestep;
|
||||
|
||||
//Should we run or walk
|
||||
if (m_shouldRun && m_staminaCurr > 0 && len < m_run.m_MaxRange &&
|
||||
( len > m_run.m_MinRange || m_isRunning ) )
|
||||
{
|
||||
scale = m_run.m_Speed * timestep;
|
||||
if ( m_actor )
|
||||
{
|
||||
if ( !m_actor->IsPlayingAnimation( "run" ) )
|
||||
{
|
||||
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
|
||||
if ( run )
|
||||
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = m_speed * timestep;
|
||||
if ( m_actor )
|
||||
{
|
||||
//Should we update animation?
|
||||
if ( !m_actor->IsPlayingAnimation( "walk" ) )
|
||||
{
|
||||
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
|
||||
if ( walk )
|
||||
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: Easy optimization: flag somewhere that this unit
|
||||
// is already pointing the way, and don't do this
|
||||
@ -338,38 +337,7 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste
|
||||
return( true );
|
||||
}
|
||||
|
||||
if ( m_actor )
|
||||
{
|
||||
//Should we run or walk
|
||||
if (m_shouldRun && m_staminaCurr > 0 && Distance < m_run.m_MaxRange &&
|
||||
( Distance > m_run.m_MinRange || m_isRunning ) )
|
||||
{
|
||||
if ( !m_actor->IsPlayingAnimation( "run" ) )
|
||||
{
|
||||
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
|
||||
if ( run )
|
||||
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Should we update animation?
|
||||
if ( !m_actor->IsPlayingAnimation( "walk" ) )
|
||||
{
|
||||
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
|
||||
if ( walk )
|
||||
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
processChooseMovement( Distance );
|
||||
|
||||
// The pathfinder will push its result back into this unit's queue and
|
||||
// add back the current order at the end with the transition type.
|
||||
@ -391,7 +359,12 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
// few hundred ms, at the 'action point' of the animation we're
|
||||
// now setting.
|
||||
m_isRunning = false;
|
||||
m_actor->GetModel()->SetAnimation( m_fsm_animation, true, 1000.0f * m_fsm_animation->m_AnimDef->GetDuration() / (float)action->m_Speed, m_fsm_animation );
|
||||
// TODO: this is set to be looping, because apparently it otherwise
|
||||
// plays one frame of 'idle' after e.g. attacks. But this way means
|
||||
// animations sometimes play ~1.5 times then repeat, which looks
|
||||
// broken too.
|
||||
//m_actor->GetModel()->SetAnimation( m_fsm_animation, true, 1000.0f * m_fsm_animation->m_AnimDef->GetDuration() / (float)action->m_Speed, m_actor->GetRandomAnimation( "idle" ) );
|
||||
m_actor->GetModel()->SetAnimation( m_fsm_animation, false, 1000.0f * m_fsm_animation->m_AnimDef->GetDuration() / (float)action->m_Speed );
|
||||
}
|
||||
if( ( m_fsm_cyclepos <= m_fsm_anipos2 ) &&
|
||||
( nextpos > m_fsm_anipos2 ) )
|
||||
@ -408,7 +381,8 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
m_orderQueue.pop_front();
|
||||
m_isRunning = false;
|
||||
m_shouldRun = false;
|
||||
m_actor->SetRandomAnimation("idle");
|
||||
m_actor->SetEntitySelection( L"idle" );
|
||||
m_actor->SetRandomAnimation( "idle" );
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
@ -460,39 +434,8 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
// We're aiming to end up at a location just inside our maximum range
|
||||
// (is this good enough?)
|
||||
delta = delta.normalize() * ( adjRange - m_bounds->m_radius );
|
||||
|
||||
if ( m_actor )
|
||||
{
|
||||
//Should we run or walk
|
||||
if (m_shouldRun && m_staminaCurr > 0 && deltaLength < m_run.m_MaxRange &&
|
||||
( deltaLength > m_run.m_MinRange || m_isRunning ) )
|
||||
{
|
||||
if ( !m_actor->IsPlayingAnimation( "run" ) )
|
||||
{
|
||||
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
|
||||
if ( run )
|
||||
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Should we update animation?
|
||||
if ( !m_actor->IsPlayingAnimation( "walk" ) )
|
||||
{
|
||||
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
|
||||
if ( walk )
|
||||
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processChooseMovement(deltaLength);
|
||||
|
||||
current->m_data[0].location = (CVector2D)current->m_data[0].entity->m_position - delta;
|
||||
|
||||
@ -553,6 +496,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
}
|
||||
|
||||
// Pick our animation, calculate the time to play it, and start the timer.
|
||||
m_actor->SetEntitySelection( animation );
|
||||
m_fsm_animation = m_actor->GetRandomAnimation( animation );
|
||||
|
||||
// Here's the idea - we want to be at that animation's event point
|
||||
@ -575,7 +519,10 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
// If we've just transitioned, play idle. Otherwise, let the previous animation complete, if it
|
||||
// hasn't already.
|
||||
if( m_transition )
|
||||
{
|
||||
// (don't change actor's entity-selection)
|
||||
m_actor->SetRandomAnimation( "idle" );
|
||||
}
|
||||
}
|
||||
|
||||
// Load time needs to be animation->m_ActionPos2 ms after the start of the animation.
|
||||
@ -635,38 +582,7 @@ bool CEntity::processGoto( CEntityOrder* current, size_t UNUSED(timestep_millis)
|
||||
return( false );
|
||||
}
|
||||
|
||||
if ( m_actor )
|
||||
{
|
||||
//Should we run or walk
|
||||
if (m_shouldRun && m_staminaCurr > 0 && Distance < m_run.m_MaxRange &&
|
||||
( Distance > m_run.m_MinRange || m_isRunning ) )
|
||||
{
|
||||
if ( !m_actor->IsPlayingAnimation( "run" ) )
|
||||
{
|
||||
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
|
||||
if ( run )
|
||||
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Should we update animation?
|
||||
if ( !m_actor->IsPlayingAnimation( "walk" ) )
|
||||
{
|
||||
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
|
||||
if ( walk )
|
||||
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
|
||||
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
processChooseMovement( Distance );
|
||||
|
||||
// The pathfinder will push its result back into this unit's queue.
|
||||
|
||||
|
@ -164,20 +164,22 @@ MESSAGEHANDLER(ObjectPreview)
|
||||
CStrW name;
|
||||
if (ParseObjectName(msg->id, isEntity, name))
|
||||
{
|
||||
std::set<CStrW> selections; // TODO: get selections from user
|
||||
|
||||
// Create new unit
|
||||
if (isEntity)
|
||||
{
|
||||
CBaseEntity* base = g_EntityTemplateCollection.getTemplate(name);
|
||||
if (base) // (ignore errors)
|
||||
{
|
||||
g_PreviewUnit = g_UnitMan.CreateUnit(base->m_actorName, NULL);
|
||||
g_PreviewUnit->GetModel()->SetPlayerID(msg->player);
|
||||
g_PreviewUnit = g_UnitMan.CreateUnit(base->m_actorName, NULL, selections);
|
||||
g_PreviewUnit->SetPlayerID(msg->player);
|
||||
// TODO: variations
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_PreviewUnit = g_UnitMan.CreateUnit(CStr(name), NULL);
|
||||
g_PreviewUnit = g_UnitMan.CreateUnit(CStr(name), NULL, selections);
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +251,8 @@ BEGIN_COMMAND(CreateObject)
|
||||
CStrW name;
|
||||
if (ParseObjectName(d->id, isEntity, name))
|
||||
{
|
||||
std::set<CStrW> selections;
|
||||
|
||||
if (isEntity)
|
||||
{
|
||||
CBaseEntity* base = g_EntityTemplateCollection.getTemplate(name);
|
||||
@ -256,7 +260,7 @@ BEGIN_COMMAND(CreateObject)
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to load entity template '%ls'", name.c_str());
|
||||
else
|
||||
{
|
||||
HEntity ent = g_EntityManager.create(base, m_Pos, m_Angle);
|
||||
HEntity ent = g_EntityManager.create(base, m_Pos, m_Angle, selections);
|
||||
|
||||
if (! ent)
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", name.c_str());
|
||||
@ -270,7 +274,7 @@ BEGIN_COMMAND(CreateObject)
|
||||
}
|
||||
else
|
||||
{
|
||||
CUnit* unit = g_UnitMan.CreateUnit(CStr(name), NULL);
|
||||
CUnit* unit = g_UnitMan.CreateUnit(CStr(name), NULL, selections);
|
||||
if (! unit)
|
||||
LOG(ERROR, LOG_CATEGORY, "Failed to load nonentity actor '%ls'", name.c_str());
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user