void CBuilderCAI::GiveCommandReal(const Command& c, bool fromSynced) { if (!AllowedCommand(c, fromSynced)) return; // don't guard yourself if ((c.GetID() == CMD_GUARD) && (c.params.size() == 1) && ((int)c.params[0] == owner->id)) { return; } // stop building/reclaiming/... if the new command is not queued, i.e. replaces our current activity // FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!) if ((c.GetID() != CMD_WAIT && c.GetID() != CMD_SET_WANTED_MAX_SPEED) && !(c.options & SHIFT_KEY)) { if (nonQueingCommands.find(c.GetID()) == nonQueingCommands.end()) { building = false; static_cast<CBuilder*>(owner)->StopBuild(); } } if (buildOptions.find(c.GetID()) != buildOptions.end()) { if (c.params.size() < 3) return; BuildInfo bi; bi.pos = c.GetPos(0); if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByID(-c.GetID()); bi.pos = CGameHelper::Pos2BuildPos(bi, true); // We are a static building, check if the buildcmd is in range if (!owner->unitDef->canmove) { if (!IsInBuildRange(bi.pos, GetBuildOptionRadius(bi.def, c.GetID()))) { return; } } const CUnit* nanoFrame = NULL; // check if the buildpos is blocked if (IsBuildPosBlocked(bi, &nanoFrame)) return; // if it is a nanoframe help to finish it if (nanoFrame != NULL) { Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER, nanoFrame->id); CMobileCAI::GiveCommandReal(c2, fromSynced); CMobileCAI::GiveCommandReal(c, fromSynced); return; } } else { if (c.GetID() < 0) return; } CMobileCAI::GiveCommandReal(c, fromSynced); }
void CBuilderCAI::ExecuteBuildCmd(Command& c) { CBuilder* builder = (CBuilder*) owner; map<int, string>::iterator boi = buildOptions.find(c.GetID()); if (boi == buildOptions.end()) return; if (!inCommand) { BuildInfo bi; bi.pos.x = floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.z = floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.y = c.params[1]; if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); CFeature* f = NULL; uh->TestUnitBuildSquare(bi, f, owner->allyteam, true); if (f != NULL) { if (!bi.def->isFeature || bi.def->wreckName != f->def->name) { ReclaimFeature(f); } else { FinishCommand(); } } else { inCommand = true; build.Parse(c); ExecuteBuildCmd(c); } return; } assert(build.def); const float radius = GetBuildOptionRadius(build.def, c.GetID()); if (building) { MoveInBuildRange(build.pos, radius); if (!builder->curBuild && !builder->terraforming) { building = false; StopMove(); // cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; builder->StopBuild(); CancelRestrictedUnit(boi->second); } } else { if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); return; } build.pos = helper->Pos2BuildPos(build, true); // we are on the way to the buildpos, meanwhile it can happen // that another builder already finished our buildcmd or blocked // the buildpos with another building (skip our buildcmd then) const bool checkBuildPos = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE; //FIXME add a per-unit solution to better balance the load? CFeature* feature = NULL; if (checkBuildPos && !uh->TestUnitBuildSquare(build, feature, owner->allyteam, true)) { if (!feature) { const int yardxpos = int(build.pos.x + 4) / SQUARE_SIZE; const int yardypos = int(build.pos.z + 4) / SQUARE_SIZE; const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); const CUnit* u = dynamic_cast<const CUnit*>(s); if (u != NULL) { const bool canAssist = u->beingBuilt && owner->unitDef->canAssist && (!u->soloBuilder || (u->soloBuilder == owner)); if ((u->unitDef != build.def) || !canAssist) { StopMove(); FinishCommand(); return; } } } } if (MoveInBuildRange(build.pos, radius, true)) { if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build)) { StopMove(); // cancel KeepPointingTo FinishCommand(); } else if (!teamHandler->Team(owner->team)->AtUnitLimit()) { // unit-limit not yet reached CFeature* f = NULL; bool waitstance = false; if (builder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) { building = true; } else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) { inCommand = false; ReclaimFeature(f); } else if (!waitstance) { const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize); const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE; // tell everything within the radius of the soon-to-be buildee // to get out of the way; using the model radius is not correct // because this can be shorter than half the footprint diagonal helper->BuggerOff(build.pos, std::max(radius, fpRadius), false, true, owner->team, NULL); NonMoving(); } } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); } } } } }
void CBuilderCAI::GiveCommandReal(const Command& c, bool fromSynced) { if (!AllowedCommand(c, fromSynced)) return; // don't guard yourself if ((c.GetID() == CMD_GUARD) && (c.params.size() == 1) && ((int)c.params[0] == owner->id)) { return; } // stop current build if the new command is a not queued and replaces the current buidcmd //FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!) if (!(c.options & SHIFT_KEY) && nonQueingCommands.find(c.GetID()) == nonQueingCommands.end() && c.GetID() != CMD_WAIT) { building = false; ((CBuilder*) owner)->StopBuild(); } map<int,string>::iterator boi = buildOptions.find(c.GetID()); if (boi != buildOptions.end()) { if (c.params.size() < 3) { return; } BuildInfo bi; bi.pos = c.GetPos(0); if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); bi.pos = helper->Pos2BuildPos(bi, true); // We are a static building, check if the buildcmd is in range if (!owner->unitDef->canmove) { const float radius = GetBuildOptionRadius(bi.def, c.GetID()); if (!IsInBuildRange(bi.pos, radius)) { return; } } // check if the buildpos is blocked if it is a nanoframe help to finish it //FIXME finish it just if it is of the same unitdef? CFeature* feature = NULL; if (!uh->TestUnitBuildSquare(bi, feature, owner->allyteam, true)) { if (!feature && owner->unitDef->canAssist) { const int yardxpos = int(bi.pos.x + 4) / SQUARE_SIZE; const int yardypos = int(bi.pos.z + 4) / SQUARE_SIZE; const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); const CUnit* u = dynamic_cast<const CUnit*>(s); if ( (u != NULL) && u->beingBuilt && (u->buildProgress == 0.0f) && (!u->soloBuilder || (u->soloBuilder == owner)) ) { Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER); c2.params.push_back(u->id); CMobileCAI::GiveCommandReal(c2); CMobileCAI::GiveCommandReal(c); } } return; } } CMobileCAI::GiveCommandReal(c); }
void CBuilderCAI::ExecuteBuildCmd(Command& c) { const map<int, string>::const_iterator boi = buildOptions.find(c.GetID()); if (boi == buildOptions.end()) return; if (!inCommand) { BuildInfo bi; bi.pos.x = math::floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.z = math::floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.y = c.params[1]; if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); CFeature* f = NULL; CGameHelper::TestUnitBuildSquare(bi, f, owner->allyteam, true); if (f != NULL) { if (!bi.def->isFeature || bi.def->wreckName != f->def->name) { ReclaimFeature(f); } else { FinishCommand(); } return; } inCommand = true; build.Parse(c); } assert(build.def->id == -c.GetID() && build.def->id != 0); const float buildeeRadius = GetBuildOptionRadius(build.def, c.GetID()); if (building) { // keep moving until 3D distance to buildPos is LEQ our buildDistance MoveInBuildRange(build.pos, 0.0f); if (!ownerBuilder->curBuild && !ownerBuilder->terraforming) { building = false; StopMove(); // cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (unitHandler->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; ownerBuilder->StopBuild(); CancelRestrictedUnit(boi->second); } } else { if (unitHandler->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); return; } build.pos = CGameHelper::Pos2BuildPos(build, true); // keep moving until until 3D distance to buildPos is LEQ our buildDistance if (MoveInBuildRange(build.pos, 0.0f, true)) { if (IsBuildPosBlocked(build)) { StopMove(); FinishCommand(); return; } if (!eventHandler.AllowUnitCreation(build.def, owner, &build)) { StopMove(); // cancel KeepPointingTo FinishCommand(); return; } if (teamHandler->Team(owner->team)->AtUnitLimit()) { return; } CFeature* f = NULL; bool waitstance = false; if (ownerBuilder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) { building = true; } else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) { inCommand = false; ReclaimFeature(f); } else if (!waitstance) { const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize); const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE; // tell everything within the radius of the soon-to-be buildee // to get out of the way; using the model radius is not correct // because this can be shorter than half the footprint diagonal CGameHelper::BuggerOff(build.pos, std::max(buildeeRadius, fpRadius), false, true, owner->team, NULL); NonMoving(); } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); return; } } // we are on the way to the buildpos, meanwhile it can happen // that another builder already finished our buildcmd or blocked // the buildpos with another building (skip our buildcmd then) if ((++randomCounter % 5) == 0) { if (IsBuildPosBlocked(build)) { StopMove(); FinishCommand(); return; } } } } }