0ad/source/simulation/BaseFormation.cpp
pyrolink 3408b078b7 #Unit formations and rank textures
-Added functionality for "casting" and creating net messages (without
JS)
-Rank textures-specified in XML
-Formations-currently don't attack correctly (i.e. travel like mobs) and
don't switch to their movement formations when tasked to go somewhere
(the pathfinder doesn't give any heads up about destination reached, so
there's no way to change back to the original).  Also, base switching is
untested so no garuntees for next and prior formation things.

This was SVN commit r3710.
2006-03-31 03:30:34 +00:00

188 lines
5.3 KiB
C++

#include "precompiled.h"
#include "BaseFormation.h"
#include "CLogger.h"
#include "CStr.h"
#define LOG_CATEGORY "Formation"
CBaseFormation::CBaseFormation()
{
m_numSlots = 0;
}
bool CBaseFormation::loadXML(CStr filename)
{
CXeromyces XeroFile;
if (XeroFile.Load(filename) != PSRETURN_OK)
return false;
#define EL(x) int el_##x = XeroFile.getElementID(#x)
#define AT(x) int at_##x = XeroFile.getAttributeID(#x)
EL(formation);
EL(fl);
EL(rk);
EL(blank);
AT(tag);
AT(bonus);
AT(bonustype);
AT(bonusval);
AT(penalty);
AT(penaltytype);
AT(penaltyval);
AT(required);
AT(next);
AT(prior);
AT(movement);
AT(filespacing);
AT(rankspacing);
AT(category);
AT(order);
#undef AT
#undef EL
XMBElement Root = XeroFile.getRoot();
if( Root.getNodeName() != el_formation )
{
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: XML root was not \"Formation\" in file %s. Load failed.", filename.c_str() );
return( false );
}
//Load in single attributes
XMBAttributeList Attributes = Root.getAttributes();
for ( int i=0; i<Attributes.Count; ++i )
{
XMBAttribute Attr = Attributes.item(i);
if ( Attr.Name == at_tag )
m_tag = CStr(Attr.Value);
else if ( Attr.Name == at_bonus )
m_bonus = CStr(Attr.Value);
else if ( Attr.Name == at_bonustype )
m_bonusType = CStr(Attr.Value);
else if ( Attr.Name == at_bonusval )
m_bonusVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_penalty )
m_penalty = CStr(Attr.Value);
else if ( Attr.Name == at_penaltytype )
m_penaltyType = CStr(Attr.Value);
else if ( Attr.Name == at_penaltyval )
m_penaltyVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_required)
m_required = CStr(Attr.Value).ToInt();
else if ( Attr.Name == at_next )
m_next = CStr(Attr.Value);
else if ( Attr.Name == at_prior )
m_prior = CStr(Attr.Value);
else if ( Attr.Name == at_movement )
m_movement = CStr(Attr.Value);
else if ( Attr.Name == at_rankspacing )
m_rankSpacing = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_filespacing )
m_fileSpacing = CStr(Attr.Value).ToFloat();
else
{
CStr invAttr = XeroFile.getAttributeString(Attr.Name);
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Invalid attribute defined in formation file %s. Load failed.", filename.c_str() );
return( false );
}
}
XMBElementList RootChildren = Root.getChildNodes();
int file=0;
int rank=0;
int maxrank=0;
//Read in files and ranks
for (int i = 0; i < RootChildren.Count; ++i)
{
XMBElement RootChild = RootChildren.item(i);
int ChildName = RootChild.getNodeName();
if ( ChildName == el_fl )
{
rank = 0;
XMBAttributeList FileAttribList = RootChild.getAttributes();
//Load default category
CStr FileCatValue = FileAttribList.getNamedItem(at_category);
//Specific slots in this file (row)
XMBElementList RankNodes = RootChild.getChildNodes();
for ( int r=0; r<RankNodes.Count; ++r )
{
XMBElement Rank = RankNodes.item(r);
if ( Rank.getNodeName() == el_blank )
{
++rank;
continue;
}
else if ( Rank.getNodeName() != el_rk )
return false;
//error
XMBAttributeList RankAttribList = Rank.getAttributes();
int order = CStr( RankAttribList.getNamedItem(at_order) ).ToInt();
CStr category = CStr( RankAttribList.getNamedItem(at_category) );
if( order <= 0 )
{
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Invalid (negative number or 0) order defined in formation file %s. The game will try to continue anyway.", filename.c_str() );
continue;
}
--order; //We need this to be in line with arrays, so start at 0
//if ( category.length() )
//AssignCategory(order, category);
//else
AssignCategory(order, FileCatValue);
m_slots[order].fileOff = file * m_fileSpacing;
m_slots[order].rankOff = rank * m_rankSpacing;
++m_numSlots;
++rank;
}
if ( rank > maxrank )
maxrank = rank;
++file;
} //if el_fl
else if ( ChildName == el_blank )
++file;
}
float centerx = maxrank * m_rankSpacing / 2.0f;
float centery = file * m_fileSpacing / 2.0f;
//Here we check to make sure no order was skipped over. If so, failure, because we rely
//on a linearly accessible slots in entityformation.cpp.
for ( int i=0; i<m_numSlots; ++i )
{
if ( m_slots.find(i) == m_slots.end() )
{
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Missing orders in %s. Load failed.", filename.c_str() );
return false;
}
else
{
m_slots[i].rankOff = m_slots[i].rankOff - centerx;
m_slots[i].fileOff = m_slots[i].fileOff - centery;
}
}
return true;
}
void CBaseFormation::AssignCategory(int order, CStr category)
{
category.Remove( CStr(",") );
category = category + " "; //So the final word will be pushed as well
CStr temp;
//Push categories until last space
while ( ( temp = category.BeforeFirst(" ") ) != "" )
{
m_slots[order].category.push_back(temp);
//Don't use remove because certain categories could be substrings of others
size_t off = category.find(temp);
category.erase( off, temp.length() );
}
}