From 374634b6306b1b311eadd53dec7133b3692b0442 Mon Sep 17 00:00:00 2001 From: titiger Date: Sun, 20 Nov 2016 16:19:53 +0100 Subject: [PATCH] spawn units on death as requested here https://forum.megaglest.org/index.php?topic=9879.0 --- source/glest_game/types/skill_type.cpp | 20 ++++ source/glest_game/types/skill_type.h | 17 ++- source/glest_game/world/unit_updater.cpp | 143 ++++++++++++++++------- source/glest_game/world/unit_updater.h | 5 +- 4 files changed, 143 insertions(+), 42 deletions(-) diff --git a/source/glest_game/types/skill_type.cpp b/source/glest_game/types/skill_type.cpp index a313fc94..935346cb 100644 --- a/source/glest_game/types/skill_type.cpp +++ b/source/glest_game/types/skill_type.cpp @@ -1354,6 +1354,13 @@ void MorphSkillType::saveGame(XmlNode *rootNode) { DieSkillType::DieSkillType(){ skillClass= scDie; fade=false; + spawn=false; + spawnStartTime=0; + spawnUnit=""; + spawnUnitcount=0; + spawnUnitHealthPercentMin=100; + spawnUnitHealthPercentMax=100; + spawnProbability=100; } void DieSkillType::load(const XmlNode *sn, const XmlNode *attackBoostsNode, @@ -1363,6 +1370,18 @@ void DieSkillType::load(const XmlNode *sn, const XmlNode *attackBoostsNode, SkillType::load(sn, attackBoostsNode,dir, tt, ft, loadedFileList, parentLoader); fade= sn->getChild("fade")->getAttribute("value")->getBoolValue(); + if(sn->hasChild("spawn")){ + const XmlNode *spawnNode= sn->getChild("spawn"); + spawn=true; + spawnStartTime=spawnNode->getAttribute("start-time")->getFloatValue(); + spawnUnit = spawnNode->getChild("unit")->getAttribute("value")->getValue(); + spawnUnitcount = spawnNode->hasChild("amount")?spawnNode->getChild("amount")->getAttribute("value")->getIntValue():0; + if(spawnNode->hasChild("health-percent")){ + spawnUnitHealthPercentMin = spawnNode->getChild("health-percent")->getAttribute("min")->getIntValue(); + spawnUnitHealthPercentMax = spawnNode->getChild("health-percent")->getAttribute("max")->getIntValue(); + } + spawnProbability = spawnNode->hasChild("probability")?spawnNode->getChild("probability")->getAttribute("value")->getIntValue():spawnProbability; + } // else keep defaults } string DieSkillType::toString(bool translatedValue) const{ @@ -1379,6 +1398,7 @@ void DieSkillType::saveGame(XmlNode *rootNode) { XmlNode *dieSkillTypeNode = rootNode->addChild("DieSkillType"); dieSkillTypeNode->addAttribute("fade",intToStr(fade), mapTagReplacements); + // no need to save spawn attributes } StaticSound *DieSkillType::getSound() const{ diff --git a/source/glest_game/types/skill_type.h b/source/glest_game/types/skill_type.h index cad0f652..fe05990b 100644 --- a/source/glest_game/types/skill_type.h +++ b/source/glest_game/types/skill_type.h @@ -476,16 +476,31 @@ public: class DieSkillType: public SkillType { private: bool fade; + bool spawn; + float spawnStartTime; + string spawnUnit; + int spawnUnitcount; + int spawnUnitHealthPercentMin; + int spawnUnitHealthPercentMax; + int spawnProbability; public: DieSkillType(); - bool getFade() const {return fade;} virtual void load(const XmlNode *sn, const XmlNode *attackBoostsNode, const string &dir, const TechTree *tt, const FactionType *ft, std::map > > &loadedFileList, string parentLoader); virtual string toString(bool translatedValue) const; + bool getFade() const {return fade;} + bool getSpawn() const {return spawn;} + inline int getSpawnStartTime() const {return spawnStartTime;} + inline string getSpawnUnit() const {return spawnUnit;} + inline int getSpawnUnitCount() const {return spawnUnitcount;} + inline int getSpawnUnitHealthPercentMin() const {return spawnUnitHealthPercentMin;} + inline int getSpawnUnitHealthPercentMax() const {return spawnUnitHealthPercentMax;} + inline int getSpawnProbability() const {return spawnProbability;} + virtual void saveGame(XmlNode *rootNode); StaticSound *getSound() const; }; diff --git a/source/glest_game/world/unit_updater.cpp b/source/glest_game/world/unit_updater.cpp index 3a5c88e6..78e4e6b7 100644 --- a/source/glest_game/world/unit_updater.cpp +++ b/source/glest_game/world/unit_updater.cpp @@ -182,6 +182,27 @@ bool UnitUpdater::updateUnit(Unit *unit) { if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld [after playsound]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); + + //start attack particle system + if(unit->getCurrSkill()->getClass() == scDie) { + const DieSkillType *dst= static_cast(unit->getCurrSkill()); + + if(dst->getSpawn() == true){ + float spawnStartTime = truncateDecimal(dst->getSpawnStartTime(),6); + float lastAnimProgress = truncateDecimal(unit->getLastAnimProgressAsFloat(),6); + float animProgress = truncateDecimal(unit->getAnimProgressAsFloat(),6); + + bool startSpawnNow = (spawnStartTime >= lastAnimProgress && spawnStartTime < animProgress); + if(startSpawnNow){ + // spawn the units + spawn(unit, dst->getSpawnUnit(), dst->getSpawnUnitCount(), + dst->getSpawnUnitHealthPercentMin(), + dst->getSpawnUnitHealthPercentMax(), + dst->getSpawnProbability()); + } + } + } + //start attack particle system if(unit->getCurrSkill()->getClass() == scAttack) { const AttackSkillType *ast= static_cast(unit->getCurrSkill()); @@ -275,7 +296,10 @@ bool UnitUpdater::updateUnit(Unit *unit) { if (act != NULL && act->getAttackSkillType() != NULL && act->getAttackSkillType()->getSpawnUnit() != "" && act->getAttackSkillType()->getSpawnUnitCount() > 0) { - spawnAttack(unit,act->getAttackSkillType()->getSpawnUnit(),act->getAttackSkillType()->getSpawnUnitCount(),act->getAttackSkillType()->getSpawnUnitAtTarget()); + spawnAttack(unit, act->getAttackSkillType()->getSpawnUnit(), + act->getAttackSkillType()->getSpawnUnitCount(), 100, + 100, 100, + act->getAttackSkillType()->getSpawnUnitAtTarget()); } } } @@ -313,51 +337,46 @@ bool UnitUpdater::updateUnit(Unit *unit) { return processUnitCommand; } -void UnitUpdater::spawnAttack(Unit *unit,string spawnUnit,int spawnUnitcount,bool spawnUnitAtTarget,Vec2i targetPos) { +void UnitUpdater::spawn(Unit *unit,string spawnUnit,int spawnUnitcount,int healthMin,int healthMax,int probability) { if(spawnUnit != "" && spawnUnitcount > 0) { - - const FactionType *ft= unit->getFaction()->getType(); - const UnitType *spawnUnitType = ft->getUnitType(spawnUnit); int spawnCount = spawnUnitcount; for (int y=0; y < spawnCount; ++y) { - if(spawnUnitType->getMaxUnitCount() > 0) { - if(spawnUnitType->getMaxUnitCount() <= unit->getFaction()->getCountForMaxUnitCount(spawnUnitType)) { - break; + if (probability > 0 && probability < 100 + && unit->getRandom()->randRange(1, 100) <= probability) { + continue; + } + Unit* spawned=this->spawnUnit(unit,spawnUnit); + if(spawned!=NULL){ + if(healthMin>0 && healthMin<100 && healthMax>=healthMin && healthMax<=100){ + int damagePercent=100-unit->getRandom()->randRange(healthMin, healthMax); + //printf("damagePercent=%d\n",damagePercent); + spawned->decHp((spawned->getHp()*damagePercent)/100); } } - UnitPathInterface *newpath = NULL; - switch(this->game->getGameSettings()->getPathFinderType()) { - case pfBasic: - newpath = new UnitPathBasic(); - break; - default: - throw megaglest_runtime_error("detected unsupported pathfinder type!"); - } + // no stat count !! + //world->getStats()->produce(unit->getFactionIndex(),spawned->getType()->getCountUnitProductionInStats()); + } + } +} - Unit *spawned= new Unit(world->getNextUnitId(unit->getFaction()), newpath, - Vec2i(0), spawnUnitType, unit->getFaction(), - world->getMap(), CardinalDir::NORTH); - //SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] about to place unit for unit [%s]\n",__FILE__,__FUNCTION__,__LINE__,spawned->toString().c_str()); - bool placedSpawnUnit=false; - if(targetPos==Vec2i(-10,-10)) { - targetPos=unit->getTargetPos(); - } - if(spawnUnitAtTarget) { - placedSpawnUnit=world->placeUnit(targetPos, 10, spawned); - } else { - placedSpawnUnit=world->placeUnit(unit->getCenteredPos(), 10, spawned); - } - if(!placedSpawnUnit) { - //SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] COULD NOT PLACE UNIT for unitID [%d]\n",__FILE__,__FUNCTION__,__LINE__,spawned->getId()); +void UnitUpdater::spawnAttack(Unit *unit,string spawnUnit,int spawnUnitcount,int healthMin,int healthMax,int probability,bool spawnUnitAtTarget,Vec2i targetPos) { + if(spawnUnit != "" && spawnUnitcount > 0) { - // This will also cleanup newPath - delete spawned; - spawned = NULL; + int spawnCount = spawnUnitcount; + for (int y=0; y < spawnCount; ++y) { + if (probability > 0 && probability < 100 + && unit->getRandom()->randRange(1, 100) <= probability) { + continue; } - else { - spawned->create(); - spawned->born(NULL); - world->getStats()->produce(unit->getFactionIndex(),spawned->getType()->getCountUnitProductionInStats()); + Unit* spawned=this->spawnUnit(unit,spawnUnit,spawnUnitAtTarget?targetPos:unit->getCenteredPos()); + if(spawned!=NULL){ + if(healthMin>0 && healthMin<100 && healthMax>=healthMin && healthMax<=100){ + int damagePercent=100-unit->getRandom()->randRange(healthMin, healthMax); + //printf("damagePercent=%d\n",damagePercent); + spawned->decHp((spawned->getHp()*damagePercent)/100); + } + // no stat count !! + // world->getStats()->produce(unit->getFactionIndex(),spawned->getType()->getCountUnitProductionInStats()); const CommandType *ct= spawned->getType()->getFirstAttackCommand(unit->getTargetField()); if(ct == NULL){ ct= spawned->computeCommandType(targetPos,map->getCell(targetPos)->getUnit(unit->getTargetField())); @@ -366,12 +385,54 @@ void UnitUpdater::spawnAttack(Unit *unit,string spawnUnit,int spawnUnitcount,boo if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); spawned->giveCommand(new Command(ct, targetPos)); } - scriptManager->onUnitCreated(spawned); } } } } + +Unit* UnitUpdater::spawnUnit(Unit *unit,string spawnUnit,Vec2i spawnPos) { + const FactionType *ft= unit->getFaction()->getType(); + const UnitType *spawnUnitType = ft->getUnitType(spawnUnit); + Vec2i _spawnPos=spawnPos; + if(_spawnPos==Vec2i(-10,-10)) { + _spawnPos=unit->getCenteredPos(); + } + if(spawnUnitType->getMaxUnitCount() > 0) { + if(spawnUnitType->getMaxUnitCount() <= unit->getFaction()->getCountForMaxUnitCount(spawnUnitType)) { + return NULL; + } + } + + UnitPathInterface *newpath = NULL; + switch(this->game->getGameSettings()->getPathFinderType()) { + case pfBasic: + newpath = new UnitPathBasic(); + break; + default: + throw megaglest_runtime_error("detected unsupported pathfinder type!"); + } + + Unit *spawned= new Unit(world->getNextUnitId(unit->getFaction()), newpath, + Vec2i(0), spawnUnitType, unit->getFaction(), + world->getMap(), CardinalDir::NORTH); + + bool placedSpawnUnit=world->placeUnit(_spawnPos, 10, spawned); + + if(!placedSpawnUnit) { + // This will also cleanup newPath + delete spawned; + spawned = NULL; + } + else { + spawned->create(); + spawned->born(NULL); + scriptManager->onUnitCreated(spawned); + } + return spawned; +} + + // ==================== progress commands ==================== //VERY IMPORTANT: compute next state depending on the first order of the list @@ -3339,7 +3400,9 @@ void ParticleDamager::update(ParticleSystem *particleSystem) { //check for spawnattack if(projectileType->getSpawnUnit()!="" && projectileType->getSpawnUnitcount()>0 ){ - unitUpdater->spawnAttack(attacker,projectileType->getSpawnUnit(),projectileType->getSpawnUnitcount(),projectileType->getSpawnUnitAtTarget(),targetPos); + unitUpdater->spawnAttack(attacker, projectileType->getSpawnUnit(), 100, + 100, 100, projectileType->getSpawnUnitcount(), + projectileType->getSpawnUnitAtTarget(), targetPos); } // check for shake and shake diff --git a/source/glest_game/world/unit_updater.h b/source/glest_game/world/unit_updater.h index 6063a48c..b02100aa 100644 --- a/source/glest_game/world/unit_updater.h +++ b/source/glest_game/world/unit_updater.h @@ -103,7 +103,6 @@ public: //update skills bool updateUnit(Unit *unit); - void spawnAttack(Unit *unit,string spawnUnit,int spawnUnitcount,bool spawnUnitAtTarget,Vec2i targetPos=Vec2i(-10,-10)); //update commands void updateUnitCommand(Unit *unit, int frameIndex); @@ -153,6 +152,10 @@ private: bool unitOnRange(Unit *unit, int range, Unit **enemyPtr, const AttackSkillType *ast,bool evalMode=false); void enemiesAtDistance(const Unit *unit, const Unit *priorityUnit, int distance, vector &enemies); + void spawn(Unit *unit,string spawnUnit,int spawnUnitcount,int healthMin,int healthMax,int probability); + void spawnAttack(Unit *unit,string spawnUnit,int spawnUnitcount,int healthMin,int healthMax,int probability,bool spawnUnitAtTarget,Vec2i targetPos=Vec2i(-10,-10)); + Unit* spawnUnit(Unit *unit,string spawnUnit,Vec2i targetPos=Vec2i(-10,-10)); + Unit * findPeerUnitBuilder(Unit *unit); void SwapActiveCommand(Unit *unitSrc, Unit *unitDest); void SwapActiveCommandState(Unit *unit, CommandStateType commandStateType,