void CFactory::StartBuild(const UnitDef* buildeeDef) { const float3& buildPos = CalcBuildPos(); const bool blocked = groundBlockingObjectMap->GroundBlocked(buildPos, this); // wait until buildPos is no longer blocked (eg. by a previous buildee) // // it might rarely be the case that a unit got stuck inside the factory // or died right after completion and left some wreckage, but that is up // to players to fix (we no longer broadcast BuggerOff directives, since // those are indiscriminate and ineffective) if (blocked) return; CUnit* b = unitLoader->LoadUnit(buildeeDef, buildPos, team, true, buildFacing, this); if (!unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this, DEPENDENCE_BUILDER); } AddDeathDependence(b, DEPENDENCE_BUILD); script->StartBuilding(); // set curBuildDef to NULL to indicate construction // has started, otherwise we would keep being called curBuild = b; curBuildDef = NULL; #if (PLAY_SOUNDS == 1) if (losStatus[gu->myAllyTeam] & LOS_INLOS) { Channels::General.PlayRandomSample(unitDef->sounds.build, buildPos); } #endif }
bool CBuilder::StartBuild(BuildInfo& buildInfo) { StopBuild(false); // logOutput.Print("start build"); buildInfo.pos=helper->Pos2BuildPos(buildInfo); CFeature* feature; // Pass -1 as allyteam to behave like we have maphack. // This is needed to prevent building on top of cloaked stuff. int canBuild=uh->TestUnitBuildSquare(buildInfo, feature, -1); if(canBuild<2){ CUnit* u=helper->GetClosestFriendlyUnit(buildInfo.pos,5,allyteam); if(u && u->unitDef==buildInfo.def && unitDef->canAssist){ curBuild=u; AddDeathDependence(u); SetBuildStanceToward(buildInfo.pos); return true; } return false; } if(feature) return false; const UnitDef* unitDef = buildInfo.def; SetBuildStanceToward(buildInfo.pos); nextBuildType=buildInfo.def->name; nextBuildPos=buildInfo.pos; CUnit* b = unitLoader.LoadUnit(nextBuildType, nextBuildPos, team, true, buildInfo.buildFacing, this); if (mapDamage->disabled || !unitDef->levelGround || unitDef->floater || (unitDef->canmove && (unitDef->speed > 0.0f))) { // skip the terraforming job. b->terraformLeft = 0; b->groundLevelled=true; } else { tx1 = (int)max((float)0,(b->pos.x - (b->unitDef->xsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE); tx2 = min(gs->mapx,tx1+b->unitDef->xsize); tz1 = (int)max((float)0,(b->pos.z - (b->unitDef->ysize*0.5f*SQUARE_SIZE))/SQUARE_SIZE); tz2 = min(gs->mapy,tz1+b->unitDef->ysize); b->terraformLeft = CalculateBuildTerraformCost(buildInfo); b->groundLevelled=false; terraforming=true; terraformType=Terraform_Building; terraformRadius=(tx2-tx1)*SQUARE_SIZE; terraformCenter = b->pos; } if (!this->unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } b->lineage = this->lineage; AddDeathDependence(b); curBuild=b; if (mapDamage->disabled && !(curBuild->floatOnWater)) { /* The ground isn't going to be terraformed. * When the building is completed, it'll 'pop' * into the correct height for the (un-flattened) * terrain it's on. * * To prevent this visual artifact, put the building * at the 'right' height to begin with. * * Duplicated from CMoveType::SlowUpdate(), which * is why we use the regular code for floating things. */ curBuild->pos.y=ground->GetHeight2(curBuild->pos.x,curBuild->pos.z); curBuild->midPos.y=curBuild->pos.y+curBuild->relMidPos.y; } else { float d=nextBuildPos.y-curBuild->pos.y; curBuild->pos.y+=d; curBuild->midPos.y+=d; } return true; }
void CBuilder::Update() { if (beingBuilt) { return; } if (!stunned) { if (terraforming && inBuildStance) { const float* heightmap = readmap->GetHeightmap(); assert(!mapDamage->disabled); // The map should not be deformed in the first place. float terraformScale = 0.1; switch (terraformType) { case Terraform_Building: if (curBuild) { terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft; curBuild->terraformLeft -= (terraformSpeed + terraformHelp); terraformHelp = 0; if (terraformScale > 1.0f) { terraformScale = 1.0f; } // prevent building from timing out while terraforming for it curBuild->AddBuildPower(0.0f, this); for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * (gs->mapx + 1) + x; float ch = heightmap[idx]; readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale); } } if(curBuild->terraformLeft<=0){ terraforming=false; mapDamage->RecalcArea(tx1,tx2,tz1,tz2); curBuild->groundLevelled = true; if (luaRules && luaRules->TerraformComplete(this, curBuild)) { StopBuild(); } } } break; case Terraform_Restore: terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft; myTerraformLeft -= (terraformSpeed + terraformHelp); terraformHelp = 0; if (terraformScale > 1.0f) { terraformScale = 1.0f; } for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * (gs->mapx + 1) + x; float ch = heightmap[idx]; float oh = readmap->orgheightmap[idx]; readmap->AddHeight(idx, (oh - ch) * terraformScale); } } if (myTerraformLeft <= 0) { terraforming=false; mapDamage->RecalcArea(tx1,tx2,tz1,tz2); StopBuild(); } break; } CreateNanoParticle(terraformCenter,terraformRadius*0.5f,false); for (int z = tz1; z <= tz2; z++) { // smooth the borders x for (int x = 1; x <= 3; x++) { if (tx1 - 3 >= 0) { float ch3 = heightmap[z * (gs->mapx + 1) + tx1 ]; float ch = heightmap[z * (gs->mapx + 1) + tx1 - x]; float ch2 = heightmap[z * (gs->mapx + 1) + tx1 - 3]; float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * (gs->mapx + 1) + tx1 - x, amount); } if (tx2 + 3 < gs->mapx) { float ch3 = heightmap[z * (gs->mapx + 1) + tx2 ]; float ch = heightmap[z * (gs->mapx + 1) + tx2 + x]; float ch2 = heightmap[z * (gs->mapx + 1) + tx2 + 3]; float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * (gs->mapx + 1) + tx2 + x, amount); } } } for (int z = 1; z <= 3; z++) { // smooth the borders z for (int x = tx1; x <= tx2; x++) { if (tz1 - 3 >= 0) { float ch3 = heightmap[(tz1 ) * (gs->mapx + 1) + x]; float ch = heightmap[(tz1 - z) * (gs->mapx + 1) + x]; float ch2 = heightmap[(tz1 - 3) * (gs->mapx + 1) + x]; float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz1 - z) * (gs->mapx + 1) + x, adjust); } if (tz2 + 3 < gs->mapy) { float ch3 = heightmap[(tz2 ) * (gs->mapx + 1) + x]; float ch = heightmap[(tz2 + z) * (gs->mapx + 1) + x]; float ch2 = heightmap[(tz2 + 3) * (gs->mapx + 1) + x]; float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz2 + z) * (gs->mapx + 1) + x, adjust); } } } } else if (helpTerraform && inBuildStance) { if (helpTerraform->terraforming) { helpTerraform->terraformHelp += terraformSpeed; CreateNanoParticle(helpTerraform->terraformCenter,helpTerraform->terraformRadius*0.5f,false); } else { DeleteDeathDependence(helpTerraform); helpTerraform=0; StopBuild(true); } } else if (curBuild && f3Dist(curBuild->pos, pos) < buildDistance + curBuild->radius) { if (curBuild->soloBuilder && (curBuild->soloBuilder != this)) { StopBuild(); } else { if (!inBuildStance) { curBuild->AddBuildPower(0.0f, this); //prevent building timing out } else { if (scriptCloak <= 2) { if (isCloaked) { isCloaked = false; eventHandler.UnitDecloaked(this); } curCloakTimeout = gs->frameNum + cloakTimeout; } float adjBuildSpeed; // adjusted build speed if (curBuild->buildProgress < 1.0f) { adjBuildSpeed = buildSpeed; // new build } else { adjBuildSpeed = min(unitDef->maxRepairSpeed / 2.0f - curBuild->repairAmount, repairSpeed); // repair } if(adjBuildSpeed > 0 && !commandAI->commandQue.empty() && commandAI->commandQue.front().id == CMD_WAIT) { curBuild->AddBuildPower(0, this); } else if (adjBuildSpeed > 0 && curBuild->AddBuildPower(adjBuildSpeed, this)) { CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false); } else { if(!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) { StopBuild(); } } } } } else if(curReclaim && f3Dist(curReclaim->pos, pos)<buildDistance+curReclaim->radius && inBuildStance){ if (scriptCloak <= 2) { if (isCloaked) { isCloaked = false; eventHandler.UnitDecloaked(this); } curCloakTimeout = gs->frameNum + cloakTimeout; } if (curReclaim->AddBuildPower(-reclaimSpeed, this)) { CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true); } } else if(curResurrect && f3Dist(curResurrect->pos, pos)<buildDistance+curResurrect->radius && inBuildStance){ const UnitDef* ud=unitDefHandler->GetUnitByName(curResurrect->createdFromUnit); if(ud){ 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(repairSpeed, this); } else { // Corpse has been restored, begin resurrection if (UseEnergy(ud->energyCost * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) { curResurrect->resurrectProgress+=resurrectSpeed/ud->buildTime; CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7f,gs->randInt()&1); } if(curResurrect->resurrectProgress>1){ //resurrect finished curResurrect->UnBlock(); CUnit* u = unitLoader.LoadUnit(curResurrect->createdFromUnit, curResurrect->pos, team, false, curResurrect->buildFacing, this); if (!this->unitDef->canBeAssisted) { u->soloBuilder = this; u->AddDeathDependence(this); } u->health*=0.05f; u->lineage = this->lineage; lastResurrected=u->id; curResurrect->resurrectProgress=0; featureHandler->DeleteFeature(curResurrect); StopBuild(true); } } } else { StopBuild(true); } } else if(curCapture && f3Dist(curCapture->pos, pos)<buildDistance+curCapture->radius && inBuildStance){ if(curCapture->team!=team){ float captureProgressTemp = curCapture->captureProgress + 1.0f/(150+curCapture->buildTime/captureSpeed*(curCapture->health+curCapture->maxHealth)/curCapture->maxHealth*0.4f); if (captureProgressTemp >= 1.0f) { captureProgressTemp = 1.0f; } const float captureFraction = captureProgressTemp - curCapture->captureProgress; const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor; if (!UseEnergy(energyUseScaled)) { gs->Team(team)->energyPull += energyUseScaled; } else { curCapture->captureProgress = captureProgressTemp; CreateNanoParticle(curCapture->midPos,curCapture->radius*0.7f,false); if(curCapture->captureProgress >= 1.0f){ if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) { // capture failed ENTER_MIXED; if (team == gu->myTeam) { logOutput.Print("%s: Capture failed, unit type limit reached", unitDef->humanName.c_str()); logOutput.SetLastMsgPos(pos); } ENTER_SYNCED; } else { // capture succesful int oldLineage = curCapture->lineage; curCapture->lineage = this->lineage; gs->Team(oldLineage)->LeftLineage(curCapture); } curCapture->captureProgress=0.0f; StopBuild(true); } } } else { StopBuild(true); } } } CUnit::Update(); }
bool CBuilder::StartBuild(BuildInfo& buildInfo, CFeature*& feature, bool& waitstance) { StopBuild(false); buildInfo.pos = helper->Pos2BuildPos(buildInfo, true); // Pass -1 as allyteam to behave like we have maphack. // This is needed to prevent building on top of cloaked stuff. const int canBuild = uh->TestUnitBuildSquare(buildInfo, feature, -1, true); if (canBuild < 2) { // the ground is blocked at the position we want // to build at; check if the blocking object is // of the same type as our buildee (which means // another builder has already started it) // note: even if construction has already started, // the buildee is *not* guaranteed to be the unit // closest to us CSolidObject* o = groundBlockingObjectMap->GroundBlocked(buildInfo.pos); CUnit* u = NULL; if (o != NULL) { u = dynamic_cast<CUnit*>(o); } else { // <pos> might map to a non-blocking portion // of the buildee's yardmap, fallback check u = helper->GetClosestFriendlyUnit(buildInfo.pos, buildDistance, allyteam); } if (u != NULL && u->unitDef == buildInfo.def && unitDef->canAssist) { curBuild = u; AddDeathDependence(u); SetBuildStanceToward(buildInfo.pos); return true; } return false; } if (feature) return false; const UnitDef* unitDef = buildInfo.def; SetBuildStanceToward(buildInfo.pos); if (!inBuildStance) { waitstance = true; return false; } CUnit* b = unitLoader->LoadUnit(buildInfo.def, buildInfo.pos, team, true, buildInfo.buildFacing, this); // floating structures don't terraform the seabed const float groundheight = ground->GetHeightReal(b->pos.x, b->pos.z); const bool onWater = (unitDef->floater && groundheight <= 0.0f); if (mapDamage->disabled || !unitDef->levelGround || onWater || (unitDef->canmove && (unitDef->speed > 0.0f))) { // skip the terraforming job b->terraformLeft = 0; b->groundLevelled=true; } else { tx1 = (int)max((float)0,(b->pos.x - (b->unitDef->xsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE); tx2 = min(gs->mapx,tx1+b->unitDef->xsize); tz1 = (int)max((float)0,(b->pos.z - (b->unitDef->zsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE); tz2 = min(gs->mapy,tz1+b->unitDef->zsize); b->terraformLeft = CalculateBuildTerraformCost(buildInfo); b->groundLevelled= false; terraforming = true; terraformType = Terraform_Building; terraformRadius = (tx2-tx1)*SQUARE_SIZE; terraformCenter = b->pos; } if (!this->unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } AddDeathDependence(b); curBuild=b; /* The ground isn't going to be terraformed. * When the building is completed, it'll 'pop' * into the correct height for the (un-flattened) * terrain it's on. * * To prevent this visual artifact, put the building * at the 'right' height to begin with. */ curBuild->moveType->SlowUpdate(); return true; }
void CBuilder::Update() { if (!beingBuilt && !stunned) { if (terraforming && inBuildStance) { const float* heightmap = readmap->GetCornerHeightMapSynced(); assert(!mapDamage->disabled); // The map should not be deformed in the first place. float terraformScale = 0.1; switch (terraformType) { case Terraform_Building: if (curBuild) { if (curBuild->terraformLeft <= 0) terraformScale = 0.0f; else terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft; curBuild->terraformLeft -= (terraformSpeed + terraformHelp); terraformHelp = 0; if (terraformScale > 1.0f) { terraformScale = 1.0f; } // prevent building from timing out while terraforming for it curBuild->AddBuildPower(0.0f, this); for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * gs->mapxp1 + x; float ch = heightmap[idx]; readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale); } } if(curBuild->terraformLeft<=0){ terraforming=false; mapDamage->RecalcArea(tx1,tx2,tz1,tz2); curBuild->groundLevelled = true; if (luaRules && luaRules->TerraformComplete(this, curBuild)) { StopBuild(); } } } break; case Terraform_Restore: if (myTerraformLeft <= 0) terraformScale = 0.0f; else terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft; myTerraformLeft -= (terraformSpeed + terraformHelp); terraformHelp = 0; if (terraformScale > 1.0f) { terraformScale = 1.0f; } for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * gs->mapxp1 + x; float ch = heightmap[idx]; float oh = readmap->GetOriginalHeightMapSynced()[idx]; readmap->AddHeight(idx, (oh - ch) * terraformScale); } } if (myTerraformLeft <= 0) { terraforming=false; mapDamage->RecalcArea(tx1,tx2,tz1,tz2); StopBuild(); } break; } ScriptDecloak(true); CreateNanoParticle(terraformCenter,terraformRadius*0.5f,false); 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 * gs->mapxp1 + tx1 ]; const float ch = heightmap[z * gs->mapxp1 + tx1 - x]; const float ch2 = heightmap[z * gs->mapxp1 + tx1 - 3]; const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * gs->mapxp1 + tx1 - x, amount); } if (tx2 + 3 < gs->mapx) { const float ch3 = heightmap[z * gs->mapxp1 + tx2 ]; const float ch = heightmap[z * gs->mapxp1 + tx2 + x]; const float ch2 = heightmap[z * gs->mapxp1 + tx2 + 3]; const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * gs->mapxp1 + tx2 + x, amount); } } } 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 ) * gs->mapxp1 + x]; const float ch = heightmap[(tz1 - z) * gs->mapxp1 + x]; const float ch2 = heightmap[(tz1 - 3) * gs->mapxp1 + x]; const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz1 - z) * gs->mapxp1 + x, adjust); } if (tz2 + 3 < gs->mapy) { const float ch3 = heightmap[(tz2 ) * gs->mapxp1 + x]; const float ch = heightmap[(tz2 + z) * gs->mapxp1 + x]; const float ch2 = heightmap[(tz2 + 3) * gs->mapxp1 + x]; const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz2 + z) * gs->mapxp1 + x, adjust); } } } } else if (helpTerraform && inBuildStance) { if (helpTerraform->terraforming) { ScriptDecloak(true); helpTerraform->terraformHelp += terraformSpeed; CreateNanoParticle(helpTerraform->terraformCenter,helpTerraform->terraformRadius*0.5f,false); } else { DeleteDeathDependence(helpTerraform, DEPENDENCE_TERRAFORM); helpTerraform=0; StopBuild(true); } } else if (curBuild && f3SqDist(curBuild->pos, pos) < Square(buildDistance + curBuild->radius)) { if (curBuild->soloBuilder && (curBuild->soloBuilder != this)) { StopBuild(); } else { if (!inBuildStance) { curBuild->AddBuildPower(0.0f, this); //prevent building timing out } else { ScriptDecloak(true); float adjBuildSpeed; // adjusted build speed if (curBuild->buildProgress < 1.0f) { adjBuildSpeed = buildSpeed; // new build } else { adjBuildSpeed = min(unitDef->maxRepairSpeed / 2.0f - curBuild->repairAmount, repairSpeed); // repair } if(adjBuildSpeed > 0 && !commandAI->commandQue.empty() && commandAI->commandQue.front().GetID() == CMD_WAIT) { curBuild->AddBuildPower(0, this); } else if (adjBuildSpeed > 0 && curBuild->AddBuildPower(adjBuildSpeed, this)) { CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false); } else { if(!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) { StopBuild(); } } } } } else if(curReclaim && f3SqDist(curReclaim->pos, pos)<Square(buildDistance+curReclaim->radius) && inBuildStance){ ScriptDecloak(true); if (curReclaim->AddBuildPower(-reclaimSpeed, this)) { CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true); } } else if(curResurrect && f3SqDist(curResurrect->pos, pos)<Square(buildDistance+curResurrect->radius) && inBuildStance){ const UnitDef* ud = curResurrect->udef; if (ud) { 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(repairSpeed, this); } else { // Corpse has been restored, begin resurrection if (UseEnergy(ud->energyCost * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) { curResurrect->resurrectProgress+=resurrectSpeed/ud->buildTime; CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7f,gs->randInt()&1); } if (curResurrect->resurrectProgress > 1) { // resurrect finished curResurrect->UnBlock(); CUnit* u = unitLoader->LoadUnit(ud, curResurrect->pos, team, false, curResurrect->buildFacing, this); if (!this->unitDef->canBeAssisted) { u->soloBuilder = this; u->AddDeathDependence(this); } u->health *= 0.05f; CBuilderCAI *cai = (CBuilderCAI *)commandAI; for (CUnitSet::iterator it = cai->resurrecters.begin(); it != cai->resurrecters.end(); ++it) { CBuilder *bld = (CBuilder *)*it; if (bld->commandAI->commandQue.empty()) continue; const Command& c = bld->commandAI->commandQue.front(); if (c.GetID() != CMD_RESURRECT || c.params.size() != 1) continue; const int cmdFeatureId = (int)c.params[0]; if (cmdFeatureId - uh->MaxUnits() == curResurrect->id && teamHandler->Ally(allyteam, bld->allyteam)) bld->lastResurrected = u->id; // all units that were rezzing shall assist the repair too } curResurrect->resurrectProgress=0; featureHandler->DeleteFeature(curResurrect); StopBuild(true); } } } else { StopBuild(true); } } else if(curCapture && f3SqDist(curCapture->pos, pos)<Square(buildDistance+curCapture->radius) && inBuildStance){ if(curCapture->team!=team){ float captureProgressTemp = curCapture->captureProgress + 1.0f/(150+curCapture->buildTime/captureSpeed*(curCapture->health+curCapture->maxHealth)/curCapture->maxHealth*0.4f); if (captureProgressTemp >= 1.0f) { captureProgressTemp = 1.0f; } const float captureFraction = captureProgressTemp - curCapture->captureProgress; const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor; if (!UseEnergy(energyUseScaled)) { teamHandler->Team(team)->energyPull += energyUseScaled; } else { curCapture->captureProgress = captureProgressTemp; CreateNanoParticle(curCapture->midPos,curCapture->radius*0.7f,false); if(curCapture->captureProgress >= 1.0f){ if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) { // capture failed if (team == gu->myTeam) { logOutput.Print("%s: Capture failed, unit type limit reached", unitDef->humanName.c_str()); logOutput.SetLastMsgPos(pos); } } curCapture->captureProgress=0.0f; StopBuild(true); } } } else { StopBuild(true); } } } CUnit::Update(); }
void CFactory::Update() { if (beingBuilt) { // factory under construction CUnit::Update(); return; } if (quedBuild && !opening && !stunned) { script->Activate(); groundBlockingObjectMap->OpenBlockingYard(this, curYardMap); opening = true; } if (quedBuild && inBuildStance && !stunned) { // start building a unit const float3 buildPos = CalcBuildPos(); const CSolidObject* solidObj = groundBlockingObjectMap->GroundBlocked(buildPos); if (solidObj == NULL || (dynamic_cast<const CUnit*>(solidObj) == this)) { quedBuild = false; CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team, true, buildFacing, this); if (!unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } AddDeathDependence(b); curBuild = b; script->StartBuilding(); int soundIdx = unitDef->sounds.build.getRandomIdx(); if (soundIdx >= 0) { Channels::UnitReply.PlaySample( unitDef->sounds.build.getID(soundIdx), pos, unitDef->sounds.build.getVolume(0)); } } else { helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8, true, true, NULL); } } if (curBuild && !beingBuilt) { if (!stunned) { // factory not under construction and // nanolathing unit: continue building lastBuild = gs->frameNum; // buildPiece is the rotating platform const int buildPiece = GetBuildPiece(); const CMatrix44f& mat = script->GetPieceMatrix(buildPiece); const int h = GetHeadingFromVector(mat[2], mat[10]); //! x.z, z.z // rotate unit nanoframe with platform curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535; const float3 buildPos = CalcBuildPos(buildPiece); curBuild->pos = buildPos; if (curBuild->floatOnWater) { float waterline = ground->GetHeight(buildPos.x, buildPos.z) - curBuild->unitDef->waterline; if (waterline > curBuild->pos.y) curBuild->pos.y = waterline; } curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y); const CCommandQueue& queue = commandAI->commandQue; if(!queue.empty() && (queue.front().id == CMD_WAIT)) { curBuild->AddBuildPower(0, this); } else { if (curBuild->AddBuildPower(buildSpeed, this)) { CreateNanoParticle(); } } } if (!curBuild->beingBuilt && (!unitDef->fullHealthFactory || (curBuild->health >= curBuild->maxHealth))) { if (group && curBuild->group == 0) { curBuild->SetGroup(group); } bool userOrders = true; if (curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) { userOrders = false; AssignBuildeeOrders(curBuild); waitCommandsAI.AddLocalUnit(curBuild, this); } eventHandler.UnitFromFactory(curBuild, this, userOrders); StopBuild(); } } if (((lastBuild + 200) < gs->frameNum) && !stunned && !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) { // close the factory after inactivity groundBlockingObjectMap->CloseBlockingYard(this, curYardMap); opening = false; script->Deactivate(); } CBuilding::Update(); }
bool CBuilder::StartBuild(BuildInfo& buildInfo, CFeature*& feature, bool& waitStance) { StopBuild(false); buildInfo.pos = CGameHelper::Pos2BuildPos(buildInfo, true); // Pass -1 as allyteam to behave like we have maphack. // This is needed to prevent building on top of cloaked stuff. const CGameHelper::BuildSquareStatus tbs = CGameHelper::TestUnitBuildSquare(buildInfo, feature, -1, true); switch (tbs) { case CGameHelper::BUILDSQUARE_OPEN: break; case CGameHelper::BUILDSQUARE_BLOCKED: case CGameHelper::BUILDSQUARE_OCCUPIED: { // the ground is blocked at the position we want // to build at; check if the blocking object is // of the same type as our buildee (which means // another builder has already started it) // note: even if construction has already started, // the buildee is *not* guaranteed to be the unit // closest to us CSolidObject* o = groundBlockingObjectMap->GroundBlocked(buildInfo.pos); CUnit* u = NULL; if (o != NULL) { u = dynamic_cast<CUnit*>(o); } else { // <pos> might map to a non-blocking portion // of the buildee's yardmap, fallback check u = CGameHelper::GetClosestFriendlyUnit(NULL, buildInfo.pos, buildDistance, allyteam); } if (u != NULL && CanAssistUnit(u, buildInfo.def)) { curBuild = u; AddDeathDependence(u, DEPENDENCE_BUILD); ScriptStartBuilding(u->pos, false); return true; } return false; } case CGameHelper::BUILDSQUARE_RECLAIMABLE: // caller should handle this return false; } if ((waitStance = !ScriptStartBuilding(buildInfo.pos, true))) { return false; } const UnitDef* buildeeDef = buildInfo.def; const UnitLoadParams buildeeParams = {buildeeDef, this, buildInfo.pos, ZeroVector, -1, team, buildInfo.buildFacing, true, false}; CUnit* buildee = unitLoader->LoadUnit(buildeeParams); // floating structures don't terraform the seabed const float groundheight = ground->GetHeightReal(buildee->pos.x, buildee->pos.z); const bool onWater = (buildeeDef->floatOnWater && groundheight <= 0.0f); if (mapDamage->disabled || !buildeeDef->levelGround || onWater || (buildeeDef->canmove && (buildeeDef->speed > 0.0f))) { // skip the terraforming job buildee->terraformLeft = 0; buildee->groundLevelled = true; } else { tx1 = (int)max( 0.0f, (buildee->pos.x - (buildeeDef->xsize * 0.5f * SQUARE_SIZE)) / SQUARE_SIZE); tx2 = min(gs->mapx, tx1 + buildeeDef->xsize ); tz1 = (int)max( 0.0f, (buildee->pos.z - (buildeeDef->zsize * 0.5f * SQUARE_SIZE)) / SQUARE_SIZE); tz2 = min(gs->mapy, tz1 + buildeeDef->zsize ); buildee->terraformLeft = CalculateBuildTerraformCost(buildInfo); buildee->groundLevelled = false; terraforming = true; terraformType = Terraform_Building; terraformRadius = (tx2 - tx1) * SQUARE_SIZE; terraformCenter = buildee->pos; } if (!this->unitDef->canBeAssisted) { buildee->soloBuilder = this; buildee->AddDeathDependence(this, DEPENDENCE_BUILDER); } AddDeathDependence(buildee, DEPENDENCE_BUILD); curBuild = buildee; /* The ground isn't going to be terraformed. * When the building is completed, it'll 'pop' * into the correct height for the (un-flattened) * terrain it's on. * * To prevent this visual artifact, put the building * at the 'right' height to begin with. */ curBuild->moveType->SlowUpdate(); return true; }
void CBuilder::Update() { CBuilderCAI* cai = static_cast<CBuilderCAI*>(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.1; switch (terraformType) { case Terraform_Building: if (curBuild != NULL) { if (curBuild->terraformLeft <= 0) terraformScale = 0.0f; else terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft; curBuild->terraformLeft -= (terraformSpeed + terraformHelp); terraformHelp = 0; terraformScale = std::min(terraformScale, 1.0f); // prevent building from timing out while terraforming for it curBuild->AddBuildPower(0.0f, this); for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * gs->mapxp1 + x; float ch = heightmap[idx]; readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale); } } if (curBuild->terraformLeft <= 0.0f) { terraforming = false; mapDamage->RecalcArea(tx1, tx2, tz1, tz2); curBuild->groundLevelled = true; if (luaRules && luaRules->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; terraformScale = std::min(terraformScale, 1.0f); for (int z = tz1; z <= tz2; z++) { for (int x = tx1; x <= tx2; x++) { int idx = z * gs->mapxp1 + x; float ch = heightmap[idx]; float oh = readmap->GetOriginalHeightMapSynced()[idx]; readmap->AddHeight(idx, (oh - ch) * terraformScale); } } if (myTerraformLeft <= 0.0f) { terraforming = false; mapDamage->RecalcArea(tx1, tx2, tz1, tz2); StopBuild(); } break; } ScriptDecloak(true); CreateNanoParticle(terraformCenter, terraformRadius * 0.5f, false); 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 * gs->mapxp1 + tx1 ]; const float ch = heightmap[z * gs->mapxp1 + tx1 - x]; const float ch2 = heightmap[z * gs->mapxp1 + tx1 - 3]; const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * gs->mapxp1 + tx1 - x, amount); } if (tx2 + 3 < gs->mapx) { const float ch3 = heightmap[z * gs->mapxp1 + tx2 ]; const float ch = heightmap[z * gs->mapxp1 + tx2 + x]; const float ch2 = heightmap[z * gs->mapxp1 + tx2 + 3]; const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale; readmap->AddHeight(z * gs->mapxp1 + tx2 + x, amount); } } } 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 ) * gs->mapxp1 + x]; const float ch = heightmap[(tz1 - z) * gs->mapxp1 + x]; const float ch2 = heightmap[(tz1 - 3) * gs->mapxp1 + x]; const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz1 - z) * gs->mapxp1 + x, adjust); } if (tz2 + 3 < gs->mapy) { const float ch3 = heightmap[(tz2 ) * gs->mapxp1 + x]; const float ch = heightmap[(tz2 + z) * gs->mapxp1 + x]; const float ch2 = heightmap[(tz2 + 3) * gs->mapxp1 + x]; const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale; readmap->AddHeight((tz2 + z) * gs->mapxp1 + x, adjust); } } } } else if (helpTerraform != NULL && inBuildStance) { if (helpTerraform->terraforming) { ScriptDecloak(true); helpTerraform->terraformHelp += terraformSpeed; CreateNanoParticle(helpTerraform->terraformCenter, helpTerraform->terraformRadius * 0.5f, false); } else { DeleteDeathDependence(helpTerraform, DEPENDENCE_TERRAFORM); helpTerraform = NULL; StopBuild(true); } } else if (curBuild != NULL && cai->IsInBuildRange(curBuild)) { if (fCommand.GetID() == CMD_WAIT) { if (curBuild->buildProgress < 1.0f) { // prevent buildee from decaying (we cannot call StopBuild here) curBuild->AddBuildPower(0.0f, this); } 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 != NULL && (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(adjBuildSpeed, this)) { CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false); } else { // check if buildee finished construction if (!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) { StopBuild(); } } } } } } else if (curReclaim != NULL && f3SqDist(curReclaim->pos, pos)<Square(buildDistance+curReclaim->radius) && inBuildStance) { if (fCommand.GetID() == CMD_WAIT) { StopBuild(); } else { ScriptDecloak(true); if (curReclaim->AddBuildPower(-reclaimSpeed, this)) { CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true, (reclaimingUnit && curReclaim->team != team)); } } } else if (curResurrect != NULL && f3SqDist(curResurrect->pos, pos)<Square(buildDistance+curResurrect->radius) && inBuildStance) { const UnitDef* ud = curResurrect->udef; if (fCommand.GetID() == CMD_WAIT) { StopBuild(); } else { if (ud != NULL) { 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(repairSpeed, this); } else { // Corpse has been restored, begin resurrection if (UseEnergy(ud->energy * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) { curResurrect->resurrectProgress += (resurrectSpeed / ud->buildTime); CreateNanoParticle(curResurrect->midPos, curResurrect->radius * 0.7f, (gs->randInt() & 1)); } if (curResurrect->resurrectProgress > 1) { // resurrect finished curResurrect->UnBlock(); UnitLoadParams resurrecteeParams = {ud, this, curResurrect->pos, ZeroVector, -1, team, curResurrect->buildFacing, false, false}; CUnit* resurrectee = unitLoader->LoadUnit(resurrecteeParams); if (!this->unitDef->canBeAssisted) { resurrectee->soloBuilder = this; resurrectee->AddDeathDependence(this, DEPENDENCE_BUILDER); } // TODO: make configurable if this should happen resurrectee->health *= 0.05f; for (CUnitSet::iterator it = cai->resurrecters.begin(); it != cai->resurrecters.end(); ++it) { CBuilder* bld = (CBuilder*) *it; CCommandAI* bldCAI = bld->commandAI; if (bldCAI->commandQue.empty()) continue; Command& c = bldCAI->commandQue.front(); if (c.GetID() != CMD_RESURRECT || c.params.size() != 1) continue; const int cmdFeatureId = c.params[0]; if (cmdFeatureId - unitHandler->MaxUnits() == curResurrect->id && teamHandler->Ally(allyteam, bld->allyteam)) { bld->lastResurrected = resurrectee->id; // all units that were rezzing shall assist the repair too c.params[0] = INT_MAX / 2; // prevent FinishCommand from removing this command when the feature is deleted, since it is needed to start the repair } } curResurrect->resurrectProgress = 0; featureHandler->DeleteFeature(curResurrect); StopBuild(true); } } } else { StopBuild(true); } } } else if (curCapture != NULL && f3SqDist(curCapture->pos, pos)<Square(buildDistance+curCapture->radius) && inBuildStance) { if (fCommand.GetID() == CMD_WAIT) { StopBuild(); } else { if (curCapture->team != team) { const float captureMagicNumber = (150 + curCapture->buildTime / captureSpeed * (curCapture->health + curCapture->maxHealth) / curCapture->maxHealth * 0.4f); const float captureProgressTemp = std::min(curCapture->captureProgress + 1.0f / captureMagicNumber, 1.0f); const float captureFraction = captureProgressTemp - curCapture->captureProgress; const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor; if (!UseEnergy(energyUseScaled)) { teamHandler->Team(team)->energyPull += energyUseScaled; } else { curCapture->captureProgress = captureProgressTemp; 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); } } } } CUnit::Update(); }
void CFactory::Update() { if (beingBuilt) { // factory under construction CUnit::Update(); return; } if (quedBuild && !opening && !stunned) { cob->Call(COBFN_Activate); groundBlockingObjectMap->OpenBlockingYard(this, yardMap); opening = true; } if (quedBuild && inBuildStance && !stunned) { // start building a unit float3 buildPos = CalcBuildPos(); bool canBuild = true; std::vector<CUnit*> units = qf->GetUnitsExact(buildPos, 16); for (std::vector<CUnit*>::iterator ui = units.begin(); ui != units.end(); ++ui) { if ((*ui) != this) canBuild = false; } if (canBuild) { quedBuild = false; CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team, true, buildFacing, this); b->lineage = this->lineage; if (!unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } AddDeathDependence(b); curBuild = b; cob->Call("StartBuilding"); int soundIdx = unitDef->sounds.build.getRandomIdx(); if (soundIdx >= 0) { sound->PlaySample( unitDef->sounds.build.getID(soundIdx), pos, unitDef->sounds.build.getVolume(0)); } } else { helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8); } } if (curBuild && !beingBuilt) { if (!stunned) { // factory not under construction and // nanolathing unit: continue building lastBuild = gs->frameNum; // buildPiece is the rotating platform const int buildPiece = GetBuildPiece(); CMatrix44f mat = localmodel->GetPieceMatrix(buildPiece); const int h = GetHeadingFromVector(mat[2], mat[10]); // rotate unit nanoframe with platform curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535; const float3 buildPos = CalcBuildPos(buildPiece); curBuild->pos = buildPos; if (curBuild->floatOnWater) { curBuild->pos.y = ground->GetHeight(buildPos.x, buildPos.z); curBuild->pos.y -= curBuild->unitDef->waterline; } curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y); const CCommandQueue& queue = commandAI->commandQue; if(!queue.empty() && (queue.front().id == CMD_WAIT)) { curBuild->AddBuildPower(0, this); } else { if (curBuild->AddBuildPower(buildSpeed, this)) { CreateNanoParticle(); } } } if (!curBuild->beingBuilt && (!unitDef->fullHealthFactory || (curBuild->health >= curBuild->maxHealth))) { if (group && curBuild->group == 0) { curBuild->SetGroup(group); } bool userOrders = true; if (curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) { userOrders = false; const CFactoryCAI* facAI = (CFactoryCAI*) commandAI; const CCommandQueue& newUnitCmds = facAI->newUnitCommands; if (newUnitCmds.empty()) { SendToEmptySpot(curBuild); } else { // XXX the pathfinder sometimes... makes mistakes, try to hack around it // XXX note this qualifies as HACK HACK HACK float3 testpos = curBuild->pos + frontdir * (this->radius - 1.0f); Command c; c.id = CMD_MOVE; c.params.push_back(testpos.x); c.params.push_back(testpos.y); c.params.push_back(testpos.z); curBuild->commandAI->GiveCommand(c); for (CCommandQueue::const_iterator ci = newUnitCmds.begin(); ci != newUnitCmds.end(); ++ci) { c = *ci; c.options |= SHIFT_KEY; curBuild->commandAI->GiveCommand(c); } } waitCommandsAI.AddLocalUnit(curBuild, this); } eventHandler.UnitFromFactory(curBuild, this, userOrders); StopBuild(); } } if (((lastBuild + 200) < gs->frameNum) && !stunned && !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) { // close the factory after inactivity groundBlockingObjectMap->CloseBlockingYard(this, yardMap); opening = false; cob->Call(COBFN_Deactivate); } CBuilding::Update(); }