// ============================================================== // 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 // ============================================================== #include "faction.h" #include #include "unit.h" #include "unit_particle_type.h" #include "world.h" #include "upgrade.h" #include "map.h" #include "command.h" #include "object.h" #include "config.h" #include "skill_type.h" #include "core_data.h" #include "renderer.h" #include "game.h" #include "socket.h" #include "sound_renderer.h" #include "leak_dumper.h" using namespace Shared::Graphics; using namespace Shared::Util; namespace Glest{ namespace Game{ const float CHANGE_COMMAND_SPEED = 325.0; //Mutex Unit::mutexDeletedUnits; //map Unit::deletedUnits; const int UnitPathBasic::maxBlockCount= GameConstants::updateFps / 2; #ifdef LEAK_CHECK_UNITS std::map UnitPathBasic::mapMemoryList; std::map Unit::mapMemoryList; std::map Unit::mapMemoryList2; #endif UnitPathBasic::UnitPathBasic() : UnitPathInterface() { #ifdef LEAK_CHECK_UNITS UnitPathBasic::mapMemoryList[this]=true; #endif this->blockCount = 0; this->pathQueue.clear(); this->lastPathCacheQueue.clear(); this->map = NULL; } UnitPathBasic::~UnitPathBasic() { this->blockCount = 0; this->pathQueue.clear(); this->lastPathCacheQueue.clear(); this->map = NULL; #ifdef LEAK_CHECK_UNITS UnitPathBasic::mapMemoryList.erase(this); #endif } #ifdef LEAK_CHECK_UNITS void UnitPathBasic::dumpMemoryList() { printf("===== START report of Unfreed UnitPathBasic pointers =====\n"); for(std::map::iterator iterMap = UnitPathBasic::mapMemoryList.begin(); iterMap != UnitPathBasic::mapMemoryList.end(); ++iterMap) { printf("************** ==> Unfreed UnitPathBasic pointer [%p]\n",iterMap->first); if(Unit::mapMemoryList2.find(iterMap->first) != Unit::mapMemoryList2.end()) { printf("Found owner unit id [%d]\n",Unit::mapMemoryList2[iterMap->first]); } } } #endif bool UnitPathBasic::isEmpty() const { return pathQueue.empty(); } bool UnitPathBasic::isBlocked() const { return blockCount >= maxBlockCount; } bool UnitPathBasic::isStuck() const { return (isBlocked() == true && blockCount >= (maxBlockCount * 2)); } void UnitPathBasic::clear() { pathQueue.clear(); lastPathCacheQueue.clear(); blockCount= 0; } void UnitPathBasic::incBlockCount() { pathQueue.clear(); lastPathCacheQueue.clear(); blockCount++; } void UnitPathBasic::add(const Vec2i &path) { if(this->map != NULL) { if(this->map->isInside(path) == false) { throw runtime_error("Invalid map path position = " + path.getString() + " map w x h = " + intToStr(map->getW()) + " " + intToStr(map->getH())); } else if(this->map->isInsideSurface(this->map->toSurfCoords(path)) == false) { throw runtime_error("Invalid map surface path position = " + path.getString() + " map surface w x h = " + intToStr(map->getSurfaceW()) + " " + intToStr(map->getSurfaceH())); } } pathQueue.push_back(path); } void UnitPathBasic::addToLastPathCache(const Vec2i &path) { if(this->map != NULL) { if(this->map->isInside(path) == false) { throw runtime_error("Invalid map path position = " + path.getString() + " map w x h = " + intToStr(map->getW()) + " " + intToStr(map->getH())); } else if(this->map->isInsideSurface(this->map->toSurfCoords(path)) == false) { throw runtime_error("Invalid map surface path position = " + path.getString() + " map surface w x h = " + intToStr(map->getSurfaceW()) + " " + intToStr(map->getSurfaceH())); } } lastPathCacheQueue.push_back(path); } Vec2i UnitPathBasic::pop(bool removeFrontPos) { if(pathQueue.size() <= 0) { throw runtime_error("pathQueue.size() = " + intToStr(pathQueue.size())); } Vec2i p= pathQueue.front(); if(removeFrontPos == true) { pathQueue.erase(pathQueue.begin()); } return p; } std::string UnitPathBasic::toString() const { std::string result = ""; result = "unit path blockCount = " + intToStr(blockCount) + " pathQueue size = " + intToStr(pathQueue.size()); for(int idx = 0; idx < pathQueue.size(); ++idx) { result += " index = " + intToStr(idx) + " " + pathQueue[idx].getString(); } return result; } // ===================================================== // class UnitPath // ===================================================== void WaypointPath::condense() { if (size() < 2) { return; } iterator prev, curr; prev = curr = begin(); while (++curr != end()) { if (prev->dist(*curr) < 3.f) { prev = erase(prev); } else { ++prev; } } } std::string UnitPath::toString() const { std::string result = ""; result = "unit path blockCount = " + intToStr(blockCount) + " pathQueue size = " + intToStr(size()); result += " path = "; for (const_iterator it = begin(); it != end(); ++it) { result += " [" + intToStr(it->x) + "," + intToStr(it->y) + "]"; } return result; } // ===================================================== // class UnitReference // ===================================================== UnitReference::UnitReference(){ id= -1; faction= NULL; } UnitReference & UnitReference::operator=(const Unit *unit){ if(unit==NULL){ id= -1; faction= NULL; } else{ id= unit->getId(); faction= unit->getFaction(); } return *this; } Unit *UnitReference::getUnit() const{ if(faction!=NULL){ return faction->findUnit(id); } return NULL; } const bool checkMemory = false; static map memoryObjectList; UnitAttackBoostEffect::UnitAttackBoostEffect() { if(checkMemory) { printf("++ Create UnitAttackBoostEffect [%p] before count = %d\n",this,memoryObjectList[this]); memoryObjectList[this]++; printf("++ Create UnitAttackBoostEffect [%p] after count = %d\n",this,memoryObjectList[this]); } boost = NULL; source = NULL; ups = NULL; upst = NULL; } UnitAttackBoostEffect::~UnitAttackBoostEffect() { if(checkMemory) { printf("-- Delete UnitAttackBoostEffect [%p] count = %d\n",this,memoryObjectList[this]); memoryObjectList[this]--; assert(memoryObjectList[this] == 0); } if(ups != NULL) { bool particleValid = Renderer::getInstance().validateParticleSystemStillExists(ups,rsGame); if(particleValid == true) { ups->fade(); vector particleSystemToRemove; particleSystemToRemove.push_back(ups); Renderer::getInstance().cleanupUnitParticleSystems(particleSystemToRemove,rsGame); ups = NULL; } } delete upst; upst = NULL; } UnitAttackBoostEffectOriginator::UnitAttackBoostEffectOriginator() { skillType = NULL; currentAppliedEffect = NULL; } UnitAttackBoostEffectOriginator::~UnitAttackBoostEffectOriginator() { delete currentAppliedEffect; currentAppliedEffect = NULL; } // ===================================================== // class Unit // ===================================================== const float Unit::speedDivider= 100.f; const int Unit::maxDeadCount= 1000; //time in until the corpse disapears - should be about 40 seconds const float Unit::highlightTime= 0.5f; const int Unit::invalidId= -1; //set Unit::livingUnits; //set Unit::livingUnitsp; // ============================ Constructor & destructor ============================= Game *Unit::game = NULL; Unit::Unit(int id, UnitPathInterface *unitpath, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map, CardinalDir placeFacing):id(id) { #ifdef LEAK_CHECK_UNITS Unit::mapMemoryList[this]=true; #endif changedActiveCommand = false; lastSynchDataString=""; modelFacing = CardinalDir::NORTH; lastStuckFrame = 0; lastStuckPos = Vec2i(0,0); lastPathfindFailedFrame = 0; lastPathfindFailedPos = Vec2i(0,0); usePathfinderExtendedMaxNodes = false; this->currentAttackBoostOriginatorEffect.skillType = NULL; targetRotationZ=.0f; targetRotationX=.0f; rotationZ=.0f; rotationX=.0f; this->unitPath = unitpath; this->unitPath->setMap(map); RandomGen random; if(map->isInside(pos) == false || map->isInsideSurface(map->toSurfCoords(pos)) == false) { throw runtime_error("#2 Invalid path position = " + pos.getString()); } this->pos=pos; this->type=type; this->faction=faction; this->map= map; this->targetRef = NULL; this->targetField = fLand; this->targetVec = Vec3f(0.0); this->targetPos = Vec2i(0); this->lastRenderFrame = 0; this->visible = true; this->retryCurrCommandCount=0; this->screenPos = Vec3f(0.0); this->ignoreCheckCommand = false; this->inBailOutAttempt = false; this->lastHarvestResourceTarget.first = Vec2i(0); //this->lastBadHarvestListPurge = 0; level= NULL; loadType= NULL; setModelFacing(placeFacing); Config &config= Config::getInstance(); showUnitParticles = config.getBool("UnitParticles","true"); maxQueuedCommandDisplayCount = config.getInt("MaxQueuedCommandDisplayCount","15"); lastPos= pos; progress= 0; lastAnimProgress= 0; animProgress= 0; progress2= 0; kills= 0; enemyKills = 0; loadCount= 0; ep= 0; deadCount= 0; hp= type->getMaxHp()/20; toBeUndertaken= false; highlight= 0.f; meetingPos= pos; alive= true; if (type->hasSkillClass(scBeBuilt) == false) { float rot= 0.f; random.init(id); rot+= random.randRange(-5, 5); rotation= rot; lastRotation= rot; targetRotation= rot; } // else it was set appropriately in setModelFacing() if(getType()->getField(fAir)) { currField = fAir; } if(getType()->getField(fLand)) { currField = fLand; } fire= NULL; computeTotalUpgrade(); //starting skill this->lastModelIndexForCurrSkillType = -1; this->animationRandomCycleCount = 0; this->currSkill = getType()->getFirstStOfClass(scStop); this->currentAttackBoostOriginatorEffect.skillType = this->currSkill; this->faction->addLivingUnits(id); this->faction->addLivingUnitsp(this); addItemToVault(&this->hp,this->hp); addItemToVault(&this->ep,this->ep); // if(isUnitDeleted(this) == true) { // MutexSafeWrapper safeMutex(&mutexDeletedUnits,string(__FILE__) + "_" + intToStr(__LINE__)); // deletedUnits.erase(this); // } logSynchData(__FILE__,__LINE__); } Unit::~Unit() { badHarvestPosList.clear(); this->faction->deleteLivingUnits(id); this->faction->deleteLivingUnitsp(this); //remove commands static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); changedActiveCommand = false; while(commands.empty() == false) { delete commands.back(); commands.pop_back(); } safeMutex.ReleaseLock(); // If the unit is not visible we better make sure we cleanup associated particles if(this->getVisible() == false) { Renderer::getInstance().cleanupUnitParticleSystems(unitParticleSystems,rsGame); Renderer::getInstance().cleanupParticleSystems(fireParticleSystems,rsGame); // Must set this to null of it will be used below in stopDamageParticles() if(Renderer::getInstance().validateParticleSystemStillExists(fire,rsGame) == false) { fire = NULL; } } // fade(and by this remove) all unit particle systems queuedUnitParticleSystemTypes.clear(); while(unitParticleSystems.empty() == false) { unitParticleSystems.back()->fade(); unitParticleSystems.pop_back(); } stopDamageParticles(true); while(currentAttackBoostEffects.empty() == false) { //UnitAttackBoostEffect &effect = currentAttackBoostEffects.back(); UnitAttackBoostEffect *ab = currentAttackBoostEffects.back(); delete ab; currentAttackBoostEffects.pop_back(); } delete currentAttackBoostOriginatorEffect.currentAppliedEffect; currentAttackBoostOriginatorEffect.currentAppliedEffect = NULL; #ifdef LEAK_CHECK_UNITS Unit::mapMemoryList2[this->unitPath] = this->getId(); #endif delete this->unitPath; this->unitPath = NULL; Renderer &renderer= Renderer::getInstance(); renderer.removeUnitFromQuadCache(this); //MutexSafeWrapper safeMutex1(&mutexDeletedUnits,string(__FILE__) + "_" + intToStr(__LINE__)); //deletedUnits[this]=true; #ifdef LEAK_CHECK_UNITS Unit::mapMemoryList.erase(this); #endif } #ifdef LEAK_CHECK_UNITS void Unit::dumpMemoryList() { printf("===== START report of Unfreed Unit pointers =====\n"); for(std::map::iterator iterMap = Unit::mapMemoryList.begin(); iterMap != Unit::mapMemoryList.end(); ++iterMap) { printf("************** ==> Unfreed Unit pointer [%p]\n",iterMap->first); } } #endif //bool Unit::isUnitDeleted(void *unit) { // bool result = false; // MutexSafeWrapper safeMutex(&mutexDeletedUnits,string(__FILE__) + "_" + intToStr(__LINE__)); // if(deletedUnits.find(unit) != deletedUnits.end()) { // result = true; // } // return result; //} void Unit::setModelFacing(CardinalDir value) { modelFacing = value; lastRotation = targetRotation = rotation = value * 90.f; } // ====================================== get ====================================== int Unit::getFactionIndex() const{ return faction->getIndex(); } int Unit::getTeam() const{ return faction->getTeam(); } Vec2i Unit::getCenteredPos() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return pos + Vec2i(type->getSize()/2, type->getSize()/2); } Vec2f Unit::getFloatCenteredPos() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return Vec2f(pos.x-0.5f+type->getSize()/2.f, pos.y-0.5f+type->getSize()/2.f); } Vec2i Unit::getCellPos() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(type->hasCellMap()) { if( type->hasEmptyCellMap() == false || type->getAllowEmptyCellMap() == true) { //find nearest pos to center that is free Vec2i centeredPos= getCenteredPos(); float nearestDist= -1.f; Vec2i nearestPos= pos; for(int i=0; igetSize(); ++i){ for(int j=0; jgetSize(); ++j){ if(type->getCellMapCell(i, j, modelFacing)){ Vec2i currPos= pos + Vec2i(i, j); float dist= currPos.dist(centeredPos); if(nearestDist==-1.f || distgetProperty(UnitType::pRotatedClimb) && currSkill->getClass()==scMove){ //if(currSkill->getClass()==scMove) if(lastPos != pos){ // targetPosCalc ( maybe also sometimes needed if no move ? terrain flatting... ) SurfaceCell* sc= map->getSurfaceCell(Map::toSurfCoords(pos)); const Vec3f normal= sc->getNormal(); #ifdef USE_STREFLOP targetRotationZ= radToDeg(streflop::atan2(abs(normal.x), abs(normal.y))); #else targetRotationZ= radToDeg(atan2(abs(normal.x), abs(normal.y))); #endif if((normal.y < 0 || normal.x < 0) && !(normal.y < 0 && normal.x < 0)){ targetRotationZ= targetRotationZ * -1; } targetRotationZ= targetRotationZ * -1; #ifdef USE_STREFLOP targetRotationX= radToDeg(streflop::atan2(abs(normal.z), abs(normal.y))); #else targetRotationX= radToDeg(atan2(abs(normal.z), abs(normal.y))); #endif if((normal.y < 0 || normal.z < 0) && !(normal.y < 0 && normal.z < 0)){ targetRotationX= targetRotationX * -1; } } //For smooth rotation we now softly adjust the angle int adjustStep= 1; if(rotationZ < targetRotationZ){ if(rotationZ + adjustStep > targetRotationZ){ rotationZ= targetRotationZ; } else{ rotationZ= rotationZ + adjustStep; } } else if(rotationZ > targetRotationZ){ if(rotationZ - adjustStep < targetRotationZ){ rotationZ= targetRotationZ; } else{ rotationZ= rotationZ - adjustStep; } } if(rotationX < targetRotationX){ if(rotationX + adjustStep > targetRotationX){ rotationX= targetRotationX; } else{ rotationX= rotationX + adjustStep; } } else if(rotationX > targetRotationX){ if(rotationX - adjustStep < targetRotationX){ rotationX= targetRotationX; } else{ rotationX= rotationX - adjustStep; } } } float Unit::getRotationZ() const{ return rotationZ; } float Unit::getRotationX() const{ return rotationX; } int Unit::getProductionPercent() const{ if(anyCommand()){ const ProducibleType *produced= commands.front()->getCommandType()->getProduced(); if(produced!=NULL){ return clamp(progress2*100/produced->getProductionTime(), 0, 100); } } return -1; } float Unit::getProgressRatio() const{ if(anyCommand()){ const ProducibleType *produced= commands.front()->getCommandType()->getProduced(); if(produced!=NULL){ float help=progress2; return clamp(help/produced->getProductionTime(), 0.f, 1.f); } } return -1; } float Unit::getHpRatio() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return clamp(static_cast(hp)/type->getTotalMaxHp(&totalUpgrade), 0.f, 1.f); } float Unit::getEpRatio() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(type->getMaxHp()==0){ return 0.f; } else{ return clamp(static_cast(ep)/type->getTotalMaxEp(&totalUpgrade), 0.f, 1.f); } } const Level *Unit::getNextLevel() const{ if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(level==NULL && type->getLevelCount()>0){ return type->getLevel(0); } else{ for(int i=1; igetLevelCount(); ++i){ if(type->getLevel(i-1)==level){ return type->getLevel(i); } } } return NULL; } string Unit::getFullName() const{ string str=""; if(level != NULL){ str += (level->getName() + " "); } if(type == NULL) { throw runtime_error("type == NULL in Unit::getFullName()!"); } str += type->getName(); return str; } // ====================================== is ====================================== bool Unit::isOperative() const{ return isAlive() && isBuilt(); } bool Unit::isAnimProgressBound() const{ if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } bool result = false; if(currSkill->getClass() == scBeBuilt) { const BeBuiltSkillType *skill = dynamic_cast(currSkill); if(skill != NULL) { result = skill->getAnimProgressBound(); } } else if(currSkill->getClass() == scProduce) { const ProduceSkillType *skill = dynamic_cast(currSkill); if(skill != NULL) { result = skill->getAnimProgressBound(); } } else if(currSkill->getClass() == scUpgrade) { const UpgradeSkillType *skill = dynamic_cast(currSkill); if(skill != NULL) { result = skill->getAnimProgressBound(); } } else if(currSkill->getClass() == scMorph) { const MorphSkillType *skill = dynamic_cast(currSkill); if(skill != NULL) { result = skill->getAnimProgressBound(); } } return result; } bool Unit::isBeingBuilt() const{ if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return (currSkill->getClass() == scBeBuilt); } bool Unit::isBuilt() const{ return (isBeingBuilt() == false); } bool Unit::isPutrefacting() const{ return deadCount!=0; } bool Unit::isAlly(const Unit *unit) const { if(unit == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: unit == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return faction->isAlly(unit->getFaction()); } bool Unit::isDamaged() const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return hp < type->getTotalMaxHp(&totalUpgrade); } bool Unit::isInteresting(InterestingUnitType iut) const { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } switch(iut) { case iutIdleHarvester: if(type->hasCommandClass(ccHarvest)) { if(commands.empty() == false) { const CommandType *ct= commands.front()->getCommandType(); if(ct != NULL){ return ct->getClass() == ccStop; } } } return false; case iutBuiltBuilding: return type->hasSkillClass(scBeBuilt) && isBuilt(); case iutProducer: return type->hasSkillClass(scProduce); case iutDamaged: return isDamaged(); case iutStore: return type->getStoredResourceCount()>0; default: return false; } } // ====================================== set ====================================== void Unit::setCurrSkill(const SkillType *currSkill) { if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(this->currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: this->currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } changedActiveCommand = false; if(currSkill->getClass() != this->currSkill->getClass() || currSkill->getName() != this->currSkill->getName()) { animProgress= 0; lastAnimProgress= 0; queuedUnitParticleSystemTypes.clear(); while(unitParticleSystems.empty() == false) { unitParticleSystems.back()->fade(); unitParticleSystems.pop_back(); } } if(showUnitParticles && (currSkill->unitParticleSystemTypes.empty() == false) && (unitParticleSystems.empty() == true) ) { //printf("START - particle system type\n"); for(UnitParticleSystemTypes::const_iterator it= currSkill->unitParticleSystemTypes.begin(); it != currSkill->unitParticleSystemTypes.end(); ++it) { if((*it)->getStartTime() == 0.0) { //printf("Adding NON-queued particle system type [%s] [%f] [%f]\n",(*it)->getType().c_str(),(*it)->getStartTime(),(*it)->getEndTime()); UnitParticleSystem *ups = new UnitParticleSystem(200); (*it)->setValues(ups); ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } unitParticleSystems.push_back(ups); Renderer::getInstance().manageParticleSystem(ups, rsGame); } else { //printf("Adding queued particle system type [%s] [%f] [%f]\n",(*it)->getType().c_str(),(*it)->getStartTime(),(*it)->getEndTime()); queuedUnitParticleSystemTypes.push_back(*it); } } } progress2= 0; if(this->currSkill != currSkill) { this->lastModelIndexForCurrSkillType = -1; this->animationRandomCycleCount = 0; } this->currSkill= currSkill; } void Unit::setCurrSkill(SkillClass sc) { if(getType() == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: getType() == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } setCurrSkill(getType()->getFirstStOfClass(sc)); } void Unit::setTarget(const Unit *unit){ if(unit == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: unit == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //find a free pos in cellmap setTargetPos(unit->getCellPos()); //ser field and vector targetField= unit->getCurrField(); targetVec= unit->getCurrVector(); targetRef= unit; } void Unit::setPos(const Vec2i &pos) { if(map->isInside(pos) == false || map->isInsideSurface(map->toSurfCoords(pos)) == false) { throw runtime_error("#3 Invalid path position = " + pos.getString()); } this->lastPos= this->pos; this->pos= pos; map->clampPos(this->pos); this->meetingPos= pos - Vec2i(1); map->clampPos(this->meetingPos); // Attempt to improve performance this->exploreCells(); logSynchData(__FILE__,__LINE__); } void Unit::setTargetPos(const Vec2i &targetPos) { if(map->isInside(targetPos) == false || map->isInsideSurface(map->toSurfCoords(targetPos)) == false) { throw runtime_error("#4 Invalid path position = " + targetPos.getString()); } Vec2i relPos= targetPos - pos; //map->clampPos(relPos); Vec2f relPosf= Vec2f((float)relPos.x, (float)relPos.y); #ifdef USE_STREFLOP targetRotation= radToDeg(streflop::atan2(relPosf.x, relPosf.y)); #else targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); #endif targetRef= NULL; //this->targetField = fLand; //this->targetVec = Vec3f(0.0); //this->targetPos = Vec2i(0); this->targetPos= targetPos; map->clampPos(this->targetPos); logSynchData(__FILE__,__LINE__); } void Unit::setVisible(const bool visible) { this->visible = visible; for(UnitParticleSystems::iterator it= unitParticleSystems.begin(); it != unitParticleSystems.end(); ++it) { (*it)->setVisible(visible); } for(UnitParticleSystems::iterator it= damageParticleSystems.begin(); it != damageParticleSystems.end(); ++it) { (*it)->setVisible(visible); } for(UnitParticleSystems::iterator it= smokeParticleSystems.begin(); it != smokeParticleSystems.end(); ++it) { (*it)->setVisible(visible); } for(unsigned int i = 0; i < currentAttackBoostEffects.size(); ++i) { UnitAttackBoostEffect *effect = currentAttackBoostEffects[i]; if(effect != NULL && effect->ups != NULL) { bool particleValid = Renderer::getInstance().validateParticleSystemStillExists(effect->ups,rsGame); if(particleValid == true) { effect->ups->setVisible(visible); } } } if(currentAttackBoostOriginatorEffect.currentAppliedEffect != NULL) { if(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups != NULL) { bool particleValid = Renderer::getInstance().validateParticleSystemStillExists(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups,rsGame); if(particleValid == true) { currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setVisible(visible); } } } } // =============================== Render related ================================== Model *Unit::getCurrentModelPtr() { if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } int currentModelIndexForCurrSkillType = lastModelIndexForCurrSkillType; Model *result = currSkill->getAnimation(animProgress,this,&lastModelIndexForCurrSkillType, &animationRandomCycleCount); if(currentModelIndexForCurrSkillType != lastModelIndexForCurrSkillType) { animationRandomCycleCount++; } return result; } const Model *Unit::getCurrentModel() { if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } int currentModelIndexForCurrSkillType = lastModelIndexForCurrSkillType; const Model *result = currSkill->getAnimation(animProgress,this,&lastModelIndexForCurrSkillType, &animationRandomCycleCount); if(currentModelIndexForCurrSkillType != lastModelIndexForCurrSkillType) { animationRandomCycleCount++; } return result; } Vec3f Unit::getCurrVector() const{ if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } return getCurrVectorFlat() + Vec3f(0.f, type->getHeight()/2.f, 0.f); } Vec3f Unit::getCurrVectorFlat() const{ return getVectorFlat(lastPos, pos); /* Vec3f v; float y1= computeHeight(lastPos); float y2= computeHeight(pos); if(currSkill->getClass() == scMove) { v.x= lastPos.x + progress * (pos.x-lastPos.x); v.z= lastPos.y + progress * (pos.y-lastPos.y); v.y= y1+progress*(y2-y1); } else{ v.x= static_cast(pos.x); v.z= static_cast(pos.y); v.y= y2; } v.x+= type->getSize()/2.f-0.5f; v.z+= type->getSize()/2.f-0.5f; return v; */ } Vec3f Unit::getVectorFlat(const Vec2i &lastPosValue, const Vec2i &curPosValue) const { Vec3f v; float y1= computeHeight(lastPosValue); float y2= computeHeight(curPosValue); if(currSkill->getClass() == scMove) { v.x= lastPosValue.x + progress * (curPosValue.x-lastPosValue.x); v.z= lastPosValue.y + progress * (curPosValue.y-lastPosValue.y); v.y= y1+progress*(y2-y1); } else{ v.x= static_cast(curPosValue.x); v.z= static_cast(curPosValue.y); v.y= y2; } v.x+= type->getSize()/2.f-0.5f; v.z+= type->getSize()/2.f-0.5f; return v; } // =================== Command list related =================== //any command bool Unit::anyCommand(bool validateCommandtype) const { bool result = false; if(validateCommandtype == false) { result = (commands.empty() == false); } else { for(Commands::const_iterator it= commands.begin(); it != commands.end(); ++it) { const CommandType *ct = (*it)->getCommandType(); if(ct != NULL && ct->getClass() != ccStop) { result = true; break; } } } return result; } //return current command, assert that there is always one command Command *Unit::getCurrrentCommandThreadSafe() { static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); if(commands.empty() == false) { return commands.front(); } return NULL; } //return current command, assert that there is always one command Command *Unit::getCurrCommand() const { if(commands.empty() == false) { return commands.front(); } return NULL; } void Unit::replaceCurrCommand(Command *cmd) { static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); assert(commands.empty() == false); commands.front() = cmd; this->setCurrentUnitTitle(""); } //returns the size of the commands unsigned int Unit::getCommandSize() const { return commands.size(); } //return current command, assert that there is always one command int Unit::getCountOfProducedUnits(const UnitType *ut) const{ int count=0; for(Commands::const_iterator it= commands.begin(); it!=commands.end(); ++it){ const CommandType* ct=(*it)->getCommandType(); if(ct->getClass()==ccProduce || ct->getClass()==ccMorph ){ const UnitType *producedUnitType= static_cast(ct->getProduced()); if(producedUnitType==ut) { count++; } } if(ct->getClass()==ccBuild){ const UnitType *builtUnitType= (*it)->getUnitType(); if(builtUnitType==ut) { count++; } } } return count; } //give one command (clear, and push back) CommandResult Unit::giveCommand(Command *command, bool tryQueue) { if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"\n======================\nUnit Command tryQueue = %d\nUnit Info:\n%s\nCommand Info:\n%s\n",tryQueue,this->toString().c_str(),command->toString().c_str()); changedActiveCommand = false; Chrono chrono; if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start(); assert(command != NULL); assert(command->getCommandType() != NULL); const int command_priority = command->getPriority(); if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); //printf("In [%s::%s] Line: %d unit [%d - %s] command [%s] tryQueue = %d command->getCommandType()->isQueuable(tryQueue) = %d\n",__FILE__,__FUNCTION__,__LINE__,this->getId(),this->getType()->getName().c_str(), command->getCommandType()->getName().c_str(), tryQueue,command->getCommandType()->isQueuable(tryQueue)); if(command->getCommandType()->isQueuable(tryQueue)) { if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] Command is Queable\n",__FILE__,__FUNCTION__,__LINE__); if(command->getCommandType()->isQueuable() == qAlways && tryQueue){ // Its a produce or upgrade command called without queued key // in this case we must NOT delete lower priority commands! // we just queue it! } else{ //Delete all lower-prioirty commands for(list::iterator i= commands.begin(); i != commands.end();){ if((*i)->getPriority() < command_priority){ if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] Deleting lower priority command [%s]\n",__FILE__,__FUNCTION__,__LINE__,(*i)->toString().c_str()); static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); deleteQueuedCommand(*i); i= commands.erase(i); safeMutex.ReleaseLock(); } else{ ++i; } } } if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); //cancel current command if it is not queuable if(commands.empty() == false && commands.back()->getCommandType()->isQueueAppendable() == false) { if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] Cancel command because last one is NOT queable [%s]\n",__FILE__,__FUNCTION__,__LINE__,commands.back()->toString().c_str()); cancelCommand(); } if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); } else { //empty command queue if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] Clear commands because current is NOT queable.\n",__FILE__,__FUNCTION__,__LINE__); bool willChangedActiveCommand = (commands.size() > 0); if(willChangedActiveCommand && getCurrCommand()->getCommandType()->getClass() == command->getCommandType()->getClass()) { willChangedActiveCommand = false; } clearCommands(); changedActiveCommand = willChangedActiveCommand; //printf("In [%s::%s] Line: %d cleared existing commands\n",__FILE__,__FUNCTION__,__LINE__); if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); } if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); //check command CommandResult result= checkCommand(command); if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] checkCommand returned: [%d]\n",__FILE__,__FUNCTION__,__LINE__,result); //printf("In [%s::%s] Line: %d check command returned %d, commands.size() = %d\n",__FILE__,__FUNCTION__,__LINE__,result,commands.size()); if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); if(result == crSuccess) { applyCommand(command); } if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); //push back command if(result == crSuccess) { static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); commands.push_back(command); safeMutex.ReleaseLock(); } else { delete command; changedActiveCommand = false; } if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); return result; } //pop front (used when order is done) CommandResult Unit::finishCommand() { changedActiveCommand = false; retryCurrCommandCount=0; this->setCurrentUnitTitle(""); //is empty? if(commands.empty()) { if(SystemFlags::getSystemSettingType(SystemFlags::debugLUA).enabled) SystemFlags::OutputDebug(SystemFlags::debugLUA,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__); return crFailUndefined; } //pop front static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); delete commands.front(); commands.erase(commands.begin()); safeMutex.ReleaseLock(true); this->unitPath->clear(); while (commands.empty() == false) { if (commands.front()->getUnit() != NULL && this->faction->isUnitInLivingUnitsp(commands.front()->getUnit()) == false) { safeMutex.Lock(); delete commands.front(); commands.erase(commands.begin()); safeMutex.ReleaseLock(true); } else { break; } } return crSuccess; } //to cancel a command CommandResult Unit::cancelCommand() { changedActiveCommand = false; retryCurrCommandCount=0; this->setCurrentUnitTitle(""); //is empty? if(commands.empty()){ if(SystemFlags::getSystemSettingType(SystemFlags::debugLUA).enabled) SystemFlags::OutputDebug(SystemFlags::debugLUA,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__); return crFailUndefined; } //undo command undoCommand(commands.back()); //delete ans pop command static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); delete commands.back(); commands.pop_back(); safeMutex.ReleaseLock(); //clear routes this->unitPath->clear(); return crSuccess; } // =================== route stack =================== void Unit::create(bool startingUnit){ faction->addUnit(this); map->putUnitCells(this, pos); if(startingUnit){ faction->applyStaticCosts(type); } } void Unit::born() { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } faction->addStore(type); faction->applyStaticProduction(type); setCurrSkill(scStop); checkItemInVault(&this->hp,this->hp); hp= type->getMaxHp(); addItemToVault(&this->hp,this->hp); } void Unit::kill() { //no longer needs static resources if(isBeingBuilt()) { faction->deApplyStaticConsumption(type); } else { faction->deApplyStaticCosts(type); } //do the cleaning map->clearUnitCells(this, pos); if(isBeingBuilt() == false) { faction->removeStore(type); } setCurrSkill(scDie); notifyObservers(UnitObserver::eKill); //clear commands clearCommands(); UnitUpdater *unitUpdater = game->getWorld()->getUnitUpdater(); //unitUpdater->clearUnitPrecache(this); unitUpdater->removeUnitPrecache(this); } void Unit::undertake() { if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] about to undertake unit id = %d [%s] [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->id, this->getFullName().c_str(),this->getDesc().c_str()); // Remove any units that were previously in attack-boost range if(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.empty() == false && currentAttackBoostOriginatorEffect.skillType != NULL) { for(unsigned int i = 0; i < currentAttackBoostOriginatorEffect.currentAttackBoostUnits.size(); ++i) { // Remove attack boost upgrades from unit int findUnitId = currentAttackBoostOriginatorEffect.currentAttackBoostUnits[i]; Unit *affectedUnit = game->getWorld()->findUnitById(findUnitId); if(affectedUnit != NULL) { affectedUnit->deapplyAttackBoost(currentAttackBoostOriginatorEffect.skillType->getAttackBoost(), this); } //printf("!!!! DE-APPLY ATTACK BOOST from unit [%s - %d]\n",affectedUnit->getType()->getName().c_str(),affectedUnit->getId()); } currentAttackBoostOriginatorEffect.currentAttackBoostUnits.clear(); } UnitUpdater *unitUpdater = game->getWorld()->getUnitUpdater(); //unitUpdater->clearUnitPrecache(this); unitUpdater->removeUnitPrecache(this); this->faction->deleteLivingUnits(id); this->faction->deleteLivingUnitsp(this); faction->removeUnit(this); } // =================== Referencers =================== void Unit::addObserver(UnitObserver *unitObserver){ observers.push_back(unitObserver); } void Unit::removeObserver(UnitObserver *unitObserver){ observers.remove(unitObserver); } void Unit::notifyObservers(UnitObserver::Event event){ for(Observers::iterator it= observers.begin(); it!=observers.end(); ++it){ (*it)->unitEvent(event, this); } } // =================== Other =================== void Unit::resetHighlight(){ highlight= 1.f; } const CommandType *Unit::computeCommandType(const Vec2i &pos, const Unit *targetUnit) const{ const CommandType *commandType= NULL; if(map->isInside(pos) == false || map->isInsideSurface(map->toSurfCoords(pos)) == false) { throw runtime_error("#6 Invalid path position = " + pos.getString()); } SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(targetUnit!=NULL){ //attack enemies if(!isAlly(targetUnit)){ commandType= type->getFirstAttackCommand(targetUnit->getCurrField()); } //repair allies else{ commandType= type->getFirstRepairCommand(targetUnit->getType()); } } else{ //check harvest command Resource *resource= sc->getResource(); if(resource!=NULL){ commandType= type->getFirstHarvestCommand(resource->getType(),this->getFaction()); } } //default command is move command if(commandType==NULL){ commandType= type->getFirstCtOfClass(ccMove); } return commandType; } bool Unit::needToUpdate() { assert(progress <= 1.f); if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } bool return_value = false; if(currSkill->getClass() != scDie) { //speed int speed = currSkill->getTotalSpeed(&totalUpgrade); if(changedActiveCommand) { speed = CHANGE_COMMAND_SPEED; } //speed modifier float diagonalFactor= 1.f; float heightFactor= 1.f; if(currSkill->getClass() == scMove) { //if moving in diagonal move slower Vec2i dest= pos - lastPos; if(abs(dest.x) + abs(dest.y) == 2) { diagonalFactor = 0.71f; } //if moving to an higher cell move slower else move faster float heightDiff= map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); heightFactor= clamp(1.f + heightDiff / 5.f, 0.2f, 5.f); } //update progresses const Game *game = Renderer::getInstance().getGame(); float speedDenominator = (speedDivider * game->getWorld()->getUpdateFps(this->getFactionIndex())); float newProgress = progress; newProgress += (speed * diagonalFactor * heightFactor) / speedDenominator; if(newProgress >= 1.f) { return_value = true; } } return return_value; } void Unit::updateTimedParticles() { //!!! // Start new particle systems based on start time for(int i = queuedUnitParticleSystemTypes.size() - 1; i >= 0; i--) { UnitParticleSystemType *pst = queuedUnitParticleSystemTypes[i]; if(pst != NULL) { if(truncateDecimal(pst->getStartTime()) <= truncateDecimal(animProgress)) { //printf("STARTING queued particle system type [%s] [%f] [%f] [%f] [%f]\n",pst->getType().c_str(),truncateDecimal(pst->getStartTime()),truncateDecimal(pst->getEndTime()),truncateDecimal(animProgress),truncateDecimal(lastAnimProgress)); UnitParticleSystem *ups = new UnitParticleSystem(200); pst->setValues(ups); ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } unitParticleSystems.push_back(ups); Renderer::getInstance().manageParticleSystem(ups, rsGame); queuedUnitParticleSystemTypes.erase(queuedUnitParticleSystemTypes.begin() + i); } } else { queuedUnitParticleSystemTypes.erase(queuedUnitParticleSystemTypes.begin() + i); } } // End existing systems based on end time for(int i = unitParticleSystems.size() - 1; i >= 0; i--) { UnitParticleSystem *ps = unitParticleSystems[i]; if(ps != NULL) { if(truncateDecimal(ps->getStartTime()) != 0.0 || truncateDecimal(ps->getEndTime()) != 1.0) { //printf("Checking for end particle system #%d [%d] [%f] [%f] [%f] [%f]\n",i,ps->shape,truncateDecimal(ps->getStartTime()),truncateDecimal(ps->getEndTime()),truncateDecimal(animProgress),truncateDecimal(lastAnimProgress)); if(truncateDecimal(animProgress) >= 0.99 || truncateDecimal(animProgress) >= truncateDecimal(ps->getEndTime())) { //printf("ENDING particle system [%d]\n",ps->shape); ps->fade(); unitParticleSystems.erase(unitParticleSystems.begin() + i); } } } } } bool Unit::update() { assert(progress <= 1.f); //highlight if(highlight > 0.f) { const Game *game = Renderer::getInstance().getGame(); highlight -= 1.f / (highlightTime * game->getWorld()->getUpdateFps(this->getFactionIndex())); } if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //speed int speed= currSkill->getTotalSpeed(&totalUpgrade); if(changedActiveCommand) { speed = CHANGE_COMMAND_SPEED; } //speed modifier float diagonalFactor = 1.f; float heightFactor = 1.f; if(currSkill->getClass() == scMove) { //if moving in diagonal move slower Vec2i dest = pos - lastPos; if(abs(dest.x) + abs(dest.y) == 2) { diagonalFactor = 0.71f; } //if moving to an higher cell move slower else move faster float heightDiff = map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); heightFactor = clamp(1.f + heightDiff / 5.f, 0.2f, 5.f); } //update progresses lastAnimProgress= animProgress; const Game *game = Renderer::getInstance().getGame(); float speedDenominator = (speedDivider * game->getWorld()->getUpdateFps(this->getFactionIndex())); progress += (speed * diagonalFactor * heightFactor) / speedDenominator; if(isAnimProgressBound() == true) { float targetProgress=0; if(currSkill->getClass() == scBeBuilt) { targetProgress = this->getHpRatio(); } if(currSkill->getClass() == scProduce) { targetProgress = this->getProgressRatio(); } if(currSkill->getClass() == scUpgrade) { targetProgress = this->getProgressRatio(); } if(currSkill->getClass() == scMorph) { targetProgress = this->getProgressRatio(); } if(animProgressgetAnimSpeed() * heightFactor) / speedDenominator; } //update target updateTarget(); //rotation if(currSkill->getClass() != scStop) { const int rotFactor= 2; if(progress < 1.f / rotFactor) { if(type->getFirstStOfClass(scMove)){ if(abs((int)(lastRotation-targetRotation)) < 180) rotation= lastRotation + (targetRotation - lastRotation) * progress * rotFactor; else { float rotationTerm = targetRotation > lastRotation ? -360.f: +360.f; rotation = lastRotation + (targetRotation - lastRotation + rotationTerm) * progress * rotFactor; } } } } if(type->getProperty(UnitType::pRotatedClimb)){ calculateXZRotation(); } else { rotationZ=.0f; rotationX=.0f; } if(Renderer::getInstance().validateParticleSystemStillExists(fire,rsGame) == false) { fire = NULL; } if (fire != NULL) { fire->setPos(getCurrVector()); } for(UnitParticleSystems::iterator it= unitParticleSystems.begin(); it != unitParticleSystems.end(); ++it) { (*it)->setPos(getCurrVector()); (*it)->setRotation(getRotation()); } for(UnitParticleSystems::iterator it= damageParticleSystems.begin(); it != damageParticleSystems.end(); ++it) { (*it)->setPos(getCurrVector()); (*it)->setRotation(getRotation()); } for(UnitParticleSystems::iterator it= smokeParticleSystems.begin(); it != smokeParticleSystems.end(); ++it) { (*it)->setPos(getCurrVector()); (*it)->setRotation(getRotation()); } for(unsigned int i = 0; i < currentAttackBoostEffects.size(); ++i) { UnitAttackBoostEffect *effect = currentAttackBoostEffects[i]; if(effect != NULL && effect->ups != NULL) { bool particleValid = Renderer::getInstance().validateParticleSystemStillExists(effect->ups,rsGame); if(particleValid == true) { effect->ups->setPos(getCurrVector()); effect->ups->setRotation(getRotation()); } } } if(currentAttackBoostOriginatorEffect.currentAppliedEffect != NULL) { if(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups != NULL) { bool particleValid = Renderer::getInstance().validateParticleSystemStillExists(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups,rsGame); if(particleValid == true) { currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setPos(getCurrVector()); currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setRotation(getRotation()); } } } //checks if(animProgress > 1.f) { bool canCycle = currSkill->CanCycleNextRandomAnimation(&animationRandomCycleCount); animProgress = currSkill->getClass() == scDie? 1.f: 0.f; if(canCycle == true) { this->lastModelIndexForCurrSkillType = -1; } } bool return_value = false; //checks if(progress >= 1.f) { lastRotation= targetRotation; if(currSkill->getClass() != scDie) { progress = 0.f; return_value = true; } else { progress= 1.f; deadCount++; if(deadCount >= maxDeadCount) { toBeUndertaken= true; return_value = false; } } } if(currSkill != currentAttackBoostOriginatorEffect.skillType) { if(currentAttackBoostOriginatorEffect.currentAppliedEffect != NULL) { delete currentAttackBoostOriginatorEffect.currentAppliedEffect; currentAttackBoostOriginatorEffect.currentAppliedEffect = NULL; //printf("- #1 DE-APPLY ATTACK BOOST SELF PARTICLE to unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId()); } // Remove any units that were previously in range if(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.empty() == false && currentAttackBoostOriginatorEffect.skillType != NULL) { for(unsigned int i = 0; i < currentAttackBoostOriginatorEffect.currentAttackBoostUnits.size(); ++i) { // Remove attack boost upgrades from unit int findUnitId = currentAttackBoostOriginatorEffect.currentAttackBoostUnits[i]; Unit *affectedUnit = game->getWorld()->findUnitById(findUnitId); if(affectedUnit != NULL) { affectedUnit->deapplyAttackBoost(currentAttackBoostOriginatorEffect.skillType->getAttackBoost(), this); } //printf("- #1 DE-APPLY ATTACK BOOST from unit [%s - %d]\n",affectedUnit->getType()->getName().c_str(),affectedUnit->getId()); } currentAttackBoostOriginatorEffect.currentAttackBoostUnits.clear(); } currentAttackBoostOriginatorEffect.skillType = currSkill; if(currSkill->isAttackBoostEnabled() == true) { // Search for units in range of this unit which apply to the // attack-boost and temporarily upgrade them UnitUpdater *unitUpdater = this->game->getWorld()->getUnitUpdater(); const AttackBoost *attackBoost = currSkill->getAttackBoost(); vector candidates = unitUpdater->findUnitsInRange(this, attackBoost->radius); for(unsigned int i = 0; i < candidates.size(); ++i) { Unit *affectedUnit = candidates[i]; if(attackBoost->isAffected(this,affectedUnit) == true) { if(affectedUnit->applyAttackBoost(attackBoost, this) == true) { currentAttackBoostOriginatorEffect.currentAttackBoostUnits.push_back(affectedUnit->getId()); //printf("+ #1 APPLY ATTACK BOOST to unit [%s - %d]\n",affectedUnit->getType()->getName().c_str(),affectedUnit->getId()); } } } if(showUnitParticles == true) { if(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.empty() == false) { if(attackBoost->unitParticleSystemTypeForSourceUnit != NULL) { currentAttackBoostOriginatorEffect.currentAppliedEffect = new UnitAttackBoostEffect(); currentAttackBoostOriginatorEffect.currentAppliedEffect->upst = new UnitParticleSystemType(); *currentAttackBoostOriginatorEffect.currentAppliedEffect->upst = *attackBoost->unitParticleSystemTypeForSourceUnit; //effect.upst = boost->unitParticleSystemTypeForAffectedUnit; currentAttackBoostOriginatorEffect.currentAppliedEffect->ups = new UnitParticleSystem(200); currentAttackBoostOriginatorEffect.currentAppliedEffect->upst->setValues(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups); currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } Renderer::getInstance().manageParticleSystem(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups, rsGame); //printf("+ #1 APPLY ATTACK BOOST SELF PARTICLE to unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId()); } } } } } else { if(currSkill->isAttackBoostEnabled() == true) { // Search for units in range of this unit which apply to the // attack-boost and temporarily upgrade them UnitUpdater *unitUpdater = this->game->getWorld()->getUnitUpdater(); const AttackBoost *attackBoost = currSkill->getAttackBoost(); vector candidates = unitUpdater->findUnitsInRange(this, attackBoost->radius); for(unsigned int i = 0; i < candidates.size(); ++i) { Unit *affectedUnit = candidates[i]; std::vector::iterator iterFound = std::find(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.begin(), currentAttackBoostOriginatorEffect.currentAttackBoostUnits.end(), affectedUnit->getId()); if(attackBoost->isAffected(this,affectedUnit) == true) { if(iterFound == currentAttackBoostOriginatorEffect.currentAttackBoostUnits.end()) { if(affectedUnit->applyAttackBoost(attackBoost, this) == true) { currentAttackBoostOriginatorEffect.currentAttackBoostUnits.push_back(affectedUnit->getId()); //printf("+ #2 APPLY ATTACK BOOST to unit [%s - %d]\n",affectedUnit->getType()->getName().c_str(),affectedUnit->getId()); } } } else { if(iterFound != currentAttackBoostOriginatorEffect.currentAttackBoostUnits.end()) { affectedUnit->deapplyAttackBoost(currentAttackBoostOriginatorEffect.skillType->getAttackBoost(), this); currentAttackBoostOriginatorEffect.currentAttackBoostUnits.erase(iterFound); //printf("- #2 DE-APPLY ATTACK BOOST from unit [%s - %d]\n",affectedUnit->getType()->getName().c_str(),affectedUnit->getId()); } } } if(showUnitParticles == true) { if(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.empty() == false) { if( attackBoost->unitParticleSystemTypeForSourceUnit != NULL && currentAttackBoostOriginatorEffect.currentAppliedEffect == NULL) { currentAttackBoostOriginatorEffect.currentAppliedEffect = new UnitAttackBoostEffect(); currentAttackBoostOriginatorEffect.currentAppliedEffect->upst = new UnitParticleSystemType(); *currentAttackBoostOriginatorEffect.currentAppliedEffect->upst = *attackBoost->unitParticleSystemTypeForSourceUnit; //effect.upst = boost->unitParticleSystemTypeForAffectedUnit; currentAttackBoostOriginatorEffect.currentAppliedEffect->ups = new UnitParticleSystem(200); currentAttackBoostOriginatorEffect.currentAppliedEffect->upst->setValues(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups); currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { currentAttackBoostOriginatorEffect.currentAppliedEffect->ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } Renderer::getInstance().manageParticleSystem(currentAttackBoostOriginatorEffect.currentAppliedEffect->ups, rsGame); //printf("+ #2 APPLY ATTACK BOOST SELF PARTICLE to unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId()); } } else if(currentAttackBoostOriginatorEffect.currentAttackBoostUnits.size() <= 0) { if(currentAttackBoostOriginatorEffect.currentAppliedEffect != NULL) { delete currentAttackBoostOriginatorEffect.currentAppliedEffect; currentAttackBoostOriginatorEffect.currentAppliedEffect = NULL; //printf("- #2 DE-APPLY ATTACK BOOST SELF PARTICLE to unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId()); } } } } } if(return_value) { changedActiveCommand = false; } return return_value; } bool Unit::unitHasAttackBoost(const AttackBoost *boost, const Unit *source) const { bool result = false; for(unsigned int i = 0; i < currentAttackBoostEffects.size(); ++i) { const UnitAttackBoostEffect *effect = currentAttackBoostEffects[i]; if( effect != NULL && effect->boost == boost && effect->source->getType()->getId() == source->getType()->getId()) { result = true; break; } } return result; } bool Unit::applyAttackBoost(const AttackBoost *boost, const Unit *source) { if(boost == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: boost == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //printf("APPLYING ATTACK BOOST to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); bool shouldApplyAttackBoost = true; if(boost->allowMultipleBoosts == false) { // Check if we already have this boost from this unit type and multiples // are not allowed bool alreadyHasAttackBoost = this->unitHasAttackBoost(boost, source); if(alreadyHasAttackBoost == true) { shouldApplyAttackBoost = false; } } if(shouldApplyAttackBoost == true) { //printf("APPLYING ATTACK BOOST START to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); UnitAttackBoostEffect *effect = new UnitAttackBoostEffect(); effect->boost = boost; effect->source = source; bool wasAlive = alive; int prevMaxHp = totalUpgrade.getMaxHp(); int prevMaxHpRegen = totalUpgrade.getMaxHpRegeneration(); //printf("#1 wasAlive = %d hp = %d boosthp = %d\n",wasAlive,hp,boost->boostUpgrade.getMaxHp()); totalUpgrade.apply(&boost->boostUpgrade, this); checkItemInVault(&this->hp,this->hp); //hp += boost->boostUpgrade.getMaxHp(); hp += (totalUpgrade.getMaxHp() - prevMaxHp); addItemToVault(&this->hp,this->hp); //regenerate hp upgrade / or boost if(totalUpgrade.getMaxHpRegeneration() != 0) { checkItemInVault(&this->hp,this->hp); //printf("BEFORE Apply Hp Regen max = %d, prev = %d, hp = %d\n",totalUpgrade.getMaxHpRegeneration(),prevMaxHpRegen,hp); hp += (totalUpgrade.getMaxHpRegeneration() - prevMaxHpRegen); //if(hp > type->getTotalMaxHp(&totalUpgrade)) { // hp = type->getTotalMaxHp(&totalUpgrade); //} addItemToVault(&this->hp,this->hp); //printf("AFTER Apply Hp Regen max = %d, prev = %d, hp = %d\n",totalUpgrade.getMaxHpRegeneration(),prevMaxHpRegen,hp); } //printf("#2 wasAlive = %d hp = %d boosthp = %d\n",wasAlive,hp,boost->boostUpgrade.getMaxHp()); if(showUnitParticles == true) { if(boost->unitParticleSystemTypeForAffectedUnit != NULL) { effect->upst = new UnitParticleSystemType(); *effect->upst = *boost->unitParticleSystemTypeForAffectedUnit; //effect.upst = boost->unitParticleSystemTypeForAffectedUnit; effect->ups = new UnitParticleSystem(200); effect->upst->setValues(effect->ups); effect->ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { effect->ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } Renderer::getInstance().manageParticleSystem(effect->ups, rsGame); } } currentAttackBoostEffects.push_back(effect); if(wasAlive == true) { //startDamageParticles startDamageParticles(); //stop DamageParticles on death if(hp <= 0) { alive= false; hp=0; addItemToVault(&this->hp,this->hp); stopDamageParticles(true); Unit::game->getWorld()->getStats()->die(getFactionIndex()); game->getScriptManager()->onUnitDied(this); StaticSound *sound= this->getType()->getFirstStOfClass(scDie)->getSound(); if(sound != NULL && (this->getFactionIndex() == Unit::game->getWorld()->getThisFactionIndex() || (game->getWorld()->showWorldForPlayer(game->getWorld()->getThisTeamIndex()) == true))) { SoundRenderer::getInstance().playFx(sound); } if(this->isDead() && this->getCurrSkill()->getClass() != scDie) { this->kill(); } } } //printf("APPLYING ATTACK BOOST END to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); } return shouldApplyAttackBoost; } void Unit::deapplyAttackBoost(const AttackBoost *boost, const Unit *source) { if(boost == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: boost == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //printf("DE-APPLYING ATTACK BOOST START to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); bool wasAlive = alive; int prevMaxHp = totalUpgrade.getMaxHp(); int prevMaxHpRegen = totalUpgrade.getMaxHpRegeneration(); totalUpgrade.deapply(&boost->boostUpgrade, this); checkItemInVault(&this->hp,this->hp); //hp -= boost->boostUpgrade.getMaxHp(); hp -= (prevMaxHp - totalUpgrade.getMaxHp()); addItemToVault(&this->hp,this->hp); //regenerate hp upgrade / or boost if(totalUpgrade.getMaxHpRegeneration() != 0) { checkItemInVault(&this->hp,this->hp); //printf("BEFORE DeApply Hp Regen max = %d, prev = %d, hp = %d\n",totalUpgrade.getMaxHpRegeneration(),prevMaxHpRegen,hp); hp -= (totalUpgrade.getMaxHpRegeneration() - prevMaxHpRegen); //if(hp > totalUpgrade.getMaxHp()) { // hp = totalUpgrade.getMaxHp(); //} addItemToVault(&this->hp,this->hp); //printf("AFTER DeApply Hp Regen max = %d, prev = %d, hp = %d\n",totalUpgrade.getMaxHpRegeneration(),prevMaxHpRegen,hp); } if(wasAlive == true) { //printf("DE-APPLYING ATTACK BOOST wasalive = true to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); //startDamageParticles startDamageParticles(); //stop DamageParticles on death if(hp <= 0) { alive= false; hp=0; addItemToVault(&this->hp,this->hp); stopDamageParticles(true); Unit::game->getWorld()->getStats()->die(getFactionIndex()); game->getScriptManager()->onUnitDied(this); StaticSound *sound= this->getType()->getFirstStOfClass(scDie)->getSound(); if(sound != NULL && (this->getFactionIndex() == Unit::game->getWorld()->getThisFactionIndex() || (game->getWorld()->showWorldForPlayer(game->getWorld()->getThisTeamIndex()) == true))) { SoundRenderer::getInstance().playFx(sound); } if(this->isDead() && this->getCurrSkill()->getClass() != scDie) { this->kill(); } } } //printf("DE-APPLYING ATTACK BOOST BEFORE END to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); for(unsigned int i = 0; i < currentAttackBoostEffects.size(); ++i) { UnitAttackBoostEffect *effect = currentAttackBoostEffects[i]; if(effect != NULL && effect->boost == boost && effect->source == source) { delete effect; currentAttackBoostEffects.erase(currentAttackBoostEffects.begin() + i); break; } } //printf("DE-APPLYING ATTACK BOOST END to unit [%s - %d] from unit [%s - %d]\n",this->getType()->getName().c_str(),this->getId(),source->getType()->getName().c_str(),source->getId()); } void Unit::tick() { if(isAlive()) { if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //regenerate hp if(type->getHpRegeneration() >= 0) { checkItemInVault(&this->hp,this->hp); hp += type->getHpRegeneration(); if(hp > type->getTotalMaxHp(&totalUpgrade)) { hp = type->getTotalMaxHp(&totalUpgrade); } addItemToVault(&this->hp,this->hp); } // If we have negative regeneration then check if the unit should die else { bool decHpResult = decHp(-type->getHpRegeneration()); if(decHpResult) { Unit::game->getWorld()->getStats()->die(getFactionIndex()); game->getScriptManager()->onUnitDied(this); } StaticSound *sound= this->getType()->getFirstStOfClass(scDie)->getSound(); if(sound != NULL && (this->getFactionIndex() == Unit::game->getWorld()->getThisFactionIndex() || (game->getWorld()->showWorldForPlayer(game->getWorld()->getThisTeamIndex()) == true))) { SoundRenderer::getInstance().playFx(sound); } } //regenerate hp upgrade / or boost if(type->getTotalMaxHpRegeneration(&totalUpgrade) >= 0) { checkItemInVault(&this->hp,this->hp); hp += type->getTotalMaxHpRegeneration(&totalUpgrade); if(hp > type->getTotalMaxHp(&totalUpgrade)) { hp = type->getTotalMaxHp(&totalUpgrade); } addItemToVault(&this->hp,this->hp); } // If we have negative regeneration then check if the unit should die else { bool decHpResult = decHp(-type->getTotalMaxHpRegeneration(&totalUpgrade)); if(decHpResult) { Unit::game->getWorld()->getStats()->die(getFactionIndex()); game->getScriptManager()->onUnitDied(this); } StaticSound *sound= this->getType()->getFirstStOfClass(scDie)->getSound(); if(sound != NULL && (this->getFactionIndex() == Unit::game->getWorld()->getThisFactionIndex() || (game->getWorld()->showWorldForPlayer(game->getWorld()->getThisTeamIndex()) == true))) { SoundRenderer::getInstance().playFx(sound); } } //stop DamageParticles stopDamageParticles(false); checkItemInVault(&this->ep,this->ep); //regenerate ep // ep += type->getEpRegeneration(); // if(ep > type->getTotalMaxEp(&totalUpgrade)){ // ep = type->getTotalMaxEp(&totalUpgrade); // } // addItemToVault(&this->ep,this->ep); //regenerate ep upgrade / or boost checkItemInVault(&this->ep,this->ep); //regenerate ep ep += type->getTotalMaxEpRegeneration(&totalUpgrade); if(ep > type->getTotalMaxEp(&totalUpgrade)){ ep = type->getTotalMaxEp(&totalUpgrade); } addItemToVault(&this->ep,this->ep); } } int Unit::update2() { progress2++; return progress2; } bool Unit::computeEp() { if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //if not enough ep if(ep - currSkill->getEpCost() < 0) { return true; } checkItemInVault(&this->ep,this->ep); //decrease ep ep -= currSkill->getEpCost(); addItemToVault(&this->ep,this->ep); if(getType() == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: getType() == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(ep > getType()->getTotalMaxEp(&totalUpgrade)){ ep = getType()->getTotalMaxEp(&totalUpgrade); } addItemToVault(&this->ep,this->ep); return false; } bool Unit::computeHp() { if(currSkill == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: currSkill == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(!isBeingBuilt()){ //cost hp if(currSkill->getHpCost() > 0) { bool decHpResult = decHp(currSkill->getHpCost()); if(decHpResult) { Unit::game->getWorld()->getStats()->die(getFactionIndex()); game->getScriptManager()->onUnitDied(this); } } // If we have negative costs then add life else { checkItemInVault(&this->hp,this->hp); hp += -currSkill->getHpCost(); if(hp > type->getTotalMaxHp(&totalUpgrade)) { hp = type->getTotalMaxHp(&totalUpgrade); } addItemToVault(&this->hp,this->hp); } } return true; } bool Unit::repair(){ if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //increase hp checkItemInVault(&this->hp,this->hp); hp += getType()->getMaxHp()/type->getProductionTime() + 1; if(hp > (getType()->getTotalMaxHp(&totalUpgrade))) { hp = getType()->getTotalMaxHp(&totalUpgrade); addItemToVault(&this->hp,this->hp); return true; } addItemToVault(&this->hp,this->hp); //stop DamageParticles stopDamageParticles(false); return false; } //decrements HP and returns if dead bool Unit::decHp(int i) { if(hp == 0) { return false; } checkItemInVault(&this->hp,this->hp); hp -= i; addItemToVault(&this->hp,this->hp); if(type == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: type == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //startDamageParticles startDamageParticles(); //stop DamageParticles on death if(hp <= 0) { alive= false; hp=0; addItemToVault(&this->hp,this->hp); stopDamageParticles(true); return true; } return false; } string Unit::getDescExtension() const{ Lang &lang= Lang::getInstance(); string str= "\n"; if(commands.empty() == false && commands.size() > 1 ){ Commands::const_iterator it= commands.begin(); for(unsigned int i= 0; i < min((size_t) maxQueuedCommandDisplayCount, commands.size()); ++i){ const CommandType *ct= (*it)->getCommandType(); if(i == 0){ str+= "\n" + lang.get("OrdersOnQueue") + ": "; } str+= "\n#" + intToStr(i + 1) + " " + ct->getName(); ++it; } } return str; } string Unit::getDesc() const { Lang &lang= Lang::getInstance(); //pos //str+="Pos: "+v2iToStr(pos)+"\n"; //hp string str= "\n"; //maxUnitCount if(type->getMaxUnitCount()>0){ str += lang.get("MaxUnitCount")+ ": " + intToStr(faction->getCountForMaxUnitCount(type)) + "/" + intToStr(type->getMaxUnitCount()); } //str += "\n"+lang.get("Hp")+ ": " + intToStr(hp) + "/" + intToStr(type->getTotalMaxHp(&totalUpgrade)) + " [" + floatToStr(getHpRatio()) + "] [" + floatToStr(animProgress) + "]"; str += "\n"+lang.get("Hp")+ ": " + intToStr(hp) + "/" + intToStr(type->getTotalMaxHp(&totalUpgrade)); if(type->getHpRegeneration() != 0 || totalUpgrade.getMaxHpRegeneration() != 0) { str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getHpRegeneration()); if(totalUpgrade.getMaxHpRegeneration() != 0) { str+= "+" + intToStr(totalUpgrade.getMaxHpRegeneration()); } str+= ")"; } //ep if(getType()->getMaxEp()!=0){ str+= "\n" + lang.get("Ep")+ ": " + intToStr(ep) + "/" + intToStr(type->getTotalMaxEp(&totalUpgrade)); } if(type->getEpRegeneration() != 0 || totalUpgrade.getMaxEpRegeneration() != 0) { str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getEpRegeneration()); if(totalUpgrade.getMaxEpRegeneration() != 0) { str += "+" + intToStr(totalUpgrade.getMaxEpRegeneration()); } str+= ")"; } //armor str+= "\n" + lang.get("Armor")+ ": " + intToStr(getType()->getArmor()); if(totalUpgrade.getArmor()!=0){ str+="+"+intToStr(totalUpgrade.getArmor()); } str+= " ("+getType()->getArmorType()->getName()+")"; //sight str+="\n"+ lang.get("Sight")+ ": " + intToStr(getType()->getSight()); if(totalUpgrade.getSight()!=0){ str+="+"+intToStr(totalUpgrade.getSight()); } //kills const Level *nextLevel= getNextLevel(); if(enemyKills > 0 || nextLevel != NULL) { str+= "\n" + lang.get("Kills") +": " + intToStr(enemyKills); if(nextLevel != NULL) { str+= " (" + nextLevel->getName() + ": " + intToStr(nextLevel->getKills()) + ")"; } } //str+= "\nskl: "+scToStr(currSkill->getClass()); //load if(loadCount!=0){ str+= "\n" + lang.get("Load")+ ": " + intToStr(loadCount) +" " + loadType->getName(); } //consumable production for(int i=0; i < getType()->getCostCount(); ++i) { const Resource *r= getType()->getCost(i); if(r->getType()->getClass() == rcConsumable) { str+= "\n"; str+= r->getAmount() < 0 ? lang.get("Produce")+": ": lang.get("Consume")+": "; str+= intToStr(abs(r->getAmount())) + " " + r->getType()->getName(); } } //command info if(commands.empty() == false) { str+= "\n" + commands.front()->getCommandType()->getName(); if(commands.size() > 1) { str+= "\n" + lang.get("OrdersOnQueue") + ": " + intToStr(commands.size()); } } else{ //can store if(getType()->getStoredResourceCount() > 0) { for(int i = 0; i < getType()->getStoredResourceCount(); ++i) { const Resource *r= getType()->getStoredResource(i); str+= "\n" + lang.get("Store") + ": "; str+= intToStr(r->getAmount()) + " " + r->getType()->getName(); } } } return str; } void Unit::applyUpgrade(const UpgradeType *upgradeType){ if(upgradeType == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: upgradeType == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(upgradeType->isAffected(type)){ totalUpgrade.sum(upgradeType, this); checkItemInVault(&this->hp,this->hp); hp += upgradeType->getMaxHp(); hp = max(0,hp); addItemToVault(&this->hp,this->hp); } } void Unit::computeTotalUpgrade(){ faction->getUpgradeManager()->computeTotalUpgrade(this, &totalUpgrade); } void Unit::incKills(int team) { ++kills; if(team != this->getTeam()) { ++enemyKills; } const Level *nextLevel= getNextLevel(); if(nextLevel != NULL && enemyKills >= nextLevel->getKills()) { level= nextLevel; int maxHp= totalUpgrade.getMaxHp(); totalUpgrade.incLevel(type); checkItemInVault(&this->hp,this->hp); hp += totalUpgrade.getMaxHp() - maxHp; addItemToVault(&this->hp,this->hp); } } bool Unit::morph(const MorphCommandType *mct){ if(mct == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: mct == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } const UnitType *morphUnitType= mct->getMorphUnit(); if(morphUnitType == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: morphUnitType == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } Field morphUnitField=fLand; if(morphUnitType->getField(fAir)) morphUnitField=fAir; if(morphUnitType->getField(fLand)) morphUnitField=fLand; if(map->isFreeCellsOrHasUnit(pos, morphUnitType->getSize(), morphUnitField, this,morphUnitType)){ map->clearUnitCells(this, pos); faction->deApplyStaticCosts(type); checkItemInVault(&this->hp,this->hp); hp += morphUnitType->getMaxHp() - type->getMaxHp(); addItemToVault(&this->hp,this->hp); type= morphUnitType; level= NULL; currField=morphUnitField; computeTotalUpgrade(); map->putUnitCells(this, pos); faction->applyDiscount(morphUnitType, mct->getDiscount()); faction->addStore(type); faction->applyStaticProduction(morphUnitType); return true; } else{ return false; } } // ==================== PRIVATE ==================== float Unit::computeHeight(const Vec2i &pos) const{ if(map->isInside(pos) == false || map->isInsideSurface(map->toSurfCoords(pos)) == false) { throw runtime_error("#7 Invalid path position = " + pos.getString()); } float height= map->getCell(pos)->getHeight(); if(currField == fAir) { height += World::airHeight; Unit *unit = map->getCell(pos)->getUnit(fLand); if(unit != NULL && unit->getType()->getHeight() > World::airHeight) { height += (std::min((float)unit->getType()->getHeight(),World::airHeight * 3) - World::airHeight); } else { SurfaceCell *sc = map->getSurfaceCell(map->toSurfCoords(pos)); if(sc != NULL && sc->getObject() != NULL && sc->getObject()->getType() != NULL) { if(sc->getObject()->getType()->getHeight() > World::airHeight) { height += (std::min((float)sc->getObject()->getType()->getHeight(),World::airHeight * 3) - World::airHeight); } } } } return height; } void Unit::updateTarget(){ Unit *target= targetRef.getUnit(); if(target!=NULL){ //update target pos targetPos= target->getCellPos(); Vec2i relPos= targetPos - pos; Vec2f relPosf= Vec2f((float)relPos.x, (float)relPos.y); #ifdef USE_STREFLOP targetRotation= radToDeg(streflop::atan2(relPosf.x, relPosf.y)); #else targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); #endif //update target vec targetVec= target->getCurrVector(); //if(getFrameCount() % 40 == 0) { //logSynchData(string(__FILE__) + string("::") + string(__FUNCTION__) + string(" Line: ") + intToStr(__LINE__)); //logSynchData(); //} } } void Unit::clearCommands() { this->setCurrentUnitTitle(""); this->unitPath->clear(); while(commands.empty() == false) { undoCommand(commands.back()); static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__); MutexSafeWrapper safeMutex(&mutexCommands,mutexOwnerId); delete commands.back(); commands.pop_back(); safeMutex.ReleaseLock(); } changedActiveCommand = false; } void Unit::deleteQueuedCommand(Command *command) { if(getCurrCommand() == command) { this->setCurrentUnitTitle(""); this->unitPath->clear(); } undoCommand(command); delete command; } CommandResult Unit::checkCommand(Command *command) const { if(command == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //if not operative or has not command type => fail if(isOperative() == false || command->getUnit() == this || getType()->hasCommandType(command->getCommandType()) == false || (ignoreCheckCommand == false && this->getFaction()->reqsOk(command->getCommandType()) == false)) { if(SystemFlags::getSystemSettingType(SystemFlags::debugLUA).enabled) SystemFlags::OutputDebug(SystemFlags::debugLUA,"In [%s::%s Line: %d] isOperative() = %d, command->getUnit() = %p, getType()->hasCommandType(command->getCommandType()) = %d, this->getFaction()->reqsOk(command->getCommandType()) = %d\n",__FILE__,__FUNCTION__, __LINE__,isOperative(),command->getUnit(),getType()->hasCommandType(command->getCommandType()),this->getFaction()->reqsOk(command->getCommandType())); // Allow self healing if able to heal own unit type if( command->getUnit() == this && command->getCommandType()->getClass() == ccRepair && this->getType()->getFirstRepairCommand(this->getType()) != NULL) { } else { return crFailUndefined; } } //if pos is not inside the world (if comand has not a pos, pos is (0, 0) and is inside world if(map->isInside(command->getPos()) == false) { if(SystemFlags::getSystemSettingType(SystemFlags::debugLUA).enabled) SystemFlags::OutputDebug(SystemFlags::debugLUA,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__); return crFailUndefined; } //check produced if(command->getCommandType() == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command->getCommandType() == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } const ProducibleType *produced= command->getCommandType()->getProduced(); if(produced!=NULL) { if(ignoreCheckCommand == false && faction->reqsOk(produced) == false) { return crFailReqs; } if(ignoreCheckCommand == false && faction->checkCosts(produced) == false) { return crFailRes; } } //build command specific, check resources and requirements for building if(command->getCommandType()->getClass() == ccBuild) { const UnitType *builtUnit= command->getUnitType(); if(builtUnit == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: builtUnit == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(faction->reqsOk(builtUnit) == false) { return crFailReqs; } if(faction->checkCosts(builtUnit) == false) { return crFailRes; } } //upgrade command specific, check that upgrade is not upgraded else if(command->getCommandType()->getClass() == ccUpgrade) { const UpgradeCommandType *uct= static_cast(command->getCommandType()); if(uct == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: uct == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } if(faction->getUpgradeManager()->isUpgradingOrUpgraded(uct->getProducedUpgrade())){ if(SystemFlags::getSystemSettingType(SystemFlags::debugLUA).enabled) SystemFlags::OutputDebug(SystemFlags::debugLUA,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__, __LINE__); return crFailUndefined; } } return crSuccess; } void Unit::applyCommand(Command *command){ if(command == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } else if(command->getCommandType() == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command->getCommandType() == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //check produced const ProducibleType *produced= command->getCommandType()->getProduced(); if(produced!=NULL) { faction->applyCosts(produced); } //build command specific if(command->getCommandType()->getClass()==ccBuild){ faction->applyCosts(command->getUnitType()); } //upgrade command specific else if(command->getCommandType()->getClass()==ccUpgrade){ const UpgradeCommandType *uct= static_cast(command->getCommandType()); if(uct == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: uct == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } faction->startUpgrade(uct->getProducedUpgrade()); } } CommandResult Unit::undoCommand(Command *command){ if(command == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } else if(command->getCommandType() == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: command->getCommandType() == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } //return cost const ProducibleType *produced= command->getCommandType()->getProduced(); if(produced!=NULL){ faction->deApplyCosts(produced); } //return building cost if not already building it or dead if(command->getCommandType()->getClass() == ccBuild){ if(currSkill->getClass() != scBuild && currSkill->getClass() != scDie) { faction->deApplyCosts(command->getUnitType()); } } //upgrade command cancel from list if(command->getCommandType()->getClass() == ccUpgrade){ const UpgradeCommandType *uct= static_cast(command->getCommandType()); if(uct == NULL) { char szBuf[4096]=""; sprintf(szBuf,"In [%s::%s Line: %d] ERROR: uct == NULL, Unit = [%s]\n",__FILE__,__FUNCTION__,__LINE__,this->toString().c_str()); throw runtime_error(szBuf); } faction->cancelUpgrade(uct->getProducedUpgrade()); } retryCurrCommandCount=0; this->setCurrentUnitTitle(""); return crSuccess; } void Unit::stopDamageParticles(bool force) { if(force == true || (hp > type->getTotalMaxHp(&totalUpgrade) / 2) ) { //printf("Checking to stop damageparticles for unit [%s - %d] hp = %d\n",this->getType()->getName().c_str(),this->getId(),hp); if(Renderer::getInstance().validateParticleSystemStillExists(fire,rsGame) == false) { fire = NULL; } // stop fire if(fire != NULL) { fire->fade(); fire = NULL; } // stop additional particles if(smokeParticleSystems.empty() == false) { for(int i = smokeParticleSystems.size()-1; i >= 0; --i) { UnitParticleSystem *ps = smokeParticleSystems[i]; ps->fade(); smokeParticleSystems.pop_back(); } } if(damageParticleSystems.empty() == false) { for(int i = damageParticleSystems.size()-1; i >= 0; --i) { UnitParticleSystem *ps = damageParticleSystems[i]; UnitParticleSystemType *pst = NULL; int foundParticleIndexType = -2; for(std::map::iterator iterMap = damageParticleSystemsInUse.begin(); iterMap != damageParticleSystemsInUse.end(); ++iterMap) { if(iterMap->second == ps) { foundParticleIndexType = iterMap->first; if(foundParticleIndexType >= 0) { pst = type->damageParticleSystemTypes[foundParticleIndexType]; break; } } } if(force == true || ( pst !=NULL && pst->getMinmaxEnabled() == false )) { damageParticleSystemsInUse.erase(foundParticleIndexType); ps->fade(); damageParticleSystems.pop_back(); } } } } checkCustomizedParticleTriggers(force); } void Unit::checkCustomizedParticleTriggers(bool force) { // Now check if we have special hp triggered particles if(damageParticleSystems.empty() == false) { for(int i = damageParticleSystems.size()-1; i >= 0; --i) { UnitParticleSystem *ps = damageParticleSystems[i]; UnitParticleSystemType *pst = NULL; int foundParticleIndexType = -2; for(std::map::iterator iterMap = damageParticleSystemsInUse.begin(); iterMap != damageParticleSystemsInUse.end(); ++iterMap) { if(iterMap->second == ps) { foundParticleIndexType = iterMap->first; if(foundParticleIndexType >= 0) { pst = type->damageParticleSystemTypes[foundParticleIndexType]; break; } } } if(force == true || (pst != NULL && pst->getMinmaxEnabled() == true)) { bool stopParticle = force; if(force == false) { if(pst->getMinmaxIsPercent() == false) { if(hp < pst->getMinHp() || hp > pst->getMaxHp()) { stopParticle = true; } } else { int hpPercent = (hp / type->getTotalMaxHp(&totalUpgrade) * 100); if(hpPercent < pst->getMinHp() || hpPercent > pst->getMaxHp()) { stopParticle = true; } } } //printf("CHECKING to STOP customized particle trigger by HP [%d to %d percentbased = %d] current hp = %d stopParticle = %d\n",pst->getMinHp(),pst->getMaxHp(),pst->getMinmaxIsPercent(),hp,stopParticle); if(stopParticle == true) { //printf("STOPPING customized particle trigger by HP [%d to %d] current hp = %d\n",pst->getMinHp(),pst->getMaxHp(),hp); damageParticleSystemsInUse.erase(foundParticleIndexType); ps->fade(); damageParticleSystems.pop_back(); } } } } // Now check if we have special hp triggered particles //start additional particles if(showUnitParticles && (type->damageParticleSystemTypes.empty() == false) && force == false && alive == true) { for(unsigned int i = 0; i < type->damageParticleSystemTypes.size(); ++i) { UnitParticleSystemType *pst = type->damageParticleSystemTypes[i]; if(pst->getMinmaxEnabled() == true && damageParticleSystemsInUse.find(i) == damageParticleSystemsInUse.end()) { bool showParticle = false; if(pst->getMinmaxIsPercent() == false) { if(hp >= pst->getMinHp() && hp <= pst->getMaxHp()) { showParticle = true; } } else { int hpPercent = (hp / type->getTotalMaxHp(&totalUpgrade) * 100); if(hpPercent >= pst->getMinHp() && hpPercent <= pst->getMaxHp()) { showParticle = true; } } //printf("CHECKING to START customized particle trigger by HP [%d to %d percentbased = %d] current hp = %d showParticle = %d\n",pst->getMinHp(),pst->getMaxHp(),pst->getMinmaxIsPercent(),hp,showParticle); if(showParticle == true) { //printf("STARTING customized particle trigger by HP [%d to %d] current hp = %d\n",pst->getMinHp(),pst->getMaxHp(),hp); UnitParticleSystem *ups = new UnitParticleSystem(200); pst->setValues(ups); ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } damageParticleSystems.push_back(ups); damageParticleSystemsInUse[i] = ups; Renderer::getInstance().manageParticleSystem(ups, rsGame); } } } } } void Unit::startDamageParticles() { if(hp < type->getMaxHp() / 2 && hp > 0 && alive == true) { //start additional particles if( showUnitParticles && (!type->damageParticleSystemTypes.empty()) ) { for(unsigned int i = 0; i < type->damageParticleSystemTypes.size(); ++i) { UnitParticleSystemType *pst = type->damageParticleSystemTypes[i]; if(pst->getMinmaxEnabled() == false && damageParticleSystemsInUse.find(i) == damageParticleSystemsInUse.end()) { UnitParticleSystem *ups = new UnitParticleSystem(200); pst->setValues(ups); ups->setPos(getCurrVector()); if(getFaction()->getTexture()) { ups->setFactionColor(getFaction()->getTexture()->getPixmapConst()->getPixel3f(0,0)); } damageParticleSystems.push_back(ups); damageParticleSystemsInUse[i] = ups; Renderer::getInstance().manageParticleSystem(ups, rsGame); } } } // start fire if(type->getProperty(UnitType::pBurnable) && fire == NULL) { FireParticleSystem *fps = new FireParticleSystem(200); const Game *game = Renderer::getInstance().getGame(); fps->setSpeed(2.5f / game->getWorld()->getUpdateFps(this->getFactionIndex())); fps->setPos(getCurrVector()); fps->setRadius(type->getSize()/3.f); fps->setTexture(CoreData::getInstance().getFireTexture()); fps->setParticleSize(type->getSize()/3.f); fire= fps; fireParticleSystems.push_back(fps); Renderer::getInstance().manageParticleSystem(fps, rsGame); if(showUnitParticles) { // smoke UnitParticleSystem *ups= new UnitParticleSystem(400); ups->setColorNoEnergy(Vec4f(0.0f, 0.0f, 0.0f, 0.13f)); ups->setColor(Vec4f(0.115f, 0.115f, 0.115f, 0.22f)); ups->setPos(getCurrVector()); ups->setBlendMode(ups->strToBlendMode("black")); ups->setOffset(Vec3f(0,2,0)); ups->setDirection(Vec3f(0,1,-0.2f)); ups->setRadius(type->getSize()/3.f); ups->setShape(Shared::Graphics::UnitParticleSystem::sLinear); ups->setTexture(CoreData::getInstance().getFireTexture()); const Game *game = Renderer::getInstance().getGame(); ups->setSpeed(2.0f / game->getWorld()->getUpdateFps(this->getFactionIndex())); ups->setGravity(0.0004f); ups->setEmissionRate(1); ups->setMaxParticleEnergy(150); ups->setSizeNoEnergy(type->getSize()*0.6f); ups->setParticleSize(type->getSize()*0.8f); smokeParticleSystems.push_back(ups); //damageParticleSystemsInUse[-1] = ups; Renderer::getInstance().manageParticleSystem(ups, rsGame); } } } checkCustomizedParticleTriggers(false); } void Unit::setTargetVec(const Vec3f &targetVec) { this->targetVec= targetVec; logSynchData(__FILE__,__LINE__); } void Unit::setMeetingPos(const Vec2i &meetingPos) { this->meetingPos= meetingPos; map->clampPos(this->meetingPos); if(map->isInside(this->meetingPos) == false || map->isInsideSurface(map->toSurfCoords(this->meetingPos)) == false) { throw runtime_error("#8 Invalid path position = " + this->meetingPos.getString()); } logSynchData(__FILE__,__LINE__); } bool Unit::isMeetingPointSettable() const { return (type != NULL ? type->getMeetingPoint() : false); } int Unit::getFrameCount() const { int frameCount = 0; const Game *game = Renderer::getInstance().getGame(); if(game != NULL && game->getWorld() != NULL) { frameCount = game->getWorld()->getFrameCount(); } return frameCount; } void Unit::exploreCells() { if(this->isOperative()) { const Vec2i &newPos = this->getCenteredPos(); int sightRange = this->getType()->getSight(); int teamIndex = this->getTeam(); if(game == NULL) { throw runtime_error("game == NULL"); } else if(game->getWorld() == NULL) { throw runtime_error("game->getWorld() == NULL"); } game->getWorld()->exploreCells(newPos, sightRange, teamIndex); } } void Unit::logSynchData(string file,int line,string source) { if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true) { char szBuf[4096]=""; sprintf(szBuf, "FrameCount [%d] Unit = %d [%s][%s] pos = %s, lastPos = %s, targetPos = %s, targetVec = %s, meetingPos = %s, progress [%f], progress2 [%d]\nUnit Path [%s]\n", getFrameCount(), id, getFullName().c_str(), faction->getType()->getName().c_str(), //getDesc().c_str(), pos.getString().c_str(), lastPos.getString().c_str(), targetPos.getString().c_str(), targetVec.getString().c_str(), meetingPos.getString().c_str(), // lastRotation, // targetRotation, // rotation, progress, progress2, (unitPath != NULL ? unitPath->toString().c_str() : "NULL")); /* sprintf(szBuf, "FrameCount [%d] Unit = %d [%s][%s] pos = %s, lastPos = %s, targetPos = %s, targetVec = %s, meetingPos = %s, lastRotation [%f], targetRotation [%f], rotation [%f], progress [%f], progress2 [%d]\nUnit Path [%s]\n", getFrameCount(), id, getFullName().c_str(), faction->getType()->getName().c_str(), //getDesc().c_str(), pos.getString().c_str(), lastPos.getString().c_str(), targetPos.getString().c_str(), targetVec.getString().c_str(), meetingPos.getString().c_str(), lastRotation, targetRotation, rotation, progress, progress2, (unitPath != NULL ? unitPath->toString().c_str() : "NULL")); */ if( lastSynchDataString != string(szBuf) || lastFile != file || lastLine != line || lastSource != source) { lastSynchDataString = string(szBuf); lastFile = file; lastSource = source; SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,"----------------------------------- START [%d] ------------------------------------------------\n",getFrameCount()); SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,"[%s::%d]\n",extractFileFromDirectoryPath(file).c_str(),line); if(source != "") { SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,"%s ",source.c_str()); } SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,"%s\n",szBuf); SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,"------------------------------------ END [%d] -------------------------------------------------\n",getFrameCount()); } } } void Unit::addBadHarvestPos(const Vec2i &value) { //Chrono chron; //chron.start(); badHarvestPosList[value] = getFrameCount(); cleanupOldBadHarvestPos(); } void Unit::removeBadHarvestPos(const Vec2i &value) { std::map::iterator iter = badHarvestPosList.find(value); if(iter != badHarvestPosList.end()) { badHarvestPosList.erase(value); } cleanupOldBadHarvestPos(); } bool Unit::isBadHarvestPos(const Vec2i &value, bool checkPeerUnits) const { bool result = false; std::map::const_iterator iter = badHarvestPosList.find(value); if(iter != badHarvestPosList.end()) { result = true; } else if(checkPeerUnits == true) { // Check if any other units of similar type have this position tagged // as bad? for(int i = 0; i < this->getFaction()->getUnitCount(); ++i) { Unit *peerUnit = this->getFaction()->getUnit(i); if( peerUnit != NULL && peerUnit->getId() != this->getId() && peerUnit->getType()->getSize() <= this->getType()->getSize()) { if(peerUnit->isBadHarvestPos(value,false) == true) { result = true; break; } } } } return result; } void Unit::cleanupOldBadHarvestPos() { const int cleanupInterval = (GameConstants::updateFps * 5); bool needToCleanup = (getFrameCount() % cleanupInterval == 0); //printf("========================> cleanupOldBadHarvestPos() [%d] badHarvestPosList.size [%ld] cleanupInterval [%d] getFrameCount() [%d] needToCleanup [%d]\n",getFrameCount(),badHarvestPosList.size(),cleanupInterval,getFrameCount(),needToCleanup); if(needToCleanup == true) { //printf("========================> cleanupOldBadHarvestPos() [%d] badHarvestPosList.size [%ld]\n",getFrameCount(),badHarvestPosList.size()); std::vector purgeList; for(std::map::iterator iter = badHarvestPosList.begin(); iter != badHarvestPosList.end(); iter++) { if(getFrameCount() - iter->second >= cleanupInterval) { //printf("cleanupOldBadHarvestPos() [%d][%d]\n",getFrameCount(),iter->second); purgeList.push_back(iter->first); } } if(purgeList.empty() == false) { char szBuf[4096]=""; sprintf(szBuf,"[cleaning old bad harvest targets] purgeList.size() [%ld]",purgeList.size()); logSynchData(__FILE__,__LINE__,szBuf); for(int i = 0; i < purgeList.size(); ++i) { const Vec2i &item = purgeList[i]; badHarvestPosList.erase(item); } } } } void Unit::setLastHarvestResourceTarget(const Vec2i *pos) { if(pos == NULL) { lastHarvestResourceTarget.first = Vec2i(0); //lastHarvestResourceTarget.second = 0; } else { const Vec2i resourceLocation = *pos; if(resourceLocation != lastHarvestResourceTarget.first) { lastHarvestResourceTarget.first = resourceLocation; //Chrono chron; //chron.start(); lastHarvestResourceTarget.second = getFrameCount(); } else { // If we cannot harvest for > 10 seconds tag the position // as a bad one const int addInterval = (GameConstants::updateFps * 5); if(lastHarvestResourceTarget.second - getFrameCount() >= addInterval) { //printf("-----------------------> setLastHarvestResourceTarget() [%d][%d]\n",getFrameCount(),lastHarvestResourceTarget.second); addBadHarvestPos(resourceLocation); } } } } //void Unit::addCurrentTargetPathTakenCell(const Vec2i &target,const Vec2i &cell) { // if(currentTargetPathTaken.first != target) { // currentTargetPathTaken.second.clear(); // } // currentTargetPathTaken.first = target; // currentTargetPathTaken.second.push_back(cell); //} void Unit::setLastPathfindFailedFrameToCurrentFrame() { lastPathfindFailedFrame = getFrameCount(); } bool Unit::isLastPathfindFailedFrameWithinCurrentFrameTolerance() const { static const bool enablePathfinderEnlargeMaxNodes = Config::getInstance().getBool("EnablePathfinderEnlargeMaxNodes","false"); bool result = enablePathfinderEnlargeMaxNodes; if(enablePathfinderEnlargeMaxNodes) { const int MIN_FRAME_ELAPSED_RETRY = 960; result = (getFrameCount() - lastPathfindFailedFrame >= MIN_FRAME_ELAPSED_RETRY); } return result; } void Unit::setLastStuckFrameToCurrentFrame() { lastStuckFrame = getFrameCount(); } bool Unit::isLastStuckFrameWithinCurrentFrameTolerance() const { const int MIN_FRAME_ELAPSED_RETRY = 300; bool result (getFrameCount() - lastStuckFrame <= MIN_FRAME_ELAPSED_RETRY); return result; } Vec2i Unit::getPosWithCellMapSet() const { Vec2i cellMapPos = this->getType()->getFirstOccupiedCellInCellMap(pos); return cellMapPos; } std::string Unit::toString() const { std::string result = ""; result += "id = " + intToStr(this->id); if(this->type != NULL) { result += " name [" + this->type->getName() + "][" + intToStr(this->type->getId()) + "]"; } if(this->faction != NULL) { result += "\nFactionIndex = " + intToStr(this->faction->getIndex()) + "\n"; result += "teamIndex = " + intToStr(this->faction->getTeam()) + "\n"; result += "startLocationIndex = " + intToStr(this->faction->getStartLocationIndex()) + "\n"; result += "thisFaction = " + intToStr(this->faction->getThisFaction()) + "\n"; result += "control = " + intToStr(this->faction->getControlType()) + "\n"; if(this->faction->getType() != NULL) { result += "factionName = " + this->faction->getType()->getName() + "\n"; } } result += " hp = " + intToStr(this->hp); result += " ep = " + intToStr(this->ep); result += " loadCount = " + intToStr(this->loadCount); result += " deadCount = " + intToStr(this->deadCount); result += " progress = " + floatToStr(this->progress); result += "\n"; result += " lastAnimProgress = " + floatToStr(this->lastAnimProgress); result += " animProgress = " + floatToStr(this->animProgress); result += " highlight = " + floatToStr(this->highlight); result += " progress2 = " + intToStr(this->progress2); result += " kills = " + intToStr(this->kills); result += " enemyKills = " + intToStr(this->enemyKills); result += "\n"; // WARNING!!! Don't access the Unit pointer in this->targetRef in this method or it causes // a stack overflow if(this->targetRef.getUnitId() >= 0) { //result += " targetRef = " + this->targetRef.getUnit()->toString(); result += " targetRef = " + intToStr(this->targetRef.getUnitId()) + " - factionIndex = " + intToStr(this->targetRef.getUnitFaction()->getIndex()); } result += " currField = " + intToStr(this->currField); result += " targetField = " + intToStr(this->targetField); if(level != NULL) { result += " level = " + level->getName(); } result += "\n"; result += " pos = " + pos.getString(); result += " lastPos = " + lastPos.getString(); result += "\n"; result += " targetPos = " + targetPos.getString(); result += " targetVec = " + targetVec.getString(); result += " meetingPos = " + meetingPos.getString(); result += "\n"; result += " lastRotation = " + floatToStr(this->lastRotation); result += " targetRotation = " + floatToStr(this->targetRotation); result += " rotation = " + floatToStr(this->rotation); if(loadType != NULL) { result += " loadType = " + loadType->getName(); } if(currSkill != NULL) { result += " currSkill = " + currSkill->getName(); } result += "\n"; result += " toBeUndertaken = " + intToStr(this->toBeUndertaken); result += " alive = " + intToStr(this->alive); result += " showUnitParticles = " + intToStr(this->showUnitParticles); result += " totalUpgrade = " + totalUpgrade.toString(); result += " " + this->unitPath->toString() + "\n"; result += "\n"; result += "Command count = " + intToStr(commands.size()) + "\n"; int cmdIdx = 0; for(Commands::const_iterator iterList = commands.begin(); iterList != commands.end(); ++iterList) { result += " index = " + intToStr(cmdIdx) + " "; const Command *cmd = *iterList; if(cmd != NULL) { result += cmd->toString() + "\n"; } cmdIdx++; } result += "\n"; result += "modelFacing = " + intToStr(modelFacing.asInt()) + "\n"; result += "retryCurrCommandCount = " + intToStr(retryCurrCommandCount) + "\n"; result += "screenPos = " + screenPos.getString() + "\n"; result += "currentUnitTitle = " + currentUnitTitle + "\n"; result += "inBailOutAttempt = " + intToStr(inBailOutAttempt) + "\n"; return result; } }}//end namespace