bool CAirCAI::SelectNewAreaAttackTargetOrPos(const Command& ac) { assert(ac.GetID() == CMD_AREA_ATTACK || (ac.GetID() == CMD_ATTACK && ac.GetParamsCount() >= 3)); if (ac.GetID() == CMD_ATTACK) { FinishCommand(); return false; } const float3& pos = ac.GetPos(0); const float radius = ac.params[3]; std::vector<int> enemyUnitIDs; CGameHelper::GetEnemyUnits(pos, radius, owner->allyteam, enemyUnitIDs); if (enemyUnitIDs.empty()) { float3 attackPos = pos + (gs->randVector() * radius); attackPos.y = CGround::GetHeightAboveWater(attackPos.x, attackPos.z); owner->AttackGround(attackPos, (ac.options & INTERNAL_ORDER) == 0, false); SetGoal(attackPos, owner->pos); } else { // note: the range of randFloat() is inclusive of 1.0f const unsigned int unitIdx = std::min<int>(gs->randFloat() * enemyUnitIDs.size(), enemyUnitIDs.size() - 1); const unsigned int unitID = enemyUnitIDs[unitIdx]; CUnit* targetUnit = unitHandler->GetUnitUnsafe(unitID); SetOrderTarget(targetUnit); owner->AttackUnit(targetUnit, (ac.options & INTERNAL_ORDER) == 0, false); SetGoal(targetUnit->pos, owner->pos); } return true; }
void CCommandAI::ExecuteAttack(Command& c) { assert(owner->unitDef->canAttack); if (inCommand) { if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) { FinishCommand(); return; } if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { FinishCommand(); return; } } else { if (c.params.size() == 1) { CUnit* targetUnit = unitHandler->GetUnit(c.params[0]); if (targetUnit == NULL) { FinishCommand(); return; } if (targetUnit == owner) { FinishCommand(); return; } if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) { FinishCommand(); return; } SetOrderTarget(targetUnit); owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); inCommand = true; } else { owner->AttackGround(c.GetPos(0), (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); inCommand = true; } } }
void CFactoryCAI::DecreaseQueueCount(const Command& buildCommand, BuildOption& buildOption, bool) { // copy in case we get pop'ed // NOTE: the queue should not be empty at this point! const Command frontCommand = commandQue.empty()? Command(CMD_STOP): commandQue.front(); if (!repeatOrders || (buildCommand.options & INTERNAL_ORDER)) buildOption.numQued--; UpdateIconName(buildCommand.GetID(), buildOption); // if true, factory was set to wait and its buildee // could only have been finished by assisting units // --> make sure not to cancel the wait-order if (frontCommand.GetID() == CMD_WAIT) commandQue.pop_front(); // can only finish the real build-command command if // we still have it in our queue (FinishCommand also // asserts this) if (!commandQue.empty()) FinishCommand(); if (frontCommand.GetID() == CMD_WAIT) commandQue.push_front(frontCommand); }
bool CTransportCAI::AllowedCommand(const Command& c, bool fromSynced) { if (!CMobileCAI::AllowedCommand(c, fromSynced)) { return false; } switch (c.GetID()) { case CMD_UNLOAD_UNIT: case CMD_UNLOAD_UNITS: { const CTransportUnit* transport = static_cast<CTransportUnit*>(owner); const std::list<CTransportUnit::TransportedUnit>& transportees = transport->GetTransportedUnits(); // allow unloading empty transports for easier setup of transport bridges if (transportees.empty()) return true; if (c.GetParamsCount() == 5) { if (fromSynced) { // point transported buildings (...) in their wanted direction after unloading for (auto it = transportees.begin(); it != transportees.end(); ++it) { CBuilding* building = dynamic_cast<CBuilding*>(it->unit); if (building == NULL) continue; building->buildFacing = std::abs(int(c.GetParam(4))) % NUM_FACINGS; } } } if (c.GetParamsCount() >= 4) { // find unload positions for transportees (WHY can this run in unsynced context?) for (auto it = transportees.begin(); it != transportees.end(); ++it) { CUnit* u = it->unit; const float radius = (c.GetID() == CMD_UNLOAD_UNITS)? c.GetParam(3): 0.0f; const float spread = u->radius * transport->unitDef->unloadSpread; float3 foundPos; if (FindEmptySpot(c.GetPos(0), radius, spread, foundPos, u, fromSynced)) { return true; } // FIXME: support arbitrary unloading order for other unload types also if (unloadType != UNLOAD_LAND) { return false; } } // no empty spot found for any transported unit return false; } break; } } return true; }
std::vector<Command> CCommandAI::GetOverlapQueued(const Command& c, CCommandQueue& q) { CCommandQueue::iterator ci = q.end(); std::vector<Command> v; BuildInfo cbi(c); if (ci != q.begin()) { do { --ci; //iterate from the end and dont check the current order const Command& t = *ci; if (t.params.size() != c.params.size()) continue; if (t.GetID() == c.GetID() || (c.GetID() < 0 && t.GetID() < 0)) { if (c.params.size() == 1) { // assume the param is a unit or feature id if (t.params[0] == c.params[0]) { v.push_back(t); } } else if (c.params.size() >= 3) { // assume c and t are positional commands // NOTE: uses a BuildInfo structure, but <t> can be ANY command BuildInfo tbi; if (tbi.Parse(t)) { const float dist2X = 2.0f * math::fabs(cbi.pos.x - tbi.pos.x); const float dist2Z = 2.0f * math::fabs(cbi.pos.z - tbi.pos.z); const float addSizeX = SQUARE_SIZE * (cbi.GetXSize() + tbi.GetXSize()); const float addSizeZ = SQUARE_SIZE * (cbi.GetZSize() + tbi.GetZSize()); const float maxSizeX = SQUARE_SIZE * std::max(cbi.GetXSize(), tbi.GetXSize()); const float maxSizeZ = SQUARE_SIZE * std::max(cbi.GetZSize(), tbi.GetZSize()); if (cbi.def == NULL) continue; if (tbi.def == NULL) continue; if (((dist2X > maxSizeX) || (dist2Z > maxSizeZ)) && ((dist2X < addSizeX) && (dist2Z < addSizeZ))) { v.push_back(t); } } else { if ((cbi.pos - tbi.pos).SqLength2D() >= (COMMAND_CANCEL_DIST * COMMAND_CANCEL_DIST)) continue; if ((c.options & SHIFT_KEY) != 0 && (c.options & INTERNAL_ORDER) != 0) continue; v.push_back(t); } } } } while (ci != q.begin()); } return v; }
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 CFactoryCAI::InsertBuildCommand(CCommandQueue::iterator& it, const Command& newCmd) { map<int, BuildOption>::iterator boi = buildOptions.find(newCmd.GetID()); if (boi != buildOptions.end()) { boi->second.numQued++; UpdateIconName(newCmd.GetID(), boi->second); } if (!commandQue.empty() && (it == commandQue.begin())) { // ExecuteStop(), without the pop_front() CFactory* fac = static_cast<CFactory*>(owner); fac->StopBuild(); } commandQue.insert(it, newCmd); }
/// pushes 7 items on the stack static void PushUnitAndCommand(lua_State* L, const CUnit* unit, const Command& cmd) { // push the unit info lua_pushnumber(L, unit->id); lua_pushnumber(L, unit->unitDef->id); lua_pushnumber(L, unit->team); // push the command id lua_pushnumber(L, cmd.GetID()); // push the params list lua_newtable(L); for (int p = 0; p < (int)cmd.params.size(); p++) { lua_pushnumber(L, p + 1); lua_pushnumber(L, cmd.params[p]); lua_rawset(L, -3); } // push the options table lua_newtable(L); HSTR_PUSH_NUMBER(L, "coded", cmd.options); HSTR_PUSH_BOOL(L, "alt", !!(cmd.options & ALT_KEY)); HSTR_PUSH_BOOL(L, "ctrl", !!(cmd.options & CONTROL_KEY)); HSTR_PUSH_BOOL(L, "shift", !!(cmd.options & SHIFT_KEY)); HSTR_PUSH_BOOL(L, "right", !!(cmd.options & RIGHT_MOUSE_KEY)); HSTR_PUSH_BOOL(L, "meta", !!(cmd.options & META_KEY)); // push the command tag lua_pushnumber(L, cmd.tag); }
inline bool CBuilderCAI::OutOfImmobileRange(const Command& cmd) const { if (owner->unitDef->canmove) { return false; // builder can move } if (((cmd.options & INTERNAL_ORDER) == 0) || (cmd.params.size() != 1)) { return false; // not an internal object targetted command } const int objID = cmd.params[0]; const CWorldObject* obj = uh->GetUnit(objID); if (obj == NULL) { // features don't move, but maybe the unit was transported? obj = featureHandler->GetFeature(objID - uh->MaxUnits()); if (obj == NULL) { return false; } } switch (cmd.GetID()) { case CMD_REPAIR: case CMD_RECLAIM: case CMD_RESURRECT: case CMD_CAPTURE: { if (!IsInBuildRange(obj)) { return true; } break; } } return false; }
void CSelectedUnits::SendCommand(const Command& c) { if (selectionChanged) { // send new selection SendSelection(); } net->Send(CBaseNetProtocol::Get().SendCommand(gu->myPlayerNum, c.GetID(), c.options, c.params)); }
std::vector<Command> CCommandAI::GetOverlapQueued(const Command& c, CCommandQueue& q) { CCommandQueue::iterator ci = q.end(); std::vector<Command> v; BuildInfo cbi(c); if (ci != q.begin()){ do { --ci; //iterate from the end and dont check the current order const Command& t = *ci; if (((t.GetID() == c.GetID()) || ((c.GetID() < 0) && (t.GetID() < 0))) && (t.params.size() == c.params.size())){ if (c.params.size()==1) { // assume the param is a unit or feature id if (t.params[0] == c.params[0]) { v.push_back(t); } } else if (c.params.size() >= 3) { // assume this means that the first 3 makes a position BuildInfo tbi; if (tbi.Parse(t)) { const float dist2X = 2.0f * fabs(cbi.pos.x - tbi.pos.x); const float dist2Z = 2.0f * fabs(cbi.pos.z - tbi.pos.z); const float addSizeX = SQUARE_SIZE * (cbi.GetXSize() + tbi.GetXSize()); const float addSizeZ = SQUARE_SIZE * (cbi.GetZSize() + tbi.GetZSize()); const float maxSizeX = SQUARE_SIZE * std::max(cbi.GetXSize(), tbi.GetXSize()); const float maxSizeZ = SQUARE_SIZE * std::max(cbi.GetZSize(), tbi.GetZSize()); if (cbi.def && tbi.def && ((dist2X > maxSizeX) || (dist2Z > maxSizeZ)) && ((dist2X < addSizeX) && (dist2Z < addSizeZ))) { v.push_back(t); } } else { if ((cbi.pos - tbi.pos).SqLength2D() < (17.0f * 17.0f)) { v.push_back(t); } } } } } while (ci != q.begin()); } return v; }
inline void CCommandAI::SetCommandDescParam0(const Command& c) { for (unsigned int n = 0; n < possibleCommands.size(); n++) { if (possibleCommands[n].id != c.GetID()) continue; possibleCommands[n].params[0] = IntToString(int(c.params[0]), "%d"); break; } }
/** * Update the network tables associated with the Scheduler object on the * SmartDashboard. */ void Scheduler::UpdateTable() { CommandSet::iterator commandIter; if (m_table != nullptr) { // Get the list of possible commands to cancel auto new_toCancel = m_table->GetValue("Cancel"); if (new_toCancel) toCancel = new_toCancel->GetDoubleArray(); else toCancel.resize(0); // m_table->RetrieveValue("Ids", *ids); // cancel commands that have had the cancel buttons pressed // on the SmartDashboad if (!toCancel.empty()) { for (commandIter = m_commands.begin(); commandIter != m_commands.end(); ++commandIter) { for (unsigned i = 0; i < toCancel.size(); i++) { Command* c = *commandIter; if (c->GetID() == toCancel[i]) { c->Cancel(); } } } toCancel.resize(0); m_table->PutValue("Cancel", nt::Value::MakeDoubleArray(toCancel)); } // Set the running commands if (m_runningCommandsChanged) { commands.resize(0); ids.resize(0); for (commandIter = m_commands.begin(); commandIter != m_commands.end(); ++commandIter) { Command* c = *commandIter; commands.push_back(c->GetName()); ids.push_back(c->GetID()); } m_table->PutValue("Names", nt::Value::MakeStringArray(commands)); m_table->PutValue("Ids", nt::Value::MakeDoubleArray(ids)); } } }
inline void CCommandAI::SetCommandDescParam0(const Command& c) { vector<CommandDescription>::iterator cdi; for (cdi = possibleCommands.begin(); cdi != possibleCommands.end(); ++cdi) { if (cdi->id == c.GetID()) { char t[10]; SNPRINTF(t, 10, "%d", (int)c.params[0]); cdi->params[0] = t; return; } } }
void CCommandAI::ExecuteAttack(Command& c) { assert(owner->unitDef->canAttack); if (inCommand) { if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) { FinishCommand(); return; } if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { FinishCommand(); return; } if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { FinishCommand(); return; } } else { owner->commandShotCount = -1; if (c.params.size() == 1) { CUnit* targetUnit = uh->GetUnit(c.params[0]); if (targetUnit != NULL && targetUnit != owner) { owner->AttackUnit(targetUnit, c.GetID() == CMD_MANUALFIRE); SetOrderTarget(targetUnit); inCommand = true; } else { FinishCommand(); return; } } else { float3 pos(c.params[0], c.params[1], c.params[2]); owner->AttackGround(pos, c.GetID() == CMD_MANUALFIRE); inCommand = true; } } }
CCommandQueue::iterator CCommandAI::GetCancelQueued(const Command& c, CCommandQueue& q) { CCommandQueue::iterator ci = q.end(); while (ci != q.begin()) { --ci; //iterate from the end and dont check the current order const Command& c2 = *ci; const int cmdID = c.GetID(); const int cmd2ID = c2.GetID(); const bool attackAndFight = (cmdID == CMD_ATTACK && cmd2ID == CMD_FIGHT && c2.params.size() == 1); if (c2.params.size() != c.params.size()) continue; if ((cmdID == cmd2ID) || (cmdID < 0 && cmd2ID < 0) || attackAndFight) { if (c.params.size() == 1) { // assume the param is a unit-ID or feature-ID if ((c2.params[0] == c.params[0]) && (cmd2ID != CMD_SET_WANTED_MAX_SPEED)) { return ci; } } else if (c.params.size() >= 3) { if (cmdID < 0) { BuildInfo bc1(c); BuildInfo bc2(c2); if (bc1.def == NULL) continue; if (bc2.def == NULL) continue; if (math::fabs(bc1.pos.x - bc2.pos.x) * 2 <= std::max(bc1.GetXSize(), bc2.GetXSize()) * SQUARE_SIZE && math::fabs(bc1.pos.z - bc2.pos.z) * 2 <= std::max(bc1.GetZSize(), bc2.GetZSize()) * SQUARE_SIZE) { return ci; } } else { // assume c and c2 are positional commands const float3& c1p = c.GetPos(0); const float3& c2p = c2.GetPos(0); if ((c1p - c2p).SqLength2D() >= (COMMAND_CANCEL_DIST * COMMAND_CANCEL_DIST)) continue; if ((c.options & SHIFT_KEY) != 0 && (c.options & INTERNAL_ORDER) != 0) continue; return ci; } } } } return q.end(); }
void CCommandAI::FinishCommand() { assert(!commandQue.empty()); const Command cmd = commandQue.front(); //cppcheck false positive, copy is needed here const bool dontRepeat = (cmd.options & INTERNAL_ORDER); if (repeatOrders && !dontRepeat && (cmd.GetID() != CMD_STOP) && (cmd.GetID() != CMD_PATROL) && (cmd.GetID() != CMD_SET_WANTED_MAX_SPEED)){ commandQue.push_back(cmd); } commandQue.pop_front(); inCommand = false; targetDied = false; unimportantMove = false; SetOrderTarget(NULL); eoh->CommandFinished(*owner, cmd); eventHandler.UnitCmdDone(owner, cmd); ClearTargetLock(cmd); if (commandQue.empty()) { if (!owner->group) { eoh->UnitIdle(*owner); } eventHandler.UnitIdle(owner); } // avoid infinite loops if (lastFinishCommand != gs->frameNum) { lastFinishCommand = gs->frameNum; if (!owner->IsStunned()) { SlowUpdate(); } } }
static inline bool MayRequireSetMaxSpeedCommand(const Command &c) { switch (c.GetID()) { // this is not a complete list case CMD_STOP: case CMD_WAIT: case CMD_SELFD: case CMD_FIRE_STATE: case CMD_MOVE_STATE: case CMD_ONOFF: case CMD_REPEAT: { return false; } } return true; }
static inline bool IsCommandInMap(const Command& c) { // TODO: // extend the check to commands for which // position is not stored in params[0..2] if (c.params.size() < 3) { return true; } if ((c.GetPos(0)).IsInBounds()) { return true; } const float3 pos = c.GetPos(0); LOG_L(L_DEBUG, "Dropped command %d: outside of map (x:%f y:%f z:%f)", c.GetID(), pos.x, pos.y, pos.z); return false; }
void CWaitCommandsAI::AcknowledgeCommand(const Command& cmd) { if ((cmd.GetID() != CMD_WAIT) || (cmd.params.size() != 2)) { return; } const KeyType key = Wait::GetKeyFromFloat(cmd.params[1]); WaitMap::iterator it = unackedMap.find(key); if (it != unackedMap.end()) { Wait* wait = it->second; if (wait->GetCode() != cmd.params[0]) { return; // code mismatch } // move into the acknowledged pool unackedMap.erase(key); waitMap[key] = wait; } }
void CSelectedUnitsHandler::SendCommand(const Command& c) { if (selectionChanged) { // send new selection // first, convert CUnit* to unit IDs. std::vector<short> selectedUnitIDs(selectedUnits.size()); std::vector<short>::iterator i = selectedUnitIDs.begin(); CUnitSet::const_iterator ui = selectedUnits.begin(); for(; ui != selectedUnits.end(); ++i, ++ui) { *i = (*ui)->id; } clientNet->Send(CBaseNetProtocol::Get().SendSelect(gu->myPlayerNum, selectedUnitIDs)); selectionChanged = false; } clientNet->Send(CBaseNetProtocol::Get().SendCommand(gu->myPlayerNum, c.GetID(), c.options, c.params)); }
/// pushes 7 items on the stack static void PushUnitAndCommand(lua_State* L, const CUnit* unit, const Command& cmd) { // push the unit info lua_pushnumber(L, unit->id); lua_pushnumber(L, unit->unitDef->id); lua_pushnumber(L, unit->team); // push the command id lua_pushnumber(L, cmd.GetID()); // push the params list LuaUtils::PushCommandParamsTable(L, cmd, false); // push the options table LuaUtils::PushCommandOptionsTable(L, cmd, false); // push the command tag lua_pushnumber(L, cmd.tag); }
CCommandQueue::iterator CCommandAI::GetCancelQueued(const Command& c, CCommandQueue& q) { CCommandQueue::iterator ci = q.end(); while (ci != q.begin()) { --ci; //iterate from the end and dont check the current order const Command& c2 = *ci; const int& cmd_id = c.GetID(); const int& cmd2_id = c2.GetID(); if (((cmd_id == cmd2_id) || ((cmd_id < 0) && (cmd2_id < 0)) || (cmd2_id == CMD_FIGHT && cmd_id == CMD_ATTACK && c2.params.size() == 1)) && (c2.params.size() == c.params.size())) { if (c.params.size() == 1) { // assume the param is a unit-ID or feature-ID if ((c2.params[0] == c.params[0]) && (cmd2_id != CMD_SET_WANTED_MAX_SPEED)) { return ci; } } else if (c.params.size() >= 3) { if (cmd_id < 0) { BuildInfo bc(c); BuildInfo bc2(c2); if (bc.def && bc2.def && fabs(bc.pos.x - bc2.pos.x) * 2 <= std::max(bc.GetXSize(), bc2.GetXSize()) * SQUARE_SIZE && fabs(bc.pos.z - bc2.pos.z) * 2 <= std::max(bc.GetZSize(), bc2.GetZSize()) * SQUARE_SIZE) { return ci; } } else { // assume this means that the first 3 makes a position float3 cp(c.params[0], c.params[1], c.params[2]); float3 c2p(c2.params[0], c2.params[1], c2.params[2]); if ((cp - c2p).SqLength2D() < (17.0f * 17.0f)) { return ci; } } } } } return q.end(); }
int CCommandAI::CancelCommands(const Command &c, CCommandQueue& q, bool& first) { first = false; int cancelCount = 0; while (true) { CCommandQueue::iterator ci = GetCancelQueued(c, q); if (ci == q.end()) { return cancelCount; } first = first || (ci == q.begin()); cancelCount++; CCommandQueue::iterator firstErase = ci; CCommandQueue::iterator lastErase = ci; ++ci; if ((ci != q.end()) && (ci->GetID() == CMD_SET_WANTED_MAX_SPEED)) { lastErase = ci; cancelCount++; ++ci; } if ((ci != q.end()) && (ci->GetID() == CMD_WAIT)) { waitCommandsAI.RemoveWaitCommand(owner, *ci); lastErase = ci; cancelCount++; ++ci; } ++lastErase; // STL: erase the range [first, last) q.erase(firstErase, lastErase); if (c.GetID() >= 0) { return cancelCount; // only delete one non-build order } } return cancelCount; }
void CCommandAI::FinishCommand() { assert(!commandQue.empty()); const Command cmd = commandQue.front(); const int cmdID = cmd.GetID(); const int cmdTag = cmd.tag; const bool dontrepeat = (cmd.options & DONT_REPEAT) || (cmd.options & INTERNAL_ORDER); if (repeatOrders && !dontrepeat && (cmdID != CMD_STOP) && (cmdID != CMD_PATROL) && (cmdID != CMD_SET_WANTED_MAX_SPEED)){ commandQue.push_back(commandQue.front()); } commandQue.pop_front(); inCommand = false; targetDied = false; unimportantMove = false; SetOrderTarget(NULL); eoh->CommandFinished(*owner, cmd); eventHandler.UnitCmdDone(owner, cmdID, cmdTag); if (commandQue.empty()) { if (!owner->group) { eoh->UnitIdle(*owner); } eventHandler.UnitIdle(owner); } if (lastFinishCommand != gs->frameNum) { //avoid infinite loops lastFinishCommand = gs->frameNum; if (!owner->stunned) { SlowUpdate(); } } }
void CommandDrawer::DrawDefaultCommand(const Command& c, const CUnit* owner) const { // TODO add Lua callin perhaps, for more elaborate needs? const CCommandColors::DrawData* dd = cmdColors.GetCustomCmdData(c.GetID()); if (dd == NULL) { return; } const int paramsCount = c.params.size(); if (paramsCount == 1) { const CUnit* unit = uh->GetUnit(c.params[0]); if ((unit != NULL) && IsUnitTrackable(unit, owner)) { const float3& endPos = helper->GetUnitErrorPos(unit, owner->allyteam); lineDrawer.DrawLineAndIcon(dd->cmdIconID, endPos, dd->color); } return; } if (paramsCount < 3) { return; } const float3 endPos(c.params[0], c.params[1] + 3.0f, c.params[2]); if (!dd->showArea || (paramsCount < 4)) { lineDrawer.DrawLineAndIcon(dd->cmdIconID, endPos, dd->color); } else { const float radius = c.params[3]; lineDrawer.DrawLineAndIcon(dd->cmdIconID, endPos, dd->color); lineDrawer.Break(endPos, dd->color); glSurfaceCircle(endPos, radius, 20); lineDrawer.RestartWithColor(dd->color); } }
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 CCommandAI::GiveAllowedCommand(const Command& c, bool fromSynced) { if (ExecuteStateCommand(c)) { return; } switch (c.GetID()) { case CMD_SELFD: { if (owner->unitDef->canSelfD) { if (!(c.options & SHIFT_KEY) || commandQue.empty()) { if (owner->selfDCountdown != 0) { owner->selfDCountdown = 0; } else { owner->selfDCountdown = owner->unitDef->selfDCountdown*2+1; } } else if (commandQue.back().GetID() == CMD_SELFD) { commandQue.pop_back(); } else { commandQue.push_back(c); } } return; } case CMD_SET_WANTED_MAX_SPEED: { if (CanSetMaxSpeed() && (commandQue.empty() || (commandQue.back().GetID() != CMD_SET_WANTED_MAX_SPEED))) { // bail early, do not check for overlaps or queue cancelling commandQue.push_back(c); if (commandQue.size()==1 && !owner->beingBuilt) { SlowUpdate(); } } return; } case CMD_WAIT: { GiveWaitCommand(c); return; } case CMD_INSERT: { ExecuteInsert(c, fromSynced); return; } case CMD_REMOVE: { ExecuteRemove(c); return; } } // flush the queue for immediate commands if (!(c.options & SHIFT_KEY)) { if (!commandQue.empty()) { const int& cmd_id = commandQue.front().GetID(); if ((cmd_id == CMD_MANUALFIRE) || (cmd_id == CMD_ATTACK) || (cmd_id == CMD_AREA_ATTACK)) { owner->AttackUnit(0,true); } waitCommandsAI.ClearUnitQueue(owner, commandQue); ClearCommandDependencies(); commandQue.clear(); } inCommand=false; SetOrderTarget(NULL); } AddCommandDependency(c); if (c.GetID() == CMD_PATROL) { CCommandQueue::iterator ci = commandQue.begin(); for (; ci != commandQue.end() && ci->GetID() != CMD_PATROL; ++ci) { // just increment } if (ci == commandQue.end()) { if (commandQue.empty()) { Command c2(CMD_PATROL, c.options); c2.params.push_back(owner->pos.x); c2.params.push_back(owner->pos.y); c2.params.push_back(owner->pos.z); commandQue.push_back(c2); } else { do { --ci; if (ci->params.size() >= 3) { Command c2(CMD_PATROL, c.options); c2.params = ci->params; commandQue.push_back(c2); break; } else if (ci == commandQue.begin()) { Command c2(CMD_PATROL, c.options); c2.params.push_back(owner->pos.x); c2.params.push_back(owner->pos.y); c2.params.push_back(owner->pos.z); commandQue.push_back(c2); break; } } while (ci != commandQue.begin()); } } } // cancel duplicated commands bool first; if (CancelCommands(c, commandQue, first) > 0) { if (first) { Command stopCommand(CMD_STOP); commandQue.push_front(stopCommand); SlowUpdate(); } return; } // do not allow overlapping commands if (!GetOverlapQueued(c).empty()) { return; } if (c.GetID() == CMD_ATTACK) { // avoid weaponless units moving to 0 distance when given attack order if (owner->weapons.empty() && (owner->unitDef->canKamikaze == false)) { Command c2(CMD_STOP); commandQue.push_back(c2); return; } } commandQue.push_back(c); if (commandQue.size() == 1 && !owner->beingBuilt && !owner->stunned) { SlowUpdate(); } }
bool CCommandAI::ExecuteStateCommand(const Command& c) { switch (c.GetID()) { case CMD_FIRE_STATE: { owner->fireState = (int)c.params[0]; SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_MOVE_STATE: { owner->moveState = (int)c.params[0]; SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_REPEAT: { if (c.params[0] == 1) { repeatOrders = true; } else if(c.params[0] == 0) { repeatOrders = false; } else { // cause some code parts need it to be either 0 or 1, // we can not accept any other values as valid return false; } SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_TRAJECTORY: { owner->useHighTrajectory = !!c.params[0]; SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_ONOFF: { if (c.params[0] == 1) { owner->Activate(); } else if (c.params[0] == 0) { owner->Deactivate(); } else { // cause some code parts need it to be either 0 or 1, // we can not accept any other values as valid return false; } SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_CLOAK: { if (c.params[0] == 1) { owner->wantCloak = true; } else if(c.params[0] == 0) { owner->wantCloak = false; owner->curCloakTimeout = gs->frameNum + owner->cloakTimeout; } else { // cause some code parts need it to be either 0 or 1, // we can not accept any other values as valid return false; } SetCommandDescParam0(c); selectedUnits.PossibleCommandChange(owner); return true; } case CMD_STOCKPILE: { int change = 1; if (c.options & RIGHT_MOUSE_KEY) { change *= -1; } if (c.options & SHIFT_KEY) { change *= 5; } if (c.options & CONTROL_KEY) { change *= 20; } stockpileWeapon->numStockpileQued += change; if (stockpileWeapon->numStockpileQued < 0) { stockpileWeapon->numStockpileQued = 0; } UpdateStockpileIcon(); return true; } } return false; }
bool CCommandAI::AllowedCommand(const Command& c, bool fromSynced) { // TODO check if the command is in the map first, for more commands switch (c.GetID()) { case CMD_MOVE: case CMD_ATTACK: case CMD_AREA_ATTACK: case CMD_RECLAIM: case CMD_REPAIR: case CMD_RESURRECT: case CMD_PATROL: case CMD_RESTORE: case CMD_FIGHT: case CMD_MANUALFIRE: case CMD_UNLOAD_UNIT: case CMD_UNLOAD_UNITS: { if (!IsCommandInMap(c)) { return false; } } break; default: { // build commands if (c.GetID() < 0 && !IsCommandInMap(c)) { return false; } } break; } const UnitDef* ud = owner->unitDef; // AI's may do as they like const CSkirmishAIHandler::ids_t& saids = skirmishAIHandler.GetSkirmishAIsInTeam(owner->team); const bool aiOrder = (!saids.empty()); const int& cmd_id = c.GetID(); switch (cmd_id) { case CMD_MANUALFIRE: if (!ud->canManualFire) return false; case CMD_ATTACK: { if (!IsAttackCapable()) return false; if (c.params.size() == 1) { const CUnit* attackee = GetCommandUnit(c, 0); if (attackee && !attackee->pos.IsInBounds()) { return false; } } else { if (c.params.size() == 3) { const float3 cPos(c.params[0], c.params[1], c.params[2]); // check if attack ground is really attack ground if (!aiOrder && !fromSynced && fabs(cPos.y - ground->GetHeightReal(cPos.x, cPos.z)) > SQUARE_SIZE) { return false; } } } break; } case CMD_MOVE: if (!ud->canmove) return false; break; case CMD_FIGHT: if (!ud->canFight) return false; break; case CMD_GUARD: { const CUnit* guardee = GetCommandUnit(c, 0); if (!ud->canGuard) { return false; } if (owner && !owner->pos.IsInBounds()) { return false; } if (guardee && !guardee->pos.IsInBounds()) { return false; } } break; case CMD_PATROL: { if (!ud->canPatrol) { return false; } } break; case CMD_CAPTURE: { const CUnit* capturee = GetCommandUnit(c, 0); if (!ud->canCapture) { return false; } if (capturee && !capturee->pos.IsInBounds()) { return false; } } break; case CMD_RECLAIM: { const CUnit* reclaimeeUnit = GetCommandUnit(c, 0); const CFeature* reclaimeeFeature = NULL; if (c.IsAreaCommand()) { return true; } if (!ud->canReclaim) { return false; } if (reclaimeeUnit && !reclaimeeUnit->unitDef->reclaimable) { return false; } if (reclaimeeUnit && !reclaimeeUnit->AllowedReclaim(owner)) { return false; } if (reclaimeeUnit && !reclaimeeUnit->pos.IsInBounds()) { return false; } if (reclaimeeUnit == NULL && !c.params.empty()) { const unsigned int reclaimeeFeatureID(c.params[0]); if (reclaimeeFeatureID >= uh->MaxUnits()) { reclaimeeFeature = featureHandler->GetFeature(reclaimeeFeatureID - uh->MaxUnits()); if (reclaimeeFeature && !reclaimeeFeature->def->reclaimable) { return false; } } } } break; case CMD_RESTORE: { if (!ud->canRestore || mapDamage->disabled) { return false; } } break; case CMD_RESURRECT: { if (!ud->canResurrect) { return false; } } break; case CMD_REPAIR: { const CUnit* repairee = GetCommandUnit(c, 0); if (!ud->canRepair && !ud->canAssist) { return false; } if (repairee && !repairee->pos.IsInBounds()) { return false; } if (repairee && ((repairee->beingBuilt && !ud->canAssist) || (!repairee->beingBuilt && !ud->canRepair))) { return false; } } break; } if (cmd_id == CMD_FIRE_STATE && (c.params.empty() || !CanChangeFireState())) { return false; } if (cmd_id == CMD_MOVE_STATE && (c.params.empty() || (!ud->canmove && !ud->builder))) { return false; } if (cmd_id == CMD_REPEAT && (c.params.empty() || !ud->canRepeat || ((int)c.params[0] % 2) != (int)c.params[0]/* only 0 or 1 allowed */)) { return false; } if (cmd_id == CMD_TRAJECTORY && (c.params.empty() || ud->highTrajectoryType < 2)) { return false; } if (cmd_id == CMD_ONOFF && (c.params.empty() || !ud->onoffable || owner->beingBuilt || ((int)c.params[0] % 2) != (int)c.params[0]/* only 0 or 1 allowed */)) { return false; } if (cmd_id == CMD_CLOAK && (c.params.empty() || !ud->canCloak || ((int)c.params[0] % 2) != (int)c.params[0]/* only 0 or 1 allowed */)) { return false; } if (cmd_id == CMD_STOCKPILE && !stockpileWeapon) { return false; } return true; }