void CAirCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); if (int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))) { CUnit* guarded = uh->units[int(c.params[0])]; if(owner->unitDef->canAttack && guarded->lastAttack + 40 < gs->frameNum && owner->maxRange > 0 && IsValidTarget(guarded->lastAttacker)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options = c.options | INTERNAL_ORDER; commandQue.push_front(nc); SlowUpdate(); return; } else { Command c2; c2.id = CMD_MOVE; c2.options = c.options | INTERNAL_ORDER; c2.params.push_back(guarded->pos.x); c2.params.push_back(guarded->pos.y); c2.params.push_back(guarded->pos.z); c2.timeOut = gs->frameNum + 60; commandQue.push_front(c2); return; } } else { FinishCommand(); } }
bool CAbilityState::CanUseAbility() { if (m_PEntity->objtype == TYPE_MOB || m_PEntity->objtype == TYPE_PET) return true; if (m_PEntity->objtype == TYPE_PC) { auto PAbility = GetAbility(); auto PChar = static_cast<CCharEntity*>(m_PEntity); if (PChar->PRecastContainer->HasRecast(RECAST_ABILITY, PAbility->getRecastId(), PAbility->getRecastTime())) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_WAIT_LONGER)); return false; } if (PChar->StatusEffectContainer->HasStatusEffect({EFFECT_AMNESIA, EFFECT_IMPAIRMENT})) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_UNABLE_TO_USE_JA2)); return false; } std::unique_ptr<CMessageBasicPacket> errMsg; auto PTarget = GetTarget(); if (PChar->IsValidTarget(PTarget->targid, PAbility->getValidTarget(), errMsg)) { if (PChar != PTarget && distance(PChar->loc.p, PTarget->loc.p) > PAbility->getRange()) { PChar->pushPacket(new CMessageBasicPacket(PChar, PTarget, 0, 0, MSGBASIC_TOO_FAR_AWAY)); return false; } if (!m_PEntity->PAI->TargetFind->canSee(&PTarget->loc.p)) { m_errorMsg = std::make_unique<CMessageBasicPacket>(m_PEntity, PTarget, PAbility->getID(), 0, MSGBASIC_CANNOT_PERFORM_ACTION); return false; } if (PAbility->getID() >= ABILITY_HEALING_RUBY) { // Blood pact MP costs are stored under animation ID if (PChar->health.mp < PAbility->getAnimationID()) { PChar->pushPacket(new CMessageBasicPacket(PChar, PTarget, 0, 0, MSGBASIC_UNABLE_TO_USE_JA)); return false; } } CBaseEntity* PMsgTarget = PChar; int32 errNo = luautils::OnAbilityCheck(PChar, PTarget, PAbility, &PMsgTarget); if (errNo != 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PMsgTarget, PAbility->getID() + 16, PAbility->getID(), errNo)); return false; } return true; } return false; } return true; }
// The Node pointed to by node index is in range of it's target // If it's still a valid target (no one chomped it this frame) // then join these two segments HRESULT Chomp(short nodeIndex) { short target = g_nodes[nodeIndex].attribs.targetID; if (IsValidTarget(target, nodeIndex)) { g_nodes[nodeIndex].attribs.hasParent = true; g_nodes[target].attribs.hasChild = true; --g_numActiveNodes; } return S_OK; }
void CAirCAI::ExecuteGuard(Command& c) { assert(owner->unitDef->canGuard); const CUnit* guardee = unitHandler->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } const bool pushAttackCommand = (owner->maxRange > 0.0f) && owner->unitDef->canAttack && ((guardee->lastAttackFrame + 40) < gs->frameNum) && IsValidTarget(guardee->lastAttacker); if (pushAttackCommand) { Command nc(CMD_ATTACK, c.options | INTERNAL_ORDER, guardee->lastAttacker->id); commandQue.push_front(nc); SlowUpdate(); } else { Command c2(CMD_MOVE, c.options | INTERNAL_ORDER); c2.timeOut = gs->frameNum + 60; if (guardee->pos.IsInBounds()) { c2.PushPos(guardee->pos); } else { float3 clampedGuardeePos = guardee->pos; clampedGuardeePos.ClampInBounds(); c2.PushPos(clampedGuardeePos); } commandQue.push_front(c2); } }
void CAirCAI::ExecuteGuard(Command& c) { assert(owner->unitDef->canGuard); const CUnit* guardee = uh->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } const bool pushAttackCommand = owner->maxRange > 0.0f && owner->unitDef->canAttack && (guardee->lastAttack + 40 < gs->frameNum) && IsValidTarget(guardee->lastAttacker); if (pushAttackCommand) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(guardee->lastAttacker->id); nc.options = c.options | INTERNAL_ORDER; commandQue.push_front(nc); SlowUpdate(); } else { Command c2; c2.id = CMD_MOVE; c2.options = c.options | INTERNAL_ORDER; c2.timeOut = gs->frameNum + 60; if (guardee->pos.IsInBounds()) { c2.params.push_back(guardee->pos.x); c2.params.push_back(guardee->pos.y); c2.params.push_back(guardee->pos.z); } else { float3 clampedGuardeePos = guardee->pos; clampedGuardeePos.CheckInBounds(); c2.params.push_back(clampedGuardeePos.x); c2.params.push_back(clampedGuardeePos.y); c2.params.push_back(clampedGuardeePos.z); } commandQue.push_front(c2); } }
/** * @brief Executes the guard command c */ void CMobileCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); assert(!c.params.empty()); CUnit* guarded = uh->GetUnit(c.params[0]); if (guarded != NULL && UpdateTargetLostTimer(guarded->id)) { if (owner->unitDef->canAttack && guarded->lastAttack + 40 < gs->frameNum && IsValidTarget(guarded->lastAttacker)) { StopSlowGuard(); Command nc; nc.id = CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options = c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { //float3 dif = guarded->speed * guarded->frontdir; float3 dif = guarded->pos - owner->pos; dif.Normalize(); float3 goal = guarded->pos - dif * (guarded->radius + owner->radius + 64); if((goalPos - goal).SqLength2D() > 1600 || (goalPos - owner->pos).SqLength2D() < (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2) * (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){ SetGoal(goal, owner->pos); } if((goal - owner->pos).SqLength2D() < 6400) { StartSlowGuard(guarded->maxSpeed); if((goal-owner->pos).SqLength2D() < 1800){ StopMove(); NonMoving(); } } else { StopSlowGuard(); } } } else { FinishCommand(); } return; }
/** * @brief Executes the guard command c */ void CMobileCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); assert(!c.params.empty()); const CUnit* guardee = unitHandler->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } const bool pushAttackCommand = owner->unitDef->canAttack && (guardee->lastAttackFrame + 40 < gs->frameNum) && IsValidTarget(guardee->lastAttacker); if (pushAttackCommand) { Command nc(CMD_ATTACK, c.options, guardee->lastAttacker->id); commandQue.push_front(nc); StopSlowGuard(); SlowUpdate(); } else { const float3 dif = (guardee->pos - owner->pos).SafeNormalize(); const float3 goal = guardee->pos - dif * (guardee->radius + owner->radius + 64.0f); const bool resetGoal = ((goalPos - goal).SqLength2D() > 1600.0f) || (goalPos - owner->pos).SqLength2D() < Square(owner->moveType->GetMaxSpeed() * GAME_SPEED + 1 + SQUARE_SIZE * 2); if (resetGoal) { SetGoal(goal, owner->pos); } if ((goal - owner->pos).SqLength2D() < 6400.0f) { StartSlowGuard(guardee->moveType->GetMaxSpeed()); if ((goal - owner->pos).SqLength2D() < 1800.0f) { StopMove(); NonMoving(); } } else { StopSlowGuard(); } } }
bool CAirCAI::AirAutoGenerateTarget(AAirMoveType* myPlane) { assert(commandQue.empty()); assert(myPlane->owner == owner); const UnitDef* ownerDef = owner->unitDef; const bool autoLand = !ownerDef->DontLand() && myPlane->autoLand; const bool autoAttack = ((owner->fireState >= FIRESTATE_FIREATWILL) && (owner->moveState != MOVESTATE_HOLDPOS)); if (myPlane->aircraftState == AAirMoveType::AIRCRAFT_FLYING && autoLand) { StopMove(); } if (ownerDef->canAttack && autoAttack && owner->maxRange > 0) { if (ownerDef->IsFighterAirUnit()) { const float3 P = owner->pos + (owner->speed * 10.0); const float R = 1000.0f * owner->moveState; const CUnit* enemy = CGameHelper::GetClosestEnemyAircraft(NULL, P, R, owner->allyteam); if (IsValidTarget(enemy)) { Command nc(CMD_ATTACK, INTERNAL_ORDER, enemy->id); commandQue.push_front(nc); inCommand = false; return true; } } else { const float3 P = owner->pos + (owner->speed * 20.0f); const float R = 500.0f * owner->moveState; const CUnit* enemy = CGameHelper::GetClosestValidTarget(P, R, owner->allyteam, this); if (enemy != NULL) { Command nc(CMD_ATTACK, INTERNAL_ORDER, enemy->id); commandQue.push_front(nc); inCommand = false; return true; } } } return false; }
bool CPlayerController::Engage(uint16 targid) { //#TODO: pet engage/disengage std::unique_ptr<CMessageBasicPacket> errMsg; auto PChar = static_cast<CCharEntity*>(POwner); auto PTarget = PChar->IsValidTarget(targid, TARGET_ENEMY, errMsg); if (PTarget) { if (distance(PChar->loc.p, PTarget->loc.p) < 30) { if (m_LastAttackTime + std::chrono::milliseconds(PChar->GetWeaponDelay(false)) < server_clock::now()) { if (CController::Engage(targid)) { PChar->PLatentEffectContainer->CheckLatentsWeaponDraw(true); PChar->pushPacket(new CLockOnPacket(PChar, PTarget)); return true; } } else { errMsg = std::make_unique<CMessageBasicPacket>(PChar, PTarget, 0, 0, MSGBASIC_WAIT_LONGER); } } else { errMsg = std::make_unique<CMessageBasicPacket>(PChar, PTarget, 0, 0, MSGBASIC_TOO_FAR_AWAY); } } if (errMsg) { PChar->HandleErrorMessage(errMsg); } return false; }
/** * @brief Executes the Fight command c */ void CMobileCAI::ExecuteFight(Command &c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if(c.params.size() == 1) { if(orderTarget && owner->weapons.size() > 0 && !owner->weapons.front()->AttackUnit(orderTarget, false)) { CUnit* newTarget = helper->GetClosestEnemyUnit( owner->pos, owner->maxRange, owner->allyteam); if(IsValidTarget(newTarget) && !owner->weapons.empty() && owner->weapons.front()->AttackUnit(newTarget, false)) { c.params[0] = newTarget->id; inCommand = false; } else { owner->weapons.front()->AttackUnit(orderTarget, false); } } ExecuteAttack(c); return; } if(tempOrder){ inCommand = true; tempOrder = false; } if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got fight cmd with less than 3 params on %s in mobilecai", owner->unitDef->humanName.c_str()); return; } if(c.params.size() >= 6){ if(!inCommand){ commandPos1 = float3(c.params[3],c.params[4],c.params[5]); } } else { // Some hackery to make sure the line (commandPos1,commandPos2) is NOT // rotated (only shortened) if we reach this because the previous return // fight command finished by the 'if((curPos-pos).SqLength2D()<(64*64)){' // condition, but is actually updated correctly if you click somewhere // outside the area close to the line (for a new command). commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) { commandPos1 = owner->pos; } } float3 pos(c.params[0],c.params[1],c.params[2]); if(!inCommand){ inCommand = true; commandPos2 = pos; lastUserGoal = commandPos2; } if(c.params.size() >= 6){ pos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } if(pos!=goalPos){ SetGoal(pos, owner->pos); } if(owner->unitDef->canAttack && owner->fireState>=2){ float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); CUnit* enemy=helper->GetClosestEnemyUnit( curPosOnLine, owner->maxRange + 100 * owner->moveState * owner->moveState, owner->allyteam); if(IsValidTarget(enemy) && !owner->weapons.empty()) { Command c2; c2.id=CMD_FIGHT; c2.options=c.options|INTERNAL_ORDER; c2.params.push_back(enemy->id); PushOrUpdateReturnFight(); commandQue.push_front(c2); inCommand=false; tempOrder=true; if(lastPC!=gs->frameNum){ //avoid infinite loops lastPC=gs->frameNum; SlowUpdate(); } return; } } if((owner->pos - goalPos).SqLength2D() < (64 * 64) || (owner->moveType->progressState == AMoveType::Failed)){ FinishCommand(); } return; }
void CPlayerController::WeaponSkill(uint16 targid, uint16 wsid) { auto PChar = static_cast<CCharEntity*>(POwner); if (PChar->PAI->CanChangeState()) { //#TODO: put all this in weaponskill_state CWeaponSkill* PWeaponSkill = battleutils::GetWeaponSkill(wsid); if (PWeaponSkill && !charutils::hasWeaponSkill(PChar, PWeaponSkill->getID())) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_CANNOT_USE_WS)); return; } if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_AMNESIA)) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_CANNOT_USE_ANY_WS)); return; } if (PChar->health.tp < 1000) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_NOT_ENOUGH_TP)); return; } if (PWeaponSkill->getType() == SKILL_ARC || PWeaponSkill->getType() == SKILL_MRK) { CItemWeapon* PItem = (CItemWeapon*)PChar->getEquip(SLOT_AMMO); // before allowing ranged weapon skill... if (PItem == nullptr || !(PItem->isType(ITEM_WEAPON)) || !PChar->m_Weapons[SLOT_AMMO]->isRanged() || !PChar->m_Weapons[SLOT_RANGED]->isRanged() || PChar->equip[SLOT_AMMO] == 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_NO_RANGED_WEAPON)); return; } } std::unique_ptr<CMessageBasicPacket> errMsg; auto PTarget = PChar->IsValidTarget(targid, battleutils::isValidSelfTargetWeaponskill(wsid) ? TARGET_SELF : TARGET_ENEMY, errMsg); if (PTarget) { if (!isFaceing(PChar->loc.p, PTarget->loc.p, 40)) { PChar->pushPacket(new CMessageBasicPacket(PChar, PTarget, 0, 0, MSGBASIC_CANNOT_SEE)); return; } CController::WeaponSkill(targid, wsid); } else if (errMsg) { PChar->pushPacket(std::move(errMsg)); } } else { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_UNABLE_TO_USE_WS)); } }
void CAirCAI::ExecuteFight(Command &c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); AAirMoveType* myPlane = (AAirMoveType*) owner->moveType; if(tempOrder){ tempOrder=false; inCommand=true; } if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got fight cmd with less than 3 params on %s in AirCAI", owner->unitDef->humanName.c_str()); return; } if(c.params.size() >= 6){ if(!inCommand){ commandPos1 = float3(c.params[3],c.params[4],c.params[5]); } } else { // Some hackery to make sure the line (commandPos1,commandPos2) is NOT // rotated (only shortened) if we reach this because the previous return // fight command finished by the 'if((curPos-pos).SqLength2D()<(127*127)){' // condition, but is actually updated correctly if you click somewhere // outside the area close to the line (for a new command). commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (150 * 150)) { commandPos1 = owner->pos; } } goalPos = float3(c.params[0], c.params[1], c.params[2]); if(!inCommand){ inCommand = true; commandPos2=goalPos; } if(c.params.size() >= 6){ goalPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } // CMD_FIGHT is pretty useless if !canAttack but we try to honour the modders wishes anyway... if (owner->unitDef->canAttack && owner->fireState >= 2 && owner->moveState != 0 && owner->maxRange > 0) { float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed*10); float testRad = 1000 * owner->moveState; CUnit* enemy = NULL; if(myPlane->IsFighter()) { enemy = helper->GetClosestEnemyAircraft(curPosOnLine, testRad, owner->allyteam); } if(IsValidTarget(enemy) && (owner->moveState!=1 || LinePointDist(commandPos1, commandPos2, enemy->pos) < 1000)) { Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC1!=gs->frameNum){ //avoid infinite loops lastPC1=gs->frameNum; SlowUpdate(); } return; } else { float3 curPosOnLine = ClosestPointOnLine( commandPos1, commandPos2, owner->pos + owner->speed * 20); float testRad = 500 * owner->moveState; enemy = helper->GetClosestEnemyUnit(curPosOnLine, testRad, owner->allyteam); if(IsValidTarget(enemy)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = c.options | INTERNAL_ORDER; PushOrUpdateReturnFight(); commandQue.push_front(nc); tempOrder = true; inCommand = false; if(lastPC2 != gs->frameNum){ //avoid infinite loops lastPC2 = gs->frameNum; SlowUpdate(); } return; } } } myPlane->goalPos = goalPos; if((owner->pos - goalPos).SqLength2D() < (127 * 127) || (owner->pos + owner->speed*8 - goalPos).SqLength2D() < (127 * 127)) { FinishCommand(); } return; }
void CAirCAI::SlowUpdate() { if (!commandQue.empty() && commandQue.front().timeOut < gs->frameNum) { FinishCommand(); return; } if (owner->usingScriptMoveType) { return; // avoid the invalid (CAirMoveType*) cast } AAirMoveType* myPlane=(AAirMoveType*) owner->moveType; if(owner->unitDef->maxFuel > 0){ RefuelIfNeeded(); } if(commandQue.empty()){ if(myPlane->aircraftState == AAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand() && myPlane->autoLand){ StopMove(); // myPlane->SetState(AAirMoveType::AIRCRAFT_LANDING); } if(owner->unitDef->canAttack && owner->fireState>=2 && owner->moveState != 0 && owner->maxRange > 0){ if(myPlane->IsFighter()){ float testRad=1000 * owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft( owner->pos + (owner->speed * 10), testRad, owner->allyteam); if(IsValidTarget(enemy)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = 0; commandQue.push_front(nc); inCommand = false; return; } } float testRad = 500 * owner->moveState; CUnit* enemy = helper->GetClosestEnemyUnit( owner->pos + (owner->speed * 20), testRad, owner->allyteam); if(IsValidTarget(enemy)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = 0; commandQue.push_front(nc); inCommand = false; return; } } return; } Command& c = commandQue.front(); if (c.id == CMD_WAIT) { if ((myPlane->aircraftState == AAirMoveType::AIRCRAFT_FLYING) && !owner->unitDef->DontLand() && myPlane->autoLand) { StopMove(); // myPlane->SetState(AAirMoveType::AIRCRAFT_LANDING); } return; } if (c.id != CMD_STOP) { myPlane->Takeoff(); } float3 curPos=owner->pos; switch(c.id){ case CMD_AREA_ATTACK:{ ExecuteAreaAttack(c); return; } default:{ CMobileCAI::Execute(); return; } } }
void CAirCAI::SlowUpdate() { if(gs->paused) // Commands issued may invoke SlowUpdate when paused return; if (!commandQue.empty() && commandQue.front().timeOut < gs->frameNum) { FinishCommand(); return; } if (owner->usingScriptMoveType) { return; // avoid the invalid (CAirMoveType*) cast } AAirMoveType* myPlane=(AAirMoveType*) owner->moveType; bool wantToRefuel = LandRepairIfNeeded(); if(!wantToRefuel && owner->unitDef->maxFuel > 0){ wantToRefuel = RefuelIfNeeded(); } if(commandQue.empty()){ if(myPlane->aircraftState == AAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand() && myPlane->autoLand) { StopMove(); // myPlane->SetState(AAirMoveType::AIRCRAFT_LANDING); } if(owner->unitDef->canAttack && owner->fireState >= FIRESTATE_FIREATWILL && owner->moveState != MOVESTATE_HOLDPOS && owner->maxRange > 0) { if (myPlane->IsFighter()) { float testRad=1000 * owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft( owner->pos + (owner->speed * 10), testRad, owner->allyteam); if(IsValidTarget(enemy)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = 0; commandQue.push_front(nc); inCommand = false; return; } } const float searchRadius = 500 * owner->moveState; CUnit* enemy = helper->GetClosestValidTarget( owner->pos + (owner->speed * 20), searchRadius, owner->allyteam, this); if (enemy != NULL) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = 0; commandQue.push_front(nc); inCommand = false; return; } } return; } Command& c = commandQue.front(); if (c.id == CMD_WAIT) { if ((myPlane->aircraftState == AAirMoveType::AIRCRAFT_FLYING) && !owner->unitDef->DontLand() && myPlane->autoLand) { StopMove(); // myPlane->SetState(AAirMoveType::AIRCRAFT_LANDING); } return; } if (c.id != CMD_STOP && c.id != CMD_AUTOREPAIRLEVEL && c.id != CMD_IDLEMODE && c.id != CMD_SET_WANTED_MAX_SPEED) { myPlane->Takeoff(); } if (wantToRefuel) { switch (c.id) { case CMD_AREA_ATTACK: case CMD_ATTACK: case CMD_FIGHT: return; } } switch(c.id){ case CMD_AREA_ATTACK:{ ExecuteAreaAttack(c); return; } default:{ CMobileCAI::Execute(); return; } } }
HRESULT FindNearestNeighbor(short index) { HRESULT hr = S_OK; if (g_nodes[index].attribs.hasParent == true) return S_FALSE; float2 pos = {g_nodes[index].position.getX(), g_nodes[index].position.getY()}; if (Bin(pos.x, pos.y, nullptr) != S_OK) return S_FALSE; // if we're not in a bin backed by memory, just keep our old neighbor int xrange[2] = {int(pos.x/g_binNWidth - 0.5f), int(pos.x/g_binNWidth + 0.5f)}; int yrange[2] = {int(pos.y/g_binNHeight - 0.5f), int(pos.y/g_binNHeight + 0.5f)}; uint minDist = -1; ushort nearest = -1; int bin; do { // Yes, we'll re-iterate over some bins, but the bin rows are stored linearly in memory // So the cache should make this pretty cheap (plus it's only one access since they'll probably be empty) for (int y = yrange[0]; y <= yrange[1]; y++) { for (int x = xrange[0]; x <= xrange[1]; x++) { hr = Bin(x, y, &bin); ASSERT(SUCCEEDED(hr)); // Bin fails if the bin isn't memory backed. for (uint slot = 0; slot < g_binStride; slot++) { // TODO: These large strides are going to kill the cache! // We should probably switch to storing the node indexes linearly with the MSb denoting end of bucket // Then we'd have a separate table to index into this based on bucket // No, that won't work because inserts would be very difficult/expensive. The easiest way would be a linked // list, but that would obviously be super slow. I think I the first try was actually the best ;D ushort target = g_slots[bin*g_binStride + slot]; if (target == EMPTY_SLOT) break; else if (IsValidTarget(target, index)) { uint dist = Distance(g_nodes[index].position, g_nodes[target].position); if (dist < minDist) { minDist = dist; nearest = target; } } } } } if (xrange[0] > g_binRangeX[0]) xrange[0]--; if (xrange[1] < g_binRangeX[1]) xrange[1]++; if (yrange[0] > g_binRangeY[0]) yrange[0]--; if (yrange[1] < g_binRangeY[1]) yrange[1]++; // Do we need this? Could happen if a vert is in a quadrant of it's own if (xrange[1] - xrange[0] == g_binRangeX[1] - g_binRangeX[0] && yrange[1] - yrange[0] == g_binRangeY[1] - g_binRangeY[0]) break; } while (nearest == ushort(-1)); if (nearest != ushort(-1)) g_nodes[index].attribs.targetID = nearest; else if (IsValidTarget(g_nodes[index].attribs.targetID, index) == false) { // If our current target is invalid, and we weren't able to find a new one, we'll have to revert to N^2 for (uint i = 0; i < g_numNodes; i++) { if (IsValidTarget(i, index)) { uint dist = Distance(g_nodes[index].position, g_nodes[i].position); if (dist < minDist) { minDist = dist; nearest = i; } } } ASSERT(nearest != -1); g_nodes[index].attribs.targetID = nearest; } return S_OK; }
void CMobileCAI::IdleCheck(void) { if(owner->unitDef->canAttack && owner->moveState && owner->fireState && !owner->weapons.empty() && owner->haveTarget) { if(!owner->userTarget) { owner->haveTarget = false; } else if(owner->pos.SqDistance2D(owner->userTarget->pos) < Square(owner->maxRange + 200*owner->moveState*owner->moveState)) { Command c; c.id = CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(owner->userTarget->id); c.timeOut = gs->frameNum + 140; commandQue.push_front(c); return; } } if(owner->unitDef->canAttack && owner->moveState && owner->fireState && !owner->weapons.empty() && !owner->haveTarget) { if(owner->lastAttacker && owner->lastAttack + 200 > gs->frameNum && !(owner->unitDef->noChaseCategory & owner->lastAttacker->category)){ float3 apos=owner->lastAttacker->pos; float dist=apos.SqDistance2D(owner->pos); if(dist<Square(owner->maxRange+200*owner->moveState*owner->moveState)){ Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(owner->lastAttacker->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); return; } } } if (owner->unitDef->canAttack && (gs->frameNum >= lastIdleCheck+10) && owner->moveState && owner->fireState>=2 && !owner->weapons.empty() && !owner->haveTarget) { CUnit* u = helper->GetClosestEnemyUnit(owner->pos, owner->maxRange + 150 * owner->moveState * owner->moveState, owner->allyteam); if(IsValidTarget(u)) { Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(u->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); return; } } if (owner->usingScriptMoveType) { return; } lastIdleCheck = gs->frameNum; if (((owner->pos - lastUserGoal).SqLength2D() > 10000.0f) && !owner->haveTarget && !dynamic_cast<CTAAirMoveType*>(owner->moveType)) { //note that this is not internal order so that we dont keep generating //new orders if we cant get to that pos Command c; c.id=CMD_MOVE; c.options=0; c.params.push_back(lastUserGoal.x); c.params.push_back(lastUserGoal.y); c.params.push_back(lastUserGoal.z); commandQue.push_front(c); unimportantMove=true; } else { NonMoving(); } }
void CAirCAI::ExecuteFight(Command& c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); // FIXME: check owner->UsingScriptMoveType() and skip rest if true? AAirMoveType* myPlane = GetStrafeAirMoveType(owner); assert(owner == myPlane->owner); if (tempOrder) { tempOrder = false; inCommand = true; } if (c.params.size() < 3) { LOG_L(L_ERROR, "[ACAI::%s][f=%d][id=%d] CMD_FIGHT #params < 3", __FUNCTION__, gs->frameNum, owner->id); return; } if (c.params.size() >= 6) { if (!inCommand) { commandPos1 = c.GetPos(3); } } else { // HACK to make sure the line (commandPos1,commandPos2) is NOT // rotated (only shortened) if we reach this because the previous return // fight command finished by the 'if((curPos-pos).SqLength2D()<(127*127)){' // condition, but is actually updated correctly if you click somewhere // outside the area close to the line (for a new command). commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (150 * 150)) { commandPos1 = owner->pos; } } goalPos = c.GetPos(0); if (!inCommand) { inCommand = true; commandPos2 = goalPos; } if (c.params.size() >= 6) { goalPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } // CMD_FIGHT is pretty useless if !canAttack, but we try to honour the modders wishes anyway... if (owner->unitDef->canAttack && (owner->fireState >= FIRESTATE_FIREATWILL) && (owner->moveState != MOVESTATE_HOLDPOS) && (owner->maxRange > 0)) { CUnit* enemy = NULL; if (owner->unitDef->IsFighterAirUnit()) { const float3 P = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed*10); const float R = 1000.0f * owner->moveState; enemy = CGameHelper::GetClosestEnemyAircraft(NULL, P, R, owner->allyteam); } if (IsValidTarget(enemy) && (owner->moveState != MOVESTATE_MANEUVER || LinePointDist(commandPos1, commandPos2, enemy->pos) < 1000)) { // make the attack-command inherit <c>'s options // (if <c> is internal, then so are the attacks) // // this is needed because CWeapon code will not // fire on "internal" targets if the weapon has // noAutoTarget set (although the <enemy> CUnit* // is technically not a user-target, we treat it // as such) even when explicitly told to fight Command nc(CMD_ATTACK, c.options, enemy->id); commandQue.push_front(nc); tempOrder = true; inCommand = false; if (lastPC1 != gs->frameNum) { // avoid infinite loops lastPC1 = gs->frameNum; SlowUpdate(); } return; } else { const float3 P = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed * 20); const float R = 500.0f * owner->moveState; enemy = CGameHelper::GetClosestValidTarget(P, R, owner->allyteam, this); if (enemy != NULL) { PushOrUpdateReturnFight(); // make the attack-command inherit <c>'s options Command nc(CMD_ATTACK, c.options, enemy->id); commandQue.push_front(nc); tempOrder = true; inCommand = false; // avoid infinite loops (?) if (lastPC2 != gs->frameNum) { lastPC2 = gs->frameNum; SlowUpdate(); } return; } } } myPlane->goalPos = goalPos; const CStrafeAirMoveType* airMT = (!owner->UsingScriptMoveType())? static_cast<const CStrafeAirMoveType*>(myPlane): NULL; const float radius = (airMT != NULL)? std::max(airMT->turnRadius + 2*SQUARE_SIZE, 128.f) : 127.f; // we're either circling or will get to the target in 8 frames if ((owner->pos - goalPos).SqLength2D() < (radius * radius) || (owner->pos + owner->speed*8 - goalPos).SqLength2D() < 127*127) { FinishCommand(); } }