From 4a4225ac0c4ea69612d0dbc5c36e3ea69ea5bf4c Mon Sep 17 00:00:00 2001 From: rt Date: Fri, 25 Aug 2017 19:58:22 +0200 Subject: [PATCH] fix #5730 --- rts/Sim/Units/UnitTypes/Builder.cpp | 635 ++++++++++++++++------------ rts/Sim/Units/UnitTypes/Builder.h | 8 + 2 files changed, 363 insertions(+), 280 deletions(-) diff --git a/rts/Sim/Units/UnitTypes/Builder.cpp b/rts/Sim/Units/UnitTypes/Builder.cpp index c60ef58f368..cd9cba0541d 100644 --- a/rts/Sim/Units/UnitTypes/Builder.cpp +++ b/rts/Sim/Units/UnitTypes/Builder.cpp @@ -117,11 +117,10 @@ void CBuilder::PreInit(const UnitLoadParams& params) bool CBuilder::CanAssistUnit(const CUnit* u, const UnitDef* def) const { - return - unitDef->canAssist - && (!def || (u->unitDef == def)) - && u->beingBuilt && (u->buildProgress < 1.0f) - && (!u->soloBuilder || (u->soloBuilder == this)); + if (!unitDef->canAssist) + return false; + + return ((def == nullptr || u->unitDef == def) && u->beingBuilt && (u->buildProgress < 1.0f) && (!u->soloBuilder || u->soloBuilder == this)); } @@ -138,333 +137,409 @@ bool CBuilder::CanRepairUnit(const CUnit* u) const } -void CBuilder::Update() -{ - CBuilderCAI* cai = static_cast(commandAI); - - const CCommandQueue& cQueue = cai->commandQue; - const Command& fCommand = (!cQueue.empty())? cQueue.front(): Command(CMD_STOP); - - nanoPieceCache.Update(); - if (!beingBuilt && !IsStunned()) { - if (terraforming && inBuildStance) { - assert(!mapDamage->disabled); // The map should not be deformed in the first place. - - const float* heightmap = readMap->GetCornerHeightMapSynced(); - float terraformScale = 0.1f; +bool CBuilder::UpdateTerraform(const Command&) +{ + CUnit* curBuildee = curBuild; - switch (terraformType) { - case Terraform_Building: - if (curBuild != nullptr) { - if (curBuild->terraformLeft <= 0.0f) - terraformScale = 0.0f; - else - terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft; + if (!terraforming || !inBuildStance) + return false; - curBuild->terraformLeft -= (terraformSpeed + terraformHelp); + const float* heightmap = readMap->GetCornerHeightMapSynced(); + float terraformScale = 0.1f; - terraformHelp = 0.0f; - terraformScale = std::min(terraformScale, 1.0f); + assert(!mapDamage->disabled); - // prevent building from timing out while terraforming for it - curBuild->AddBuildPower(this, 0.0f); + switch (terraformType) { + case Terraform_Building: { + if (curBuildee != nullptr) { + if (curBuildee->terraformLeft <= 0.0f) + terraformScale = 0.0f; + else + terraformScale = (terraformSpeed + terraformHelp) / curBuildee->terraformLeft; - for (int z = tz1; z <= tz2; z++) { - for (int x = tx1; x <= tx2; x++) { - int idx = z * mapDims.mapxp1 + x; - float ch = heightmap[idx]; + curBuildee->terraformLeft -= (terraformSpeed + terraformHelp); - readMap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale); - } - } + terraformHelp = 0.0f; + terraformScale = std::min(terraformScale, 1.0f); - if (curBuild->terraformLeft <= 0.0f) { - terraforming = false; + // prevent building from timing out while terraforming for it + curBuildee->AddBuildPower(this, 0.0f); - mapDamage->RecalcArea(tx1, tx2, tz1, tz2); - curBuild->groundLevelled = true; + for (int z = tz1; z <= tz2; z++) { + for (int x = tx1; x <= tx2; x++) { + const int idx = z * mapDims.mapxp1 + x; - if (eventHandler.TerraformComplete(this, curBuild)) { - StopBuild(); - } - } - } - break; - case Terraform_Restore: - if (myTerraformLeft <= 0.0f) - terraformScale = 0.0f; - else - terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft; - - myTerraformLeft -= (terraformSpeed + terraformHelp); - - terraformHelp = 0.0f; - terraformScale = std::min(terraformScale, 1.0f); - - for (int z = tz1; z <= tz2; z++) { - for (int x = tx1; x <= tx2; x++) { - int idx = z * mapDims.mapxp1 + x; - float ch = heightmap[idx]; - float oh = readMap->GetOriginalHeightMapSynced()[idx]; - - readMap->AddHeight(idx, (oh - ch) * terraformScale); - } + readMap->AddHeight(idx, (curBuildee->pos.y - heightmap[idx]) * terraformScale); } + } + + if (curBuildee->terraformLeft <= 0.0f) { + terraforming = false; - if (myTerraformLeft <= 0.0f) { - terraforming = false; + mapDamage->RecalcArea(tx1, tx2, tz1, tz2); + curBuildee->groundLevelled = true; - mapDamage->RecalcArea(tx1, tx2, tz1, tz2); + if (eventHandler.TerraformComplete(this, curBuildee)) { StopBuild(); } - break; + } } + } break; + case Terraform_Restore: { + if (myTerraformLeft <= 0.0f) + terraformScale = 0.0f; + else + terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft; + + myTerraformLeft -= (terraformSpeed + terraformHelp); - ScriptDecloak(true); - CreateNanoParticle(terraformCenter, terraformRadius * 0.5f, false); + terraformHelp = 0.0f; + terraformScale = std::min(terraformScale, 1.0f); for (int z = tz1; z <= tz2; z++) { - // smooth the borders x - for (int x = 1; x <= 3; x++) { - if (tx1 - 3 >= 0) { - const float ch3 = heightmap[z * mapDims.mapxp1 + tx1 ]; - const float ch = heightmap[z * mapDims.mapxp1 + tx1 - x]; - const float ch2 = heightmap[z * mapDims.mapxp1 + tx1 - 3]; - const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; - - readMap->AddHeight(z * mapDims.mapxp1 + tx1 - x, amount); - } - if (tx2 + 3 < mapDims.mapx) { - const float ch3 = heightmap[z * mapDims.mapxp1 + tx2 ]; - const float ch = heightmap[z * mapDims.mapxp1 + tx2 + x]; - const float ch2 = heightmap[z * mapDims.mapxp1 + tx2 + 3]; - const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; + for (int x = tx1; x <= tx2; x++) { + int idx = z * mapDims.mapxp1 + x; + float ch = heightmap[idx]; + float oh = readMap->GetOriginalHeightMapSynced()[idx]; - readMap->AddHeight(z * mapDims.mapxp1 + tx2 + x, amount); - } + readMap->AddHeight(idx, (oh - ch) * terraformScale); } } - for (int z = 1; z <= 3; z++) { - // smooth the borders z - for (int x = tx1; x <= tx2; x++) { - if (tz1 - 3 >= 0) { - const float ch3 = heightmap[(tz1 ) * mapDims.mapxp1 + x]; - const float ch = heightmap[(tz1 - z) * mapDims.mapxp1 + x]; - const float ch2 = heightmap[(tz1 - 3) * mapDims.mapxp1 + x]; - const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; - readMap->AddHeight((tz1 - z) * mapDims.mapxp1 + x, adjust); - } - if (tz2 + 3 < mapDims.mapy) { - const float ch3 = heightmap[(tz2 ) * mapDims.mapxp1 + x]; - const float ch = heightmap[(tz2 + z) * mapDims.mapxp1 + x]; - const float ch2 = heightmap[(tz2 + 3) * mapDims.mapxp1 + x]; - const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; + if (myTerraformLeft <= 0.0f) { + terraforming = false; - readMap->AddHeight((tz2 + z) * mapDims.mapxp1 + x, adjust); - } - } + mapDamage->RecalcArea(tx1, tx2, tz1, tz2); + StopBuild(); } - } - else if (helpTerraform != nullptr && inBuildStance) { - if (helpTerraform->terraforming) { - ScriptDecloak(true); + } break; + } - helpTerraform->terraformHelp += terraformSpeed; - CreateNanoParticle(helpTerraform->terraformCenter, helpTerraform->terraformRadius * 0.5f, false); - } else { - DeleteDeathDependence(helpTerraform, DEPENDENCE_TERRAFORM); - helpTerraform = nullptr; - StopBuild(true); + ScriptDecloak(true); + CreateNanoParticle(terraformCenter, terraformRadius * 0.5f, false); + + // smooth the x-borders + for (int z = tz1; z <= tz2; z++) { + for (int x = 1; x <= 3; x++) { + if (tx1 - 3 >= 0) { + const float ch3 = heightmap[z * mapDims.mapxp1 + tx1 ]; + const float ch = heightmap[z * mapDims.mapxp1 + tx1 - x]; + const float ch2 = heightmap[z * mapDims.mapxp1 + tx1 - 3]; + const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; + + readMap->AddHeight(z * mapDims.mapxp1 + tx1 - x, amount); } - } - else if (curBuild != nullptr && cai->IsInBuildRange(curBuild)) { - if (fCommand.GetID() == CMD_WAIT) { - if (curBuild->buildProgress < 1.0f) { - // prevent buildee from decaying (we cannot call StopBuild here) - curBuild->AddBuildPower(this, 0.0f); - } else { - // stop repairing (FIXME: should be much cleaner to let BuilderCAI - // call this instead when a wait command is given?) - StopBuild(); - } - } else { - if (curBuild->soloBuilder != nullptr && (curBuild->soloBuilder != this)) { - StopBuild(); - } else { - if (inBuildStance || true) { - // NOTE: - // technically this block of code should be guarded by - // "if (inBuildStance)", but doing so can create zombie - // guarders because scripts might not set inBuildStance - // to true when guard or repair orders are executed and - // SetRepairTarget does not check for it - // - // StartBuild *does* ensure construction will not start - // until inBuildStance is set to true by the builder's - // script, and there are no cases during construction - // when inBuildStance can become false yet the buildee - // should be kept from decaying, so this is free from - // serious side-effects (when repairing, a builder might - // start adding build-power before having fully finished - // its opening animation) - // - ScriptDecloak(true); - - // adjusted build-speed: use repair-speed on units with - // progress >= 1 rather than raw build-speed on buildees - // with progress < 1 - float adjBuildSpeed = 0.0f; - - if (curBuild->buildProgress >= 1.0f) { - adjBuildSpeed = std::min(repairSpeed, unitDef->maxRepairSpeed * 0.5f - curBuild->repairAmount); // repair - } else { - adjBuildSpeed = buildSpeed; // build - } - - if (adjBuildSpeed > 0.0f && curBuild->AddBuildPower(this, adjBuildSpeed)) { - CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false); - } else { - // check if buildee finished construction - if (!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) { - StopBuild(); - } - } - } - } + if (tx2 + 3 < mapDims.mapx) { + const float ch3 = heightmap[z * mapDims.mapxp1 + tx2 ]; + const float ch = heightmap[z * mapDims.mapxp1 + tx2 + x]; + const float ch2 = heightmap[z * mapDims.mapxp1 + tx2 + 3]; + const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; + + readMap->AddHeight(z * mapDims.mapxp1 + tx2 + x, amount); } } - else if (curReclaim != nullptr && f3SqDist(curReclaim->pos, pos) < Square(buildDistance + curReclaim->radius) && inBuildStance) { - if (fCommand.GetID() == CMD_WAIT) { - StopBuild(); - } else { - ScriptDecloak(true); + } - if (curReclaim->AddBuildPower(this, -reclaimSpeed)) { - CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true, (reclaimingUnit && curReclaim->team != team)); - } + // smooth the z-borders + for (int z = 1; z <= 3; z++) { + for (int x = tx1; x <= tx2; x++) { + if ((tz1 - 3) >= 0) { + const float ch3 = heightmap[(tz1 ) * mapDims.mapxp1 + x]; + const float ch = heightmap[(tz1 - z) * mapDims.mapxp1 + x]; + const float ch2 = heightmap[(tz1 - 3) * mapDims.mapxp1 + x]; + const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; + + readMap->AddHeight((tz1 - z) * mapDims.mapxp1 + x, adjust); + } + if ((tz2 + 3) < mapDims.mapy) { + const float ch3 = heightmap[(tz2 ) * mapDims.mapxp1 + x]; + const float ch = heightmap[(tz2 + z) * mapDims.mapxp1 + x]; + const float ch2 = heightmap[(tz2 + 3) * mapDims.mapxp1 + x]; + const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; + + readMap->AddHeight((tz2 + z) * mapDims.mapxp1 + x, adjust); } } - else if (curResurrect != nullptr && f3SqDist(curResurrect->pos, pos) < Square(buildDistance + curResurrect->radius) && inBuildStance) { - const UnitDef* ud = curResurrect->udef; + } - if (fCommand.GetID() == CMD_WAIT) { - StopBuild(); - } else { - if (ud != nullptr) { - if ((modInfo.reclaimMethod != 1) && (curResurrect->reclaimLeft < 1)) { - // This corpse has been reclaimed a little, need to restore - // the resources before we can let the player resurrect it. - curResurrect->AddBuildPower(this, resurrectSpeed); - } else { - // Corpse has been restored, begin resurrection - const float step = resurrectSpeed / ud->buildTime; + return true; +} - const bool resurrectAllowed = eventHandler.AllowFeatureBuildStep(this, curResurrect, step); - const bool canExecResurrect = (resurrectAllowed && UseEnergy(ud->energy * step * modInfo.resurrectEnergyCostFactor)); +bool CBuilder::AssistTerraform(const Command&) +{ + CBuilder* helpTerraformee = helpTerraform; - if (canExecResurrect) { - curResurrect->resurrectProgress += step; - curResurrect->resurrectProgress = std::min(curResurrect->resurrectProgress, 1.0f); + if (helpTerraform == nullptr || !inBuildStance) + return false; - CreateNanoParticle(curResurrect->midPos, curResurrect->radius * 0.7f, gsRNG.NextInt(2)); - } + if (!helpTerraformee->terraforming) { + // delete our helpTerraform dependence + StopBuild(true); + return true; + } + + ScriptDecloak(true); + + helpTerraformee->terraformHelp += terraformSpeed; + CreateNanoParticle(helpTerraformee->terraformCenter, helpTerraformee->terraformRadius * 0.5f, false); + return true; +} - if (curResurrect->resurrectProgress >= 1.0f) { - if (!curResurrect->deleteMe) { - // resurrect finished and we are the first - curResurrect->UnBlock(); +bool CBuilder::UpdateBuild(const Command& fCommand) +{ + CUnit* curBuildee = curBuild; + CBuilderCAI* cai = static_cast(commandAI); - UnitLoadParams resurrecteeParams = {ud, this, curResurrect->pos, ZeroVector, -1, team, curResurrect->buildFacing, false, false}; - CUnit* resurrectee = unitLoader->LoadUnit(resurrecteeParams); + if (curBuild == nullptr || !cai->IsInBuildRange(curBuild)) + return false; - assert(ud == resurrectee->unitDef); + if (fCommand.GetID() == CMD_WAIT) { + if (curBuildee->buildProgress < 1.0f) { + // prevent buildee from decaying (we cannot call StopBuild here) + curBuildee->AddBuildPower(this, 0.0f); + } else { + // stop repairing (FIXME: should be much cleaner to let BuilderCAI + // call this instead when a wait command is given?) + StopBuild(); + } - resurrectee->SetSoloBuilder(this); + return true; + } - // TODO: make configurable if this should happen - resurrectee->health *= 0.05f; + if (curBuildee->soloBuilder != nullptr && (curBuildee->soloBuilder != this)) { + StopBuild(); + return true; + } - for (auto it = cai->resurrecters.begin(); it != cai->resurrecters.end(); ++it) { - CBuilder* bld = static_cast(unitHandler->GetUnit(*it)); - CCommandAI* bldCAI = bld->commandAI; + // NOTE: + // technically this block of code should be guarded by + // "if (inBuildStance)", but doing so can create zombie + // guarders because scripts might not set inBuildStance + // to true when guard or repair orders are executed and + // SetRepairTarget does not check for it + // + // StartBuild *does* ensure construction will not start + // until inBuildStance is set to true by the builder's + // script, and there are no cases during construction + // when inBuildStance can become false yet the buildee + // should be kept from decaying, so this is free from + // serious side-effects (when repairing, a builder might + // start adding build-power before having fully finished + // its opening animation) + if (!(inBuildStance || true)) + return true; + + ScriptDecloak(true); + + // adjusted build-speed: use repair-speed on units with + // progress >= 1 rather than raw build-speed on buildees + // with progress < 1 + float adjBuildSpeed = buildSpeed; + + if (curBuildee->buildProgress >= 1.0f) + adjBuildSpeed = std::min(repairSpeed, unitDef->maxRepairSpeed * 0.5f - curBuildee->repairAmount); // repair + + if (adjBuildSpeed > 0.0f && curBuildee->AddBuildPower(this, adjBuildSpeed)) { + CreateNanoParticle(curBuildee->midPos, curBuildee->radius * 0.5f, false); + return true; + } - if (bldCAI->commandQue.empty()) - continue; + // check if buildee finished construction + if (curBuildee->beingBuilt || curBuildee->health < curBuildee->maxHealth) + return true; - Command& c = bldCAI->commandQue.front(); + StopBuild(); + return true; +} - if (c.GetID() != CMD_RESURRECT || c.params.size() != 1) - continue; +bool CBuilder::UpdateReclaim(const Command& fCommand) +{ + // AddBuildPower can invoke StopBuild indirectly even if returns true + // and reset curReclaim to null (which would crash CreateNanoParticle) + CSolidObject* curReclaimee = curReclaim; - if ((c.params[0] - unitHandler->MaxUnits()) != curResurrect->id) - continue; + if (curReclaim == nullptr || f3SqDist(curReclaim->pos, pos) >= Square(buildDistance + curReclaim->radius) || !inBuildStance) + return false; - if (!teamHandler->Ally(allyteam, bld->allyteam)) - continue; + if (fCommand.GetID() == CMD_WAIT) { + StopBuild(); + return true; + } - // all units that were rezzing shall assist the repair too - bld->lastResurrected = resurrectee->id; - // prevent FinishCommand from removing this command when the - // feature is deleted, since it is needed to start the repair - // (WTF!) - c.params[0] = INT_MAX / 2; - } + ScriptDecloak(true); - // this takes one simframe to do the deletion - featureHandler->DeleteFeature(curResurrect); - } + if (!curReclaimee->AddBuildPower(this, -reclaimSpeed)) + return true; - StopBuild(true); - } - } - } else { - StopBuild(true); - } - } + CreateNanoParticle(curReclaimee->midPos, curReclaimee->radius * 0.7f, true, (reclaimingUnit && curReclaimee->team != team)); + return true; +} + +bool CBuilder::UpdateResurrect(const Command& fCommand) +{ + CBuilderCAI* cai = static_cast(commandAI); + CFeature* curResurrectee = curResurrect; + + const UnitDef* ud = curResurrectee->udef; + + if (curResurrect == nullptr || f3SqDist(curResurrect->pos, pos) >= Square(buildDistance + curResurrect->radius) || !inBuildStance) + return false; + + if (fCommand.GetID() == CMD_WAIT) { + StopBuild(); + return true; + } + + if (ud == nullptr) { + StopBuild(true); + return true; + } + + if ((modInfo.reclaimMethod != 1) && (curResurrectee->reclaimLeft < 1)) { + // this corpse has been reclaimed a little, need to restore + // its resources before we can let the player resurrect it + curResurrectee->AddBuildPower(this, resurrectSpeed); + return true; + } + + // corpse has been restored, begin resurrection + const float step = resurrectSpeed / ud->buildTime; + + const bool resurrectAllowed = eventHandler.AllowFeatureBuildStep(this, curResurrectee, step); + const bool canExecResurrect = (resurrectAllowed && UseEnergy(ud->energy * step * modInfo.resurrectEnergyCostFactor)); + + if (canExecResurrect) { + curResurrectee->resurrectProgress += step; + curResurrectee->resurrectProgress = std::min(curResurrectee->resurrectProgress, 1.0f); + + CreateNanoParticle(curResurrectee->midPos, curResurrectee->radius * 0.7f, gsRNG.NextInt(2)); + } + + if (curResurrectee->resurrectProgress < 1.0f) + return true; + + if (!curResurrectee->deleteMe) { + // resurrect finished and we are the first + curResurrectee->UnBlock(); + + UnitLoadParams resurrecteeParams = {ud, this, curResurrectee->pos, ZeroVector, -1, team, curResurrectee->buildFacing, false, false}; + CUnit* resurrectee = unitLoader->LoadUnit(resurrecteeParams); + + assert(ud == resurrectee->unitDef); + resurrectee->SetSoloBuilder(this); + + // TODO: make configurable if this should happen + resurrectee->health *= 0.05f; + + for (const int resurrecterID: cai->resurrecters) { + CBuilder* resurrecter = static_cast(unitHandler->GetUnit(resurrecterID)); + CCommandAI* resurrecterCAI = resurrecter->commandAI; + + if (resurrecterCAI->commandQue.empty()) + continue; + + Command& c = resurrecterCAI->commandQue.front(); + + if (c.GetID() != CMD_RESURRECT || c.params.size() != 1) + continue; + + if ((c.params[0] - unitHandler->MaxUnits()) != curResurrectee->id) + continue; + + if (!teamHandler->Ally(allyteam, resurrecter->allyteam)) + continue; + + // all units that were rezzing shall assist the repair too + resurrecter->lastResurrected = resurrectee->id; + + // prevent FinishCommand from removing this command when the + // feature is deleted, since it is needed to start the repair + // (WTF!) + c.params[0] = INT_MAX / 2; } - else if (curCapture != nullptr && f3SqDist(curCapture->pos, pos) < Square(buildDistance + curCapture->radius) && inBuildStance) { - if (fCommand.GetID() == CMD_WAIT) { - StopBuild(); - } else { - if (curCapture->team != team) { - const float captureMagicNumber = (150.0f + (curCapture->buildTime / captureSpeed) * (curCapture->health + curCapture->maxHealth) / curCapture->maxHealth * 0.4f); - const float captureProgressStep = 1.0f / captureMagicNumber; - const float captureProgressTemp = std::min(curCapture->captureProgress + captureProgressStep, 1.0f); - - const float captureFraction = captureProgressTemp - curCapture->captureProgress; - const float energyUseScaled = curCapture->cost.energy * captureFraction * modInfo.captureEnergyCostFactor; - - const bool captureAllowed = (eventHandler.AllowUnitBuildStep(this, curCapture, captureProgressStep)); - const bool canExecCapture = (captureAllowed && UseEnergy(energyUseScaled)); - - if (canExecCapture) { - curCapture->captureProgress += captureProgressStep; - curCapture->captureProgress = std::min(curCapture->captureProgress, 1.0f); - - CreateNanoParticle(curCapture->midPos, curCapture->radius * 0.7f, false, true); - - if (curCapture->captureProgress >= 1.0f) { - if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) { - // capture failed - if (team == gu->myTeam) { - LOG_L(L_WARNING, "%s: Capture failed, unit type limit reached", unitDef->humanName.c_str()); - eventHandler.LastMessagePosition(pos); - } - } - - curCapture->captureProgress = 0.0f; - StopBuild(true); - } - } - } else { - StopBuild(true); - } - } + + // this takes one simframe to do the deletion + featureHandler->DeleteFeature(curResurrectee); + } + + StopBuild(true); + return true; +} + +bool CBuilder::UpdateCapture(const Command& fCommand) +{ + CUnit* curCapturee = curCapture; + + if (curCapture == nullptr || f3SqDist(curCapture->pos, pos) >= Square(buildDistance + curCapture->radius) || !inBuildStance) + return false; + + if (fCommand.GetID() == CMD_WAIT) { + StopBuild(); + return true; + } + + if (curCapturee->team == team) { + StopBuild(true); + return true; + } + + const float captureMagicNumber = (150.0f + (curCapturee->buildTime / captureSpeed) * (curCapturee->health + curCapturee->maxHealth) / curCapturee->maxHealth * 0.4f); + const float captureProgressStep = 1.0f / captureMagicNumber; + const float captureProgressTemp = std::min(curCapturee->captureProgress + captureProgressStep, 1.0f); + + const float captureFraction = captureProgressTemp - curCapturee->captureProgress; + const float energyUseScaled = curCapturee->cost.energy * captureFraction * modInfo.captureEnergyCostFactor; + + const bool captureAllowed = (eventHandler.AllowUnitBuildStep(this, curCapturee, captureProgressStep)); + const bool canExecCapture = (captureAllowed && UseEnergy(energyUseScaled)); + + if (!canExecCapture) + return true; + + curCapturee->captureProgress += captureProgressStep; + curCapturee->captureProgress = std::min(curCapturee->captureProgress, 1.0f); + + CreateNanoParticle(curCapturee->midPos, curCapturee->radius * 0.7f, false, true); + + if (curCapturee->captureProgress < 1.0f) + return true; + + if (!curCapturee->ChangeTeam(team, CUnit::ChangeCaptured)) { + // capture failed + if (team == gu->myTeam) { + LOG_L(L_WARNING, "%s: Capture failed, unit type limit reached", unitDef->humanName.c_str()); + eventHandler.LastMessagePosition(pos); } } + curCapturee->captureProgress = 0.0f; + StopBuild(true); + return true; +} + + + +void CBuilder::Update() +{ + const CBuilderCAI* cai = static_cast(commandAI); + + const CCommandQueue& cQueue = cai->commandQue; + const Command& fCommand = (!cQueue.empty())? cQueue.front(): Command(CMD_STOP); + + bool updated = false; + + nanoPieceCache.Update(); + + if (!beingBuilt && !IsStunned()) { + updated = updated || UpdateTerraform(fCommand); + updated = updated || AssistTerraform(fCommand); + updated = updated || UpdateBuild(fCommand); + updated = updated || UpdateReclaim(fCommand); + updated = updated || UpdateResurrect(fCommand); + updated = updated || UpdateCapture(fCommand); + } + CUnit::Update(); } @@ -472,7 +547,7 @@ void CBuilder::Update() void CBuilder::SlowUpdate() { if (terraforming) - mapDamage->RecalcArea(tx1,tx2,tz1,tz2); + mapDamage->RecalcArea(tx1, tx2, tz1, tz2); CUnit::SlowUpdate(); } @@ -742,7 +817,7 @@ float CBuilder::CalculateBuildTerraformCost(BuildInfo& buildInfo) } -void CBuilder::DependentDied(CObject *o) +void CBuilder::DependentDied(CObject* o) { if (o == curBuild) { curBuild = nullptr; diff --git a/rts/Sim/Units/UnitTypes/Builder.h b/rts/Sim/Units/UnitTypes/Builder.h index e76e1250ade..797ce85b669 100644 --- a/rts/Sim/Units/UnitTypes/Builder.h +++ b/rts/Sim/Units/UnitTypes/Builder.h @@ -11,6 +11,7 @@ struct UnitDef; struct BuildInfo; +struct Command; class CFeature; class CSolidObject; @@ -42,6 +43,13 @@ class CBuilder : public CUnit void SlowUpdate(); void DependentDied(CObject* o); + bool UpdateTerraform(const Command& fCommand); + bool AssistTerraform(const Command& fCommand); + bool UpdateBuild(const Command& fCommand); + bool UpdateReclaim(const Command& fCommand); + bool UpdateResurrect(const Command& fCommand); + bool UpdateCapture(const Command& fCommand); + bool StartBuild(BuildInfo& buildInfo, CFeature*& feature, bool& waitStance); float CalculateBuildTerraformCost(BuildInfo& buildInfo); void StopBuild(bool callScript = true);