![Mike Hoffert](/assets/img/avatar_default.png)
After all, how does one loot what the other faction does not have? Still, could be useful for some places, by forcing the opponent to go into "debt".
1281 lines
44 KiB
C++
1281 lines
44 KiB
C++
// ==============================================================
|
|
// This file is part of Glest (www.glest.org)
|
|
//
|
|
// Copyright (C) 2001-2008 Martiño Figueroa
|
|
//
|
|
// You can redistribute this code and/or modify it under
|
|
// the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version
|
|
// ==============================================================
|
|
|
|
#ifdef WIN32
|
|
#include <winsock2.h>
|
|
#include <winsock.h>
|
|
#endif
|
|
|
|
#include "unit_type.h"
|
|
#include <cassert>
|
|
#include "util.h"
|
|
#include "upgrade_type.h"
|
|
#include "resource_type.h"
|
|
#include "sound.h"
|
|
#include "logger.h"
|
|
#include "xml_parser.h"
|
|
#include "tech_tree.h"
|
|
#include "resource.h"
|
|
#include "renderer.h"
|
|
#include "game_util.h"
|
|
#include "unit_particle_type.h"
|
|
#include "faction.h"
|
|
#include "leak_dumper.h"
|
|
|
|
using namespace Shared::Xml;
|
|
using namespace Shared::Graphics;
|
|
using namespace Shared::Util;
|
|
|
|
namespace Glest{ namespace Game{
|
|
|
|
auto_ptr<CommandType> UnitType::ctHarvestEmergencyReturnCommandType(new HarvestEmergencyReturnCommandType());
|
|
// ===============================
|
|
// class Level
|
|
// ===============================
|
|
|
|
void Level::init(string name, int kills){
|
|
this->name= name;
|
|
this->kills= kills;
|
|
}
|
|
|
|
string Level::getName(bool translatedValue) const {
|
|
if(translatedValue == false) return name;
|
|
|
|
Lang &lang = Lang::getInstance();
|
|
return lang.getTechTreeString("LevelName_" + name,name.c_str());
|
|
}
|
|
|
|
void Level::saveGame(XmlNode *rootNode) const {
|
|
std::map<string,string> mapTagReplacements;
|
|
XmlNode *levelNode = rootNode->addChild("Level");
|
|
|
|
levelNode->addAttribute("name",name, mapTagReplacements);
|
|
levelNode->addAttribute("kills",intToStr(kills), mapTagReplacements);
|
|
}
|
|
|
|
const Level * Level::loadGame(const XmlNode *rootNode,const UnitType *ut) {
|
|
const Level *result = NULL;
|
|
if(rootNode->hasChild("Level") == true) {
|
|
const XmlNode *levelNode = rootNode->getChild("Level");
|
|
|
|
result = ut->getLevel(levelNode->getAttribute("name")->getValue());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// =====================================================
|
|
// class UnitType
|
|
// =====================================================
|
|
|
|
// ===================== PUBLIC ========================
|
|
|
|
const char *UnitType::propertyNames[]= {"burnable", "rotated_climb"};
|
|
|
|
// ==================== creation and loading ====================
|
|
|
|
UnitType::UnitType() : ProducibleType() {
|
|
|
|
countInVictoryConditions = ucvcNotSet;
|
|
meetingPointImage = NULL;
|
|
lightColor= Vec3f(0.f);
|
|
light= false;
|
|
multiSelect= false;
|
|
armorType= NULL;
|
|
rotatedBuildPos=0;
|
|
|
|
field = fLand;
|
|
id = 0;
|
|
meetingPoint = false;
|
|
rotationAllowed = false;
|
|
|
|
countUnitDeathInStats=false;
|
|
countUnitProductionInStats=false;
|
|
countUnitKillInStats=false;
|
|
countKillForUnitUpgrade=false;
|
|
|
|
|
|
for(int i=0; i<ccCount; ++i){
|
|
firstCommandTypeOfClass[i]= NULL;
|
|
}
|
|
|
|
for(int i=0; i<scCount; ++i){
|
|
firstSkillTypeOfClass[i] = NULL;
|
|
}
|
|
|
|
for(int i=0; i<pCount; ++i){
|
|
properties[i]= false;
|
|
}
|
|
|
|
for(int i=0; i<fieldCount; ++i){
|
|
fields[i]= false;
|
|
}
|
|
|
|
cellMap= NULL;
|
|
allowEmptyCellMap=false;
|
|
hpRegeneration= 0;
|
|
epRegeneration= 0;
|
|
maxUnitCount= 0;
|
|
maxHp=0;
|
|
startHpValue=0;
|
|
startHpPercentage=1.0;
|
|
startHpType=stValue;
|
|
maxEp=0;
|
|
startEpValue=0;
|
|
startEpPercentage=0;
|
|
startEpType=stValue;
|
|
armor=0;
|
|
sight=0;
|
|
size=0;
|
|
renderSize=0;
|
|
height=0;
|
|
|
|
addItemToVault(&(this->maxHp),this->maxHp);
|
|
addItemToVault(&(this->hpRegeneration),this->hpRegeneration);
|
|
addItemToVault(&(this->maxEp),this->maxEp);
|
|
addItemToVault(&(this->epRegeneration),this->epRegeneration);
|
|
addItemToVault(&(this->maxUnitCount),this->maxUnitCount);
|
|
addItemToVault(&(this->armor),this->armor);
|
|
addItemToVault(&(this->sight),this->sight);
|
|
addItemToVault(&(this->size),this->size);
|
|
addItemToVault(&(this->height),this->height);
|
|
}
|
|
|
|
UnitType::~UnitType(){
|
|
deleteValues(commandTypes.begin(), commandTypes.end());
|
|
commandTypes.clear();
|
|
deleteValues(skillTypes.begin(), skillTypes.end());
|
|
skillTypes.clear();
|
|
deleteValues(selectionSounds.getSounds().begin(), selectionSounds.getSounds().end());
|
|
selectionSounds.clearSounds();
|
|
deleteValues(commandSounds.getSounds().begin(), commandSounds.getSounds().end());
|
|
commandSounds.clearSounds();
|
|
delete [] cellMap;
|
|
cellMap=NULL;
|
|
//remove damageParticleSystemTypes
|
|
while(!damageParticleSystemTypes.empty()){
|
|
delete damageParticleSystemTypes.back();
|
|
damageParticleSystemTypes.pop_back();
|
|
}
|
|
}
|
|
|
|
void UnitType::preLoad(const string &dir) {
|
|
name= lastDir(dir);
|
|
}
|
|
|
|
void UnitType::loaddd(int id,const string &dir, const TechTree *techTree,
|
|
const string &techTreePath, const FactionType *factionType,
|
|
Checksum* checksum, Checksum* techtreeChecksum,
|
|
std::map<string,vector<pair<string, string> > > &loadedFileList,
|
|
bool validationMode) {
|
|
|
|
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
|
|
|
|
string currentPath = dir;
|
|
endPathWithSlash(currentPath);
|
|
string path = currentPath + name + ".xml";
|
|
string sourceXMLFile = path;
|
|
|
|
this->id= id;
|
|
|
|
try {
|
|
//Lang &lang= Lang::getInstance();
|
|
|
|
char szBuf[8096]="";
|
|
snprintf(szBuf,8096,Lang::getInstance().getString("LogScreenGameLoadingUnitType","",true).c_str(),formatString(this->getName(true)).c_str());
|
|
Logger::getInstance().add(szBuf, true);
|
|
|
|
//file load
|
|
checksum->addFile(path);
|
|
techtreeChecksum->addFile(path);
|
|
|
|
XmlTree xmlTree;
|
|
std::map<string,string> mapExtraTagReplacementValues;
|
|
mapExtraTagReplacementValues["$COMMONDATAPATH"] = techTreePath + "/commondata/";
|
|
xmlTree.load(path, Properties::getTagReplacementValues(&mapExtraTagReplacementValues));
|
|
loadedFileList[path].push_back(make_pair(dir,dir));
|
|
|
|
const XmlNode *unitNode= xmlTree.getRootNode();
|
|
|
|
const XmlNode *parametersNode= unitNode->getChild("parameters");
|
|
|
|
if(parametersNode->hasChild("count-in-victory-conditions") == true) {
|
|
bool countUnit = parametersNode->getChild("count-in-victory-conditions")->getAttribute("value")->getBoolValue();
|
|
if(countUnit == true) {
|
|
countInVictoryConditions = ucvcTrue;
|
|
}
|
|
else {
|
|
countInVictoryConditions = ucvcFalse;
|
|
}
|
|
}
|
|
|
|
//size
|
|
//checkItemInVault(&(this->size),this->size);
|
|
size= parametersNode->getChild("size")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->size),this->size);
|
|
renderSize=size;
|
|
if(parametersNode->hasChild("render-size")){
|
|
renderSize=parametersNode->getChild("render-size")->getAttribute("value")->getIntValue();
|
|
}
|
|
|
|
//height
|
|
//checkItemInVault(&(this->height),this->height);
|
|
height= parametersNode->getChild("height")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->height),this->height);
|
|
|
|
//maxHp
|
|
//checkItemInVault(&(this->maxHp),this->maxHp);
|
|
maxHp = parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->maxHp),this->maxHp);
|
|
|
|
//hpRegeneration
|
|
//checkItemInVault(&(this->hpRegeneration),this->hpRegeneration);
|
|
hpRegeneration= parametersNode->getChild("max-hp")->getAttribute("regeneration")->getIntValue();
|
|
addItemToVault(&(this->hpRegeneration),this->hpRegeneration);
|
|
|
|
//maxEp
|
|
//checkItemInVault(&(this->maxEp),this->maxEp);
|
|
maxEp= parametersNode->getChild("max-ep")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->maxEp),this->maxEp);
|
|
|
|
if(maxEp != 0) {
|
|
//epRegeneration
|
|
//checkItemInVault(&(this->epRegeneration),this->epRegeneration);
|
|
epRegeneration= parametersNode->getChild("max-ep")->getAttribute("regeneration")->getIntValue();
|
|
}
|
|
addItemToVault(&(this->epRegeneration),this->epRegeneration);
|
|
|
|
// Check that we don't use both start-value and start-percentage, as they are mutually
|
|
// exclusive
|
|
if(parametersNode->getChild("max-hp")->hasAttribute("start-value") &&
|
|
parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) {
|
|
throw megaglest_runtime_error("Unit " + name +
|
|
" has both start-value and start-percentage for HP", validationMode);
|
|
}
|
|
|
|
//startHpValue -- the *absolute* value to use for starting HP
|
|
if(parametersNode->getChild("max-hp")->hasAttribute("start-value")) {
|
|
//checkItemInVault(&(this->startEp),this->startEp);
|
|
startHpValue= parametersNode->getChild("max-hp")->getAttribute("start-value")->getIntValue();
|
|
startHpType= stValue;
|
|
}
|
|
addItemToVault(&(this->startHpValue),this->startHpValue);
|
|
|
|
//startHpPercentage -- the *relative* value to use for starting HP
|
|
if(parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) {
|
|
startHpPercentage= parametersNode->getChild("max-hp")->getAttribute("start-percentage")->getFloatValue();
|
|
startHpType= stPercentage;
|
|
}
|
|
|
|
// No start value set; use max HP before upgrades
|
|
if(!parametersNode->getChild("max-hp")->hasAttribute("start-value") &&
|
|
!parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) {
|
|
startHpValue= parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue();
|
|
startHpType= stValue;
|
|
}
|
|
addItemToVault(&(this->startHpPercentage),this->startHpPercentage);
|
|
|
|
// Check that we don't use both start-value and start-percentage, as they are mutually
|
|
// exclusive
|
|
if(parametersNode->getChild("max-ep")->hasAttribute("start-value") &&
|
|
parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) {
|
|
throw megaglest_runtime_error("Unit " + name +
|
|
" has both start-value and start-percentage for EP", validationMode);
|
|
}
|
|
|
|
//startEpValue -- the *absolute* value to use for starting EP
|
|
if(parametersNode->getChild("max-ep")->hasAttribute("start-value")) {
|
|
//checkItemInVault(&(this->startEp),this->startEp);
|
|
startEpValue= parametersNode->getChild("max-ep")->getAttribute("start-value")->getIntValue();
|
|
startEpType= stValue;
|
|
}
|
|
addItemToVault(&(this->startEpValue),this->startEpValue);
|
|
|
|
//startEpPercentage -- the *relative* value to use for starting EP
|
|
if(parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) {
|
|
startEpPercentage= parametersNode->getChild("max-ep")->getAttribute("start-percentage")->getFloatValue();
|
|
startEpType= stPercentage;
|
|
}
|
|
addItemToVault(&(this->startEpPercentage),this->startEpPercentage);
|
|
|
|
//maxUnitCount
|
|
if(parametersNode->hasChild("max-unit-count")) {
|
|
//checkItemInVault(&(this->maxUnitCount),this->maxUnitCount);
|
|
maxUnitCount= parametersNode->getChild("max-unit-count")->getAttribute("value")->getIntValue();
|
|
}
|
|
addItemToVault(&(this->maxUnitCount),this->maxUnitCount);
|
|
|
|
//armor
|
|
//checkItemInVault(&(this->armor),this->armor);
|
|
armor= parametersNode->getChild("armor")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->armor),this->armor);
|
|
|
|
//armor type string
|
|
string armorTypeName= parametersNode->getChild("armor-type")->getAttribute("value")->getRestrictedValue();
|
|
armorType= techTree->getArmorType(armorTypeName);
|
|
|
|
//sight
|
|
//checkItemInVault(&(this->sight),this->sight);
|
|
sight= parametersNode->getChild("sight")->getAttribute("value")->getIntValue();
|
|
addItemToVault(&(this->sight),this->sight);
|
|
|
|
//prod time
|
|
productionTime= parametersNode->getChild("time")->getAttribute("value")->getIntValue();
|
|
|
|
//multi selection
|
|
multiSelect= parametersNode->getChild("multi-selection")->getAttribute("value")->getBoolValue();
|
|
|
|
//cellmap
|
|
allowEmptyCellMap = false;
|
|
const XmlNode *cellMapNode= parametersNode->getChild("cellmap");
|
|
bool hasCellMap= cellMapNode->getAttribute("value")->getBoolValue();
|
|
if(hasCellMap == true) {
|
|
if(cellMapNode->getAttribute("allowEmpty",false) != NULL) {
|
|
allowEmptyCellMap = cellMapNode->getAttribute("allowEmpty")->getBoolValue();
|
|
}
|
|
|
|
cellMap= new bool[size*size];
|
|
for(int i=0; i<size; ++i){
|
|
const XmlNode *rowNode= cellMapNode->getChild("row", i);
|
|
string row= rowNode->getAttribute("value")->getRestrictedValue();
|
|
if((int)row.size() != size){
|
|
throw megaglest_runtime_error("Cellmap row has not the same length as unit size",validationMode);
|
|
}
|
|
for(int j=0; j < (int)row.size(); ++j){
|
|
cellMap[i*size+j]= row[j]=='0'? false: true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//levels
|
|
const XmlNode *levelsNode= parametersNode->getChild("levels");
|
|
levels.resize(levelsNode->getChildCount());
|
|
for(int i=0; i < (int)levels.size(); ++i){
|
|
const XmlNode *levelNode= levelsNode->getChild("level", i);
|
|
|
|
levels[i].init(
|
|
levelNode->getAttribute("name")->getRestrictedValue(),
|
|
levelNode->getAttribute("kills")->getIntValue());
|
|
}
|
|
|
|
//fields
|
|
const XmlNode *fieldsNode= parametersNode->getChild("fields");
|
|
for(int i=0; i < (int)fieldsNode->getChildCount(); ++i){
|
|
const XmlNode *fieldNode= fieldsNode->getChild("field", i);
|
|
string fieldName= fieldNode->getAttribute("value")->getRestrictedValue();
|
|
if(fieldName=="land"){
|
|
fields[fLand]= true;
|
|
}
|
|
else if(fieldName=="air"){
|
|
fields[fAir]= true;
|
|
}
|
|
else{
|
|
throw megaglest_runtime_error("Not a valid field: "+fieldName+": "+ path, validationMode);
|
|
}
|
|
}
|
|
|
|
if (fields[fLand]) {
|
|
field = fLand;
|
|
}
|
|
else if (fields[fAir]) {
|
|
field = fAir;
|
|
}
|
|
else {
|
|
throw megaglest_runtime_error("Unit has no field: " + path, validationMode);
|
|
}
|
|
|
|
//properties
|
|
const XmlNode *propertiesNode= parametersNode->getChild("properties");
|
|
for(int i = 0; i < (int)propertiesNode->getChildCount(); ++i) {
|
|
const XmlNode *propertyNode= propertiesNode->getChild("property", i);
|
|
string propertyName= propertyNode->getAttribute("value")->getRestrictedValue();
|
|
bool found= false;
|
|
for(int i = 0; i < pCount; ++i) {
|
|
if(propertyName == propertyNames[i]) {
|
|
properties[i]= true;
|
|
found= true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found) {
|
|
throw megaglest_runtime_error("Unknown property: " + propertyName, validationMode);
|
|
}
|
|
}
|
|
//damage-particles
|
|
if(parametersNode->hasChild("damage-particles")) {
|
|
const XmlNode *particleNode= parametersNode->getChild("damage-particles");
|
|
bool particleEnabled= particleNode->getAttribute("value")->getBoolValue();
|
|
|
|
if(particleEnabled) {
|
|
for(int i = 0; i < (int)particleNode->getChildCount(); ++i) {
|
|
const XmlNode *particleFileNode= particleNode->getChild("particle-file", i);
|
|
string path= particleFileNode->getAttribute("path")->getRestrictedValue();
|
|
UnitParticleSystemType *unitParticleSystemType= new UnitParticleSystemType();
|
|
|
|
unitParticleSystemType->load(particleFileNode, dir, currentPath + path,
|
|
&Renderer::getInstance(),loadedFileList, sourceXMLFile,
|
|
techTree->getPath());
|
|
loadedFileList[currentPath + path].push_back(make_pair(sourceXMLFile,particleFileNode->getAttribute("path")->getRestrictedValue()));
|
|
|
|
if(particleFileNode->getAttribute("minHp",false) != NULL && particleFileNode->getAttribute("maxHp",false) != NULL) {
|
|
unitParticleSystemType->setMinmaxEnabled(true);
|
|
unitParticleSystemType->setMinHp(particleFileNode->getAttribute("minHp")->getIntValue());
|
|
unitParticleSystemType->setMaxHp(particleFileNode->getAttribute("maxHp")->getIntValue());
|
|
|
|
if(particleFileNode->getAttribute("ispercentbased",false) != NULL) {
|
|
unitParticleSystemType->setMinmaxIsPercent(particleFileNode->getAttribute("ispercentbased")->getBoolValue());
|
|
}
|
|
|
|
}
|
|
|
|
damageParticleSystemTypes.push_back(unitParticleSystemType);
|
|
}
|
|
}
|
|
}
|
|
|
|
//light
|
|
const XmlNode *lightNode= parametersNode->getChild("light");
|
|
light= lightNode->getAttribute("enabled")->getBoolValue();
|
|
if(light){
|
|
lightColor.x= lightNode->getAttribute("red")->getFloatValue(0.f, 1.f);
|
|
lightColor.y= lightNode->getAttribute("green")->getFloatValue(0.f, 1.f);
|
|
lightColor.z= lightNode->getAttribute("blue")->getFloatValue(0.f, 1.f);
|
|
}
|
|
|
|
//rotationAllowed
|
|
if(parametersNode->hasChild("rotationAllowed")) {
|
|
const XmlNode *rotationAllowedNode= parametersNode->getChild("rotationAllowed");
|
|
rotationAllowed= rotationAllowedNode->getAttribute("value")->getBoolValue();
|
|
}
|
|
else
|
|
{
|
|
rotationAllowed=true;
|
|
}
|
|
|
|
std::map<string,int> sortedItems;
|
|
|
|
//unit requirements
|
|
bool hasDup = false;
|
|
const XmlNode *unitRequirementsNode= parametersNode->getChild("unit-requirements");
|
|
for(int i=0; i < (int)unitRequirementsNode->getChildCount(); ++i){
|
|
const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i);
|
|
string name= unitNode->getAttribute("name")->getRestrictedValue();
|
|
|
|
if(sortedItems.find(name) != sortedItems.end()) {
|
|
hasDup = true;
|
|
}
|
|
|
|
sortedItems[name] = 0;
|
|
}
|
|
if(hasDup) {
|
|
printf("WARNING, unit type [%s] has one or more duplicate unit requirements\n",this->getName(false).c_str());
|
|
}
|
|
|
|
for(std::map<string,int>::iterator iterMap = sortedItems.begin();
|
|
iterMap != sortedItems.end(); ++iterMap) {
|
|
unitReqs.push_back(factionType->getUnitType(iterMap->first));
|
|
}
|
|
sortedItems.clear();
|
|
hasDup = false;
|
|
|
|
//upgrade requirements
|
|
const XmlNode *upgradeRequirementsNode= parametersNode->getChild("upgrade-requirements");
|
|
for(int i=0; i < (int)upgradeRequirementsNode->getChildCount(); ++i){
|
|
const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i);
|
|
string name= upgradeReqNode->getAttribute("name")->getRestrictedValue();
|
|
|
|
if(sortedItems.find(name) != sortedItems.end()) {
|
|
hasDup = true;
|
|
}
|
|
|
|
sortedItems[name] = 0;
|
|
}
|
|
|
|
if(hasDup) {
|
|
printf("WARNING, unit type [%s] has one or more duplicate upgrade requirements\n",this->getName(false).c_str());
|
|
}
|
|
|
|
for(std::map<string,int>::iterator iterMap = sortedItems.begin();
|
|
iterMap != sortedItems.end(); ++iterMap) {
|
|
upgradeReqs.push_back(factionType->getUpgradeType(iterMap->first));
|
|
}
|
|
sortedItems.clear();
|
|
hasDup = false;
|
|
|
|
//resource requirements
|
|
const XmlNode *resourceRequirementsNode= parametersNode->getChild("resource-requirements");
|
|
costs.resize(resourceRequirementsNode->getChildCount());
|
|
for(int i = 0; i < (int)costs.size(); ++i) {
|
|
const XmlNode *resourceNode= resourceRequirementsNode->getChild("resource", i);
|
|
string name= resourceNode->getAttribute("name")->getRestrictedValue();
|
|
int amount= resourceNode->getAttribute("amount")->getIntValue();
|
|
|
|
if(sortedItems.find(name) != sortedItems.end()) {
|
|
hasDup = true;
|
|
}
|
|
sortedItems[name] = amount;
|
|
}
|
|
//if(hasDup || sortedItems.size() != costs.size()) printf("Found duplicate resource requirement, costs.size() = %d sortedItems.size() = %d\n",costs.size(),sortedItems.size());
|
|
|
|
if(hasDup) {
|
|
printf("WARNING, unit type [%s] has one or more duplicate resource requirements\n",this->getName(false).c_str());
|
|
}
|
|
|
|
if(sortedItems.size() < costs.size()) {
|
|
costs.resize(sortedItems.size());
|
|
}
|
|
int index = 0;
|
|
for(std::map<string,int>::iterator iterMap = sortedItems.begin();
|
|
iterMap != sortedItems.end(); ++iterMap) {
|
|
try {
|
|
costs[index].init(techTree->getResourceType(iterMap->first), iterMap->second);
|
|
index++;
|
|
}
|
|
catch(megaglest_runtime_error& ex) {
|
|
if(validationMode == false) {
|
|
throw;
|
|
}
|
|
else {
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s Cost: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),iterMap->second);
|
|
}
|
|
}
|
|
}
|
|
sortedItems.clear();
|
|
hasDup = false;
|
|
|
|
//resources stored
|
|
const XmlNode *resourcesStoredNode= parametersNode->getChild("resources-stored");
|
|
storedResources.resize(resourcesStoredNode->getChildCount());
|
|
for(int i=0; i < (int)storedResources.size(); ++i){
|
|
const XmlNode *resourceNode= resourcesStoredNode->getChild("resource", i);
|
|
string name= resourceNode->getAttribute("name")->getRestrictedValue();
|
|
int amount= resourceNode->getAttribute("amount")->getIntValue();
|
|
|
|
if(sortedItems.find(name) != sortedItems.end()) {
|
|
hasDup = true;
|
|
}
|
|
|
|
sortedItems[name] = amount;
|
|
}
|
|
|
|
if(hasDup) {
|
|
printf("WARNING, unit type [%s] has one or more duplicate stored resources\n",this->getName(false).c_str());
|
|
}
|
|
|
|
if(sortedItems.size() < storedResources.size()) {
|
|
storedResources.resize(sortedItems.size());
|
|
}
|
|
|
|
index = 0;
|
|
for(std::map<string,int>::iterator iterMap = sortedItems.begin();
|
|
iterMap != sortedItems.end(); ++iterMap) {
|
|
try {
|
|
storedResources[index].init(techTree->getResourceType(iterMap->first), iterMap->second);
|
|
index++;
|
|
}
|
|
catch(megaglest_runtime_error& ex) {
|
|
if(validationMode == false) {
|
|
throw;
|
|
}
|
|
else {
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s Store: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),iterMap->second);
|
|
}
|
|
}
|
|
}
|
|
sortedItems.clear();
|
|
|
|
// Lootable resources
|
|
if(parametersNode->hasChild("resources-death")) {
|
|
const XmlNode *deathResourcesNode= parametersNode->getChild("resources-death");
|
|
|
|
for(int i=0; i < deathResourcesNode->getChildCount(); ++i){
|
|
const XmlNode *resourceNode= deathResourcesNode->getChild("resource", i);
|
|
string name= resourceNode->getAttribute("name")->getRestrictedValue();
|
|
|
|
LootableResource resource;
|
|
resource.setResourceType(techTree->getResourceType(name));
|
|
|
|
// All attributes are optional, although nothing happens if they aren't used. They can
|
|
// be combined freely. Percentages will take affect before absolute values.
|
|
if(resourceNode->hasAttribute("amount-value")) {
|
|
resource.setAmountValue(resourceNode->getAttribute("amount-value")->getIntValue());
|
|
}
|
|
else {
|
|
resource.setAmountValue(0);
|
|
}
|
|
|
|
if(resourceNode->hasAttribute("amount-percentage")) {
|
|
resource.setAmountPercentage(resourceNode->getAttribute("amount-percentage")->getFloatValue());
|
|
}
|
|
else {
|
|
resource.setAmountPercentage(0);
|
|
}
|
|
|
|
if(resourceNode->hasAttribute("loss-value")) {
|
|
resource.setLossValue(resourceNode->getAttribute("loss-value")->getIntValue());
|
|
}
|
|
else {
|
|
resource.setLossValue(0);
|
|
}
|
|
|
|
if(resourceNode->hasAttribute("loss-percentage")) {
|
|
resource.setLossPercentage(resourceNode->getAttribute("loss-percentage")->getFloatValue());
|
|
}
|
|
else {
|
|
resource.setLossPercentage(0);
|
|
}
|
|
|
|
if(resourceNode->hasAttribute("allow-negative")) {
|
|
resource.setNegativeAllowed(resourceNode->getAttribute("allow-negative")->getBoolValue());
|
|
}
|
|
else {
|
|
resource.setNegativeAllowed(false);
|
|
}
|
|
|
|
lootableResources.push_back(resource);
|
|
|
|
// TODO: Add checks for duplicate resources
|
|
}
|
|
}
|
|
|
|
//image
|
|
const XmlNode *imageNode= parametersNode->getChild("image");
|
|
image= Renderer::getInstance().newTexture2D(rsGame);
|
|
if(image) {
|
|
image->load(imageNode->getAttribute("path")->getRestrictedValue(currentPath));
|
|
}
|
|
loadedFileList[imageNode->getAttribute("path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,imageNode->getAttribute("path")->getRestrictedValue()));
|
|
|
|
//image cancel
|
|
const XmlNode *imageCancelNode= parametersNode->getChild("image-cancel");
|
|
cancelImage= Renderer::getInstance().newTexture2D(rsGame);
|
|
if(cancelImage) {
|
|
cancelImage->load(imageCancelNode->getAttribute("path")->getRestrictedValue(currentPath));
|
|
}
|
|
loadedFileList[imageCancelNode->getAttribute("path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,imageCancelNode->getAttribute("path")->getRestrictedValue()));
|
|
|
|
//meeting point
|
|
const XmlNode *meetingPointNode= parametersNode->getChild("meeting-point");
|
|
meetingPoint= meetingPointNode->getAttribute("value")->getBoolValue();
|
|
if(meetingPoint) {
|
|
meetingPointImage= Renderer::getInstance().newTexture2D(rsGame);
|
|
if(meetingPointImage) {
|
|
meetingPointImage->load(meetingPointNode->getAttribute("image-path")->getRestrictedValue(currentPath));
|
|
}
|
|
loadedFileList[meetingPointNode->getAttribute("image-path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,meetingPointNode->getAttribute("image-path")->getRestrictedValue()));
|
|
}
|
|
|
|
//countUnitDeathInStats
|
|
if(parametersNode->hasChild("count-unit-death-in-stats")){
|
|
const XmlNode *countUnitDeathInStatsNode= parametersNode->getChild("count-unit-death-in-stats");
|
|
countUnitDeathInStats= countUnitDeathInStatsNode->getAttribute("value")->getBoolValue();
|
|
}
|
|
else {
|
|
countUnitDeathInStats=true;
|
|
}
|
|
//countUnitProductionInStats
|
|
if(parametersNode->hasChild("count-unit-production-in-stats")){
|
|
const XmlNode *countUnitProductionInStatsNode= parametersNode->getChild("count-unit-production-in-stats");
|
|
countUnitProductionInStats= countUnitProductionInStatsNode->getAttribute("value")->getBoolValue();
|
|
}
|
|
else {
|
|
countUnitProductionInStats=true;
|
|
}
|
|
//countUnitKillInStats
|
|
if(parametersNode->hasChild("count-unit-kill-in-stats")){
|
|
const XmlNode *countUnitKillInStatsNode= parametersNode->getChild("count-unit-kill-in-stats");
|
|
countUnitKillInStats= countUnitKillInStatsNode->getAttribute("value")->getBoolValue();
|
|
}
|
|
else {
|
|
countUnitKillInStats=true;
|
|
}
|
|
|
|
//countKillForUnitUpgrade
|
|
if(parametersNode->hasChild("count-kill-for-unit-upgrade")){
|
|
const XmlNode *countKillForUnitUpgradeNode= parametersNode->getChild("count-kill-for-unit-upgrade");
|
|
countKillForUnitUpgrade= countKillForUnitUpgradeNode->getAttribute("value")->getBoolValue();
|
|
} else {
|
|
countKillForUnitUpgrade=true;
|
|
}
|
|
|
|
if(countKillForUnitUpgrade == false){
|
|
// it makes no sense if we count it in stats but not for upgrades
|
|
countUnitKillInStats=false;
|
|
}
|
|
|
|
//selection sounds
|
|
const XmlNode *selectionSoundNode= parametersNode->getChild("selection-sounds");
|
|
if(selectionSoundNode->getAttribute("enabled")->getBoolValue()){
|
|
selectionSounds.resize((int)selectionSoundNode->getChildCount());
|
|
for(int i = 0; i < (int)selectionSounds.getSounds().size(); ++i) {
|
|
const XmlNode *soundNode= selectionSoundNode->getChild("sound", i);
|
|
string path= soundNode->getAttribute("path")->getRestrictedValue(currentPath);
|
|
StaticSound *sound= new StaticSound();
|
|
sound->load(path);
|
|
loadedFileList[path].push_back(make_pair(sourceXMLFile,soundNode->getAttribute("path")->getRestrictedValue()));
|
|
selectionSounds[i]= sound;
|
|
}
|
|
}
|
|
|
|
//command sounds
|
|
const XmlNode *commandSoundNode= parametersNode->getChild("command-sounds");
|
|
if(commandSoundNode->getAttribute("enabled")->getBoolValue()) {
|
|
commandSounds.resize((int)commandSoundNode->getChildCount());
|
|
for(int i = 0; i < (int)commandSoundNode->getChildCount(); ++i) {
|
|
const XmlNode *soundNode= commandSoundNode->getChild("sound", i);
|
|
string path= soundNode->getAttribute("path")->getRestrictedValue(currentPath);
|
|
StaticSound *sound= new StaticSound();
|
|
sound->load(path);
|
|
loadedFileList[path].push_back(make_pair(sourceXMLFile,soundNode->getAttribute("path")->getRestrictedValue()));
|
|
commandSounds[i]= sound;
|
|
}
|
|
}
|
|
|
|
//skills
|
|
|
|
const XmlNode *attackBoostsNode= NULL;
|
|
if(unitNode->hasChild("attack-boosts") == true) {
|
|
attackBoostsNode=unitNode->getChild("attack-boosts");
|
|
}
|
|
|
|
const XmlNode *skillsNode= unitNode->getChild("skills");
|
|
skillTypes.resize(skillsNode->getChildCount());
|
|
|
|
snprintf(szBuf,8096,Lang::getInstance().getString("LogScreenGameLoadingUnitTypeSkills","",true).c_str(),formatString(this->getName(true)).c_str(),skillTypes.size());
|
|
Logger::getInstance().add(szBuf, true);
|
|
|
|
for(int i = 0; i < (int)skillTypes.size(); ++i) {
|
|
const XmlNode *sn= skillsNode->getChild("skill", i);
|
|
const XmlNode *typeNode= sn->getChild("type");
|
|
string classId= typeNode->getAttribute("value")->getRestrictedValue();
|
|
SkillType *skillType= SkillTypeFactory::getInstance().newInstance(classId);
|
|
|
|
skillTypes[i]=NULL;
|
|
try {
|
|
skillType->load(sn, attackBoostsNode, dir, techTree, factionType, loadedFileList,sourceXMLFile);
|
|
skillTypes[i]= skillType;
|
|
}
|
|
catch(megaglest_runtime_error& ex) {
|
|
if(validationMode == false) {
|
|
throw;
|
|
}
|
|
else {
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s SkillType: %s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),classId.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//commands
|
|
const XmlNode *commandsNode= unitNode->getChild("commands");
|
|
commandTypes.resize(commandsNode->getChildCount());
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i) {
|
|
const XmlNode *commandNode= commandsNode->getChild("command", i);
|
|
const XmlNode *typeNode= commandNode->getChild("type");
|
|
string classId= typeNode->getAttribute("value")->getRestrictedValue();
|
|
CommandType *commandType= CommandTypeFactory::getInstance().newInstance(classId);
|
|
|
|
commandTypes[i]=NULL;
|
|
try {
|
|
commandType->load(i, commandNode, dir, techTree, factionType, *this,
|
|
loadedFileList,sourceXMLFile);
|
|
commandTypes[i]= commandType;
|
|
}
|
|
catch(megaglest_runtime_error& ex) {
|
|
if(validationMode == false) {
|
|
throw;
|
|
}
|
|
else {
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s CommandType:%s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),classId.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
computeFirstStOfClass();
|
|
computeFirstCtOfClass();
|
|
|
|
if(getFirstStOfClass(scStop)==NULL){
|
|
throw megaglest_runtime_error("Every unit must have at least one stop skill: "+ path,validationMode);
|
|
}
|
|
if(getFirstStOfClass(scDie)==NULL){
|
|
throw megaglest_runtime_error("Every unit must have at least one die skill: "+ path,validationMode);
|
|
}
|
|
|
|
}
|
|
//Exception handling (conversions and so on);
|
|
catch(megaglest_runtime_error& ex) {
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
|
|
throw megaglest_runtime_error("Error loading UnitType: " + path + "\nMessage: " + ex.what(),!ex.wantStackTrace());
|
|
}
|
|
catch(const exception &e){
|
|
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what());
|
|
throw megaglest_runtime_error("Error loading UnitType: " + path + "\nMessage: " + e.what());
|
|
}
|
|
|
|
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
|
|
}
|
|
|
|
// ==================== get ====================
|
|
|
|
const Level *UnitType::getLevel(string name) const {
|
|
const Level *result = NULL;
|
|
for(unsigned int i = 0; i < levels.size(); ++i) {
|
|
const Level &level = levels[i];
|
|
if(level.getName() == name) {
|
|
result = &level;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const CommandType *UnitType::getFirstCtOfClass(CommandClass commandClass) const{
|
|
if(firstCommandTypeOfClass[commandClass] == NULL) {
|
|
//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] commandClass = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,commandClass);
|
|
|
|
/*
|
|
for(int j=0; j<ccCount; ++j){
|
|
for(int i=0; i<commandTypes.size(); ++i){
|
|
if(commandTypes[i]->getClass()== CommandClass(j)){
|
|
return commandTypes[i];
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
|
|
}
|
|
return firstCommandTypeOfClass[commandClass];
|
|
}
|
|
|
|
const SkillType *UnitType::getFirstStOfClass(SkillClass skillClass) const{
|
|
if(firstSkillTypeOfClass[skillClass] == NULL) {
|
|
/*
|
|
for(int j= 0; j<scCount; ++j){
|
|
for(int i= 0; i<skillTypes.size(); ++i){
|
|
if(skillTypes[i]->getClass()== SkillClass(j)){
|
|
return skillTypes[i];
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
|
|
}
|
|
return firstSkillTypeOfClass[skillClass];
|
|
}
|
|
|
|
const HarvestCommandType *UnitType::getFirstHarvestCommand(const ResourceType *resourceType, const Faction *faction) const {
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i) {
|
|
if(commandTypes[i]->getClass() == ccHarvest) {
|
|
const HarvestCommandType *hct = static_cast<const HarvestCommandType*>(commandTypes[i]);
|
|
|
|
if (faction->reqsOk(hct) == false) {
|
|
continue;
|
|
}
|
|
|
|
if(hct->canHarvest(resourceType)) {
|
|
return hct;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const HarvestEmergencyReturnCommandType *UnitType::getFirstHarvestEmergencyReturnCommand() const {
|
|
const HarvestEmergencyReturnCommandType *result = dynamic_cast<const HarvestEmergencyReturnCommandType *>(ctHarvestEmergencyReturnCommandType.get());
|
|
return result;
|
|
}
|
|
|
|
const AttackCommandType *UnitType::getFirstAttackCommand(Field field) const{
|
|
//printf("$$$ Unit [%s] commandTypes.size() = %d\n",this->getName().c_str(),(int)commandTypes.size());
|
|
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i){
|
|
if(commandTypes[i] == NULL) {
|
|
throw megaglest_runtime_error("commandTypes[i] == NULL");
|
|
}
|
|
|
|
//printf("$$$ Unit [%s] i = %d, commandTypes[i] [%s]\n",this->getName().c_str(),(int)i, commandTypes[i]->toString().c_str());
|
|
if(commandTypes[i]->getClass()== ccAttack){
|
|
const AttackCommandType *act= dynamic_cast<const AttackCommandType*>(commandTypes[i]);
|
|
if(act != NULL && act->getAttackSkillType()->getAttackField(field)) {
|
|
//printf("## Unit [%s] i = %d, is found\n",this->getName().c_str(),(int)i);
|
|
return act;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const AttackStoppedCommandType *UnitType::getFirstAttackStoppedCommand(Field field) const{
|
|
//printf("$$$ Unit [%s] commandTypes.size() = %d\n",this->getName().c_str(),(int)commandTypes.size());
|
|
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i){
|
|
if(commandTypes[i] == NULL) {
|
|
throw megaglest_runtime_error("commandTypes[i] == NULL");
|
|
}
|
|
|
|
//printf("$$$ Unit [%s] i = %d, commandTypes[i] [%s]\n",this->getName().c_str(),(int)i, commandTypes[i]->toString().c_str());
|
|
if(commandTypes[i]->getClass()== ccAttackStopped){
|
|
const AttackStoppedCommandType *act= dynamic_cast<const AttackStoppedCommandType*>(commandTypes[i]);
|
|
if(act != NULL && act->getAttackSkillType()->getAttackField(field)) {
|
|
//printf("## Unit [%s] i = %d, is found\n",this->getName().c_str(),(int)i);
|
|
return act;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const RepairCommandType *UnitType::getFirstRepairCommand(const UnitType *repaired) const{
|
|
for(int i=0; i < (int)commandTypes.size(); ++i){
|
|
if(commandTypes[i]->getClass()== ccRepair){
|
|
const RepairCommandType *rct= static_cast<const RepairCommandType*>(commandTypes[i]);
|
|
if(rct->isRepairableUnitType(repaired)){
|
|
return rct;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool UnitType::hasEmptyCellMap() const {
|
|
//checkItemInVault(&(this->size),this->size);
|
|
bool result = (size > 0);
|
|
for(int i = 0; result == true && i < size; ++i) {
|
|
for(int j = 0; j < size; ++j){
|
|
if(cellMap[i*size+j] == true) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Vec2i UnitType::getFirstOccupiedCellInCellMap(Vec2i currentPos) const {
|
|
Vec2i cell = currentPos;
|
|
//printf("\n\n\n\n^^^^^^^^^^ currentPos [%s] size [%d]\n",currentPos.getString().c_str(),size);
|
|
|
|
//checkItemInVault(&(this->size),this->size);
|
|
if(hasCellMap() == true) {
|
|
for(int i = 0; i < size; ++i) {
|
|
for(int j = 0; j < size; ++j){
|
|
if(cellMap[i*size+j] == true) {
|
|
cell.x += i;
|
|
cell.y += j;
|
|
//printf("\n^^^^^^^^^^ cell [%s] i [%d] j [%d]\n",cell.getString().c_str(),i,j);
|
|
return cell;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return cell;
|
|
}
|
|
|
|
bool UnitType::getCellMapCell(int x, int y, CardinalDir facing) const {
|
|
assert(cellMap);
|
|
if(cellMap == NULL) {
|
|
throw megaglest_runtime_error("cellMap == NULL");
|
|
}
|
|
|
|
//checkItemInVault(&(this->size),this->size);
|
|
int tmp=0;
|
|
switch (facing) {
|
|
case CardinalDir::EAST:
|
|
tmp = y;
|
|
y = x;
|
|
x = size - tmp - 1;
|
|
break;
|
|
case CardinalDir::SOUTH:
|
|
x = size - x - 1;
|
|
y = size - y - 1;
|
|
break;
|
|
case CardinalDir::WEST:
|
|
tmp = x;
|
|
x = y;
|
|
y = size - tmp - 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return cellMap[y * size + x];
|
|
}
|
|
|
|
int UnitType::getStore(const ResourceType *rt) const{
|
|
for(int i=0; i < (int)storedResources.size(); ++i){
|
|
if(storedResources[i].getType()==rt){
|
|
return storedResources[i].getAmount();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const SkillType *UnitType::getSkillType(const string &skillName, SkillClass skillClass) const{
|
|
for(int i=0; i < (int)skillTypes.size(); ++i){
|
|
if(skillTypes[i]->getName()==skillName){
|
|
if(skillTypes[i]->getClass()==skillClass){
|
|
return skillTypes[i];
|
|
}
|
|
else{
|
|
throw megaglest_runtime_error("Skill \""+skillName+"\" is not of class \""+SkillType::skillClassToStr(skillClass));
|
|
}
|
|
}
|
|
}
|
|
throw megaglest_runtime_error("No skill named \""+skillName+"\"");
|
|
}
|
|
|
|
// ==================== totals ====================
|
|
|
|
int UnitType::getTotalMaxHp(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->maxHp),this->maxHp);
|
|
int result = maxHp + totalUpgrade->getMaxHp();
|
|
result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
int UnitType::getTotalMaxHpRegeneration(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->hpRegeneration),this->hpRegeneration);
|
|
int result = hpRegeneration + totalUpgrade->getMaxHpRegeneration();
|
|
//result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
int UnitType::getTotalMaxEp(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->maxEp),this->maxEp);
|
|
int result = maxEp + totalUpgrade->getMaxEp();
|
|
result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
int UnitType::getTotalMaxEpRegeneration(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->epRegeneration),this->epRegeneration);
|
|
int result = epRegeneration + totalUpgrade->getMaxEpRegeneration();
|
|
//result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
int UnitType::getTotalArmor(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->armor),this->armor);
|
|
int result = armor + totalUpgrade->getArmor();
|
|
result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
int UnitType::getTotalSight(const TotalUpgrade *totalUpgrade) const {
|
|
checkItemInVault(&(this->sight),this->sight);
|
|
int result = sight + totalUpgrade->getSight();
|
|
result = max(0,result);
|
|
return result;
|
|
}
|
|
|
|
// ==================== has ====================
|
|
|
|
bool UnitType::hasSkillClass(SkillClass skillClass) const {
|
|
return firstSkillTypeOfClass[skillClass]!=NULL;
|
|
}
|
|
|
|
bool UnitType::hasCommandType(const CommandType *commandType) const {
|
|
assert(commandType!=NULL);
|
|
|
|
const HarvestEmergencyReturnCommandType *result = dynamic_cast<const HarvestEmergencyReturnCommandType *>(ctHarvestEmergencyReturnCommandType.get());
|
|
if(commandType == result) {
|
|
return true;
|
|
}
|
|
|
|
for(int i=0; i < (int)commandTypes.size(); ++i) {
|
|
if(commandTypes[i]==commandType) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UnitType::hasSkillType(const SkillType *skillType) const {
|
|
assert(skillType!=NULL);
|
|
for(int i=0; i < (int)skillTypes.size(); ++i) {
|
|
if(skillTypes[i]==skillType) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UnitType::isOfClass(UnitClass uc) const{
|
|
switch(uc){
|
|
case ucWarrior:
|
|
return hasSkillClass(scAttack) && !hasSkillClass(scHarvest);
|
|
case ucWorker:
|
|
return hasSkillClass(scBuild) || hasSkillClass(scRepair)|| hasSkillClass(scHarvest);
|
|
case ucBuilding:
|
|
return hasSkillClass(scBeBuilt);
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ==================== PRIVATE ====================
|
|
|
|
void UnitType::computeFirstStOfClass() {
|
|
for(int j= 0; j < scCount; ++j) {
|
|
firstSkillTypeOfClass[j]= NULL;
|
|
for(int i= 0; i < (int)skillTypes.size(); ++i) {
|
|
if(skillTypes[i] != NULL && skillTypes[i]->getClass()== SkillClass(j)) {
|
|
firstSkillTypeOfClass[j]= skillTypes[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UnitType::computeFirstCtOfClass() {
|
|
for(int j = 0; j < ccCount; ++j) {
|
|
firstCommandTypeOfClass[j]= NULL;
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i) {
|
|
if(commandTypes[i] != NULL && commandTypes[i]->getClass() == CommandClass(j)) {
|
|
firstCommandTypeOfClass[j] = commandTypes[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const CommandType* UnitType::findCommandTypeById(int id) const{
|
|
const HarvestEmergencyReturnCommandType *result = dynamic_cast<const HarvestEmergencyReturnCommandType *>(ctHarvestEmergencyReturnCommandType.get());
|
|
if(result != NULL && id == result->getId()) {
|
|
return result;
|
|
}
|
|
|
|
for(int i=0; i < getCommandTypeCount(); ++i) {
|
|
const CommandType *commandType= getCommandType(i);
|
|
if(commandType->getId() == id){
|
|
return commandType;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const CommandType *UnitType::getCommandType(int i) const {
|
|
if(i >= (int)commandTypes.size()) {
|
|
char szBuf[8096]="";
|
|
snprintf(szBuf,8096,"In [%s::%s Line: %d] i >= commandTypes.size(), i = %d, commandTypes.size() = " MG_SIZE_T_SPECIFIER "",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,commandTypes.size());
|
|
throw megaglest_runtime_error(szBuf);
|
|
}
|
|
return commandTypes[i];
|
|
}
|
|
|
|
string UnitType::getCommandTypeListDesc() const {
|
|
string desc = "Commands: ";
|
|
for(int i=0; i<getCommandTypeCount(); ++i){
|
|
const CommandType* commandType= getCommandType(i);
|
|
desc += " id = " + intToStr(commandType->getId()); + " toString: " + commandType->toString(false);
|
|
}
|
|
return desc;
|
|
|
|
}
|
|
|
|
string UnitType::getReqDesc(bool translatedValue) const{
|
|
Lang &lang= Lang::getInstance();
|
|
//string desc = "Limits: ";
|
|
string resultTxt="";
|
|
|
|
checkItemInVault(&(this->maxUnitCount),this->maxUnitCount);
|
|
if(getMaxUnitCount() > 0) {
|
|
resultTxt += "\n" + lang.getString("MaxUnitCount") + " " + intToStr(getMaxUnitCount());
|
|
}
|
|
if(resultTxt == "")
|
|
return ProducibleType::getReqDesc(translatedValue);
|
|
else
|
|
return ProducibleType::getReqDesc(translatedValue)+"\n" + lang.getString("Limits") + " " + resultTxt;
|
|
}
|
|
|
|
string UnitType::getName(bool translatedValue) const {
|
|
if(translatedValue == false) return name;
|
|
|
|
Lang &lang = Lang::getInstance();
|
|
return lang.getTechTreeString("UnitTypeName_" + name,name.c_str());
|
|
}
|
|
|
|
std::string UnitType::toString() const {
|
|
std::string result = "Unit Name: [" + name + "] id = " + intToStr(id);
|
|
result += " maxHp = " + intToStr(maxHp);
|
|
result += " hpRegeneration = " + intToStr(hpRegeneration);
|
|
result += " maxEp = " + intToStr(maxEp);
|
|
result += " startEpValue = " + intToStr(startEpValue);
|
|
result += " startEpPercentage = " + intToStr(startEpPercentage);
|
|
result += " epRegeneration = " + intToStr(epRegeneration);
|
|
result += " maxUnitCount = " + intToStr(getMaxUnitCount());
|
|
|
|
|
|
for(int i = 0; i < fieldCount; i++) {
|
|
result += " fields index = " + intToStr(i) + " value = " + intToStr(fields[i]);
|
|
}
|
|
for(int i = 0; i < pCount; i++) {
|
|
result += " properties index = " + intToStr(i) + " value = " + intToStr(properties[i]);
|
|
}
|
|
|
|
result += " armor = " + intToStr(armor);
|
|
|
|
if(armorType != NULL) {
|
|
result += " armorType Name: [" + armorType->getName(false) + " id = " + intToStr(armorType->getId());
|
|
}
|
|
|
|
result += " light = " + intToStr(light);
|
|
result += " lightColor = " + lightColor.getString();
|
|
result += " multiSelect = " + intToStr(multiSelect);
|
|
result += " sight = " + intToStr(sight);
|
|
result += " size = " + intToStr(size);
|
|
result += " height = " + intToStr(height);
|
|
result += " rotatedBuildPos = " + floatToStr(rotatedBuildPos,6);
|
|
result += " rotationAllowed = " + intToStr(rotationAllowed);
|
|
|
|
if(cellMap != NULL) {
|
|
result += " cellMap: [" + intToStr(size) + "]";
|
|
for(int i = 0; i < size; ++i) {
|
|
for(int j = 0; j < size; ++j){
|
|
result += " i = " + intToStr(i) + " j = " + intToStr(j) + " value = " + intToStr(cellMap[i*size+j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
result += " skillTypes: [" + intToStr(skillTypes.size()) + "]";
|
|
for(int i = 0; i < (int)skillTypes.size(); ++i) {
|
|
result += " i = " + intToStr(i) + " " + skillTypes[i]->toString(false);
|
|
}
|
|
|
|
result += " commandTypes: [" + intToStr(commandTypes.size()) + "]";
|
|
for(int i = 0; i < (int)commandTypes.size(); ++i) {
|
|
result += " i = " + intToStr(i) + " " + commandTypes[i]->toString(false);
|
|
}
|
|
|
|
result += " storedResources: [" + intToStr(storedResources.size()) + "]";
|
|
for(int i = 0; i < (int)storedResources.size(); ++i) {
|
|
result += " i = " + intToStr(i) + " " + storedResources[i].getDescription(false);
|
|
}
|
|
|
|
result += " levels: [" + intToStr(levels.size()) + "]";
|
|
for(int i = 0; i < (int)levels.size(); ++i) {
|
|
result += " i = " + intToStr(i) + " " + levels[i].getName();
|
|
}
|
|
|
|
result += " meetingPoint = " + intToStr(meetingPoint);
|
|
|
|
result += " countInVictoryConditions = " + intToStr(countInVictoryConditions);
|
|
|
|
return result;
|
|
}
|
|
|
|
}}//end namespace
|