/** ** Find the next idle worker, select it, and center on it */ void UiFindIdleWorker() { if (ThisPlayer->FreeWorkers.empty()) { return; } CUnit *unit = ThisPlayer->FreeWorkers[0]; if (LastIdleWorker) { const std::vector<CUnit *> &freeWorkers = ThisPlayer->FreeWorkers; std::vector<CUnit *>::const_iterator it = std::find(freeWorkers.begin(), freeWorkers.end(), LastIdleWorker); if (it != ThisPlayer->FreeWorkers.end()) { if (*it != ThisPlayer->FreeWorkers.back()) { unit = *(++it); } } } if (unit != NULL) { LastIdleWorker = unit; SelectSingleUnit(*unit); UI.StatusLine.Clear(); UI.StatusLine.ClearCosts(); CurrentButtonLevel = 0; PlayUnitSound(*Selected[0], VoiceSelected); SelectionChanged(); UI.SelectedViewport->Center(unit->GetMapPixelPosCenter()); } }
/** ** Show the current order of a unit. ** ** @param unit Pointer to the unit. */ void ShowOrder(const CUnit &unit) { if (unit.Destroyed || unit.Removed) { return; } #ifndef DEBUG if (!ThisPlayer->IsAllied(unit) && unit.Player != ThisPlayer) { return; } #endif // Get current position const PixelPos mapPos = unit.GetMapPixelPosCenter(); PixelPos screenStartPos = CurrentViewport->MapToScreenPixelPos(mapPos); const bool flushed = unit.Orders[0]->Finished; COrderPtr order; // If the current order is cancelled show the next one if (unit.Orders.size() > 1 && flushed) { order = unit.Orders[1]; } else { order = unit.Orders[0]; } PixelPos screenPos = order->Show(*CurrentViewport, screenStartPos); // Show the rest of the orders for (size_t i = 1 + (flushed ? 1 : 0); i < unit.Orders.size(); ++i) { screenPos = unit.Orders[i]->Show(*CurrentViewport, screenPos); } // Show order for new trained units if (unit.NewOrder) { unit.NewOrder->Show(*CurrentViewport, screenStartPos); } }
/** ** Find the next idle worker, select it, and center on it */ static void UiFindIdleWorker() { if (ThisPlayer->FreeWorkers.empty()) { return; } CUnit *unit = ThisPlayer->FreeWorkers[0]; if (unit != NULL) { SelectSingleUnit(*unit); UI.StatusLine.Clear(); ClearCosts(); CurrentButtonLevel = 0; PlayUnitSound(*Selected[0], VoiceSelected); SelectionChanged(); UI.SelectedViewport->Center(unit->GetMapPixelPosCenter()); } }
/** ** Show selection marker around a unit. ** ** @param unit Pointer to unit. */ void DrawUnitSelection(const CViewport &vp, const CUnit &unit) { IntColor color; // FIXME: make these colors customizable with scripts. if (Editor.Running && UnitUnderCursor == &unit && Editor.State == EditorSelecting) { color = ColorWhite; } else if (unit.Selected || unit.TeamSelected || (unit.Blink & 1)) { if (unit.Player->Index == PlayerNumNeutral) { color = ColorYellow; } else if ((unit.Selected || (unit.Blink & 1)) && (unit.Player == ThisPlayer || ThisPlayer->IsTeamed(unit))) { color = ColorGreen; } else if (ThisPlayer->IsEnemy(unit)) { color = ColorRed; } else { color = PlayerColors[GameSettings.Presets[unit.Player->Index].PlayerColor][0]; for (int i = 0; i < PlayerMax; ++i) { if (unit.TeamSelected & (1 << i)) { color = PlayerColors[GameSettings.Presets[i].PlayerColor][0]; } } } } else if (CursorBuilding && unit.Type->Building && unit.CurrentAction() != UnitActionDie && (unit.Player == ThisPlayer || ThisPlayer->IsTeamed(unit))) { // If building mark all own buildings color = ColorGray; } else { return; } const CUnitType &type = *unit.Type; const PixelPos screenPos = vp.MapToScreenPixelPos(unit.GetMapPixelPosCenter()); const int x = screenPos.x - type.BoxWidth / 2 - (type.Width - (type.Sprite ? type.Sprite->Width : 0)) / 2; const int y = screenPos.y - type.BoxHeight / 2 - (type.Height - (type.Sprite ? type.Sprite->Height : 0)) / 2; DrawSelection(color, x + type.BoxOffsetX, y + type.BoxOffsetY, x + type.BoxWidth + type.BoxOffsetX, y + type.BoxHeight + type.BoxOffsetY); }
/* virtual */ void COrder_Follow::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); UnitShowAnimation(unit, unit.GetAnimations()->Still); //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); // Reached target if (this->State == State_TargetReached) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->Finished = true; return ; } // Don't follow after immobile units if (goal && goal->CanMove() == false) { this->Finished = true; return; } //Wyrmgus start // if (goal->tilePos == this->goalPos) { if (goal->tilePos == this->goalPos && goal->MapLayer == this->MapLayer) { //Wyrmgus end // Move to the next order if (unit.Orders.size() > 1) { this->Finished = true; return ; } unit.Wait = 10; if (this->Range > 1) { this->Range = 1; this->State = State_Init; } return ; } this->State = State_Init; } if (this->State == State_Init) { // first entry this->State = State_Initialized; } switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start if ((Map.Field(unit.tilePos, unit.MapLayer)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table, unit.MapLayer); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands, this->HasGoal() ? this->GetGoal()->MapLayer : this->MapLayer); } return; } } } //Wyrmgus end // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal) { // goal has died this->Finished = true; return ; } // Handle Teleporter Units // FIXME: BAD HACK // goal shouldn't be busy and portal should be alive if (goal->Type->BoolFlag[TELEPORTER_INDEX].value && goal->Goal && goal->Goal->IsAlive() && unit.MapDistanceTo(*goal) <= 1) { if (!goal->IsIdle()) { // wait unit.Wait = 10; return; } // Check if we have enough mana if (goal->Goal->Type->TeleportCost > goal->Variable[MANA_INDEX].Value) { this->Finished = true; return; } else { goal->Variable[MANA_INDEX].Value -= goal->Goal->Type->TeleportCost; } // Everything is OK, now teleport the unit unit.Remove(NULL); if (goal->Type->TeleportEffectIn) { goal->Type->TeleportEffectIn->pushPreamble(); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(unit)); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(*goal)); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().x); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().y); goal->Type->TeleportEffectIn->run(); } unit.tilePos = goal->Goal->tilePos; //Wyrmgus start unit.MapLayer = goal->Goal->MapLayer; //Wyrmgus end DropOutOnSide(unit, unit.Direction, NULL); // FIXME: we must check if the units supports the new order. CUnit &dest = *goal->Goal; if (dest.Type->TeleportEffectOut) { dest.Type->TeleportEffectOut->pushPreamble(); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(unit)); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(dest)); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().x); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().y); dest.Type->TeleportEffectOut->run(); } if (dest.NewOrder == NULL || (dest.NewOrder->Action == UnitActionResource && !unit.Type->BoolFlag[HARVESTER_INDEX].value) //Wyrmgus start // || (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack) || (dest.NewOrder->Action == UnitActionAttack && !unit.CanAttack(true)) //Wyrmgus end || (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) { this->Finished = true; return ; } else { if (dest.NewOrder->HasGoal()) { if (dest.NewOrder->GetGoal()->Destroyed) { delete dest.NewOrder; dest.NewOrder = NULL; this->Finished = true; return ; } unit.Orders.insert(unit.Orders.begin() + 1, dest.NewOrder->Clone()); this->Finished = true; return ; } } } this->goalPos = goal->tilePos; //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->State = State_TargetReached; } // FALL THROUGH default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->ClearGoal(); goal = NULL; } if (unit.Anim.Unbreakable) { return ; } // If our leader is dead or stops or attacks: // Attack any enemy in reaction range. // If don't set the goal, the unit can than choose a // better goal if moving nearer to enemy. //Wyrmgus start // if (unit.Type->CanAttack if (unit.CanAttack() //Wyrmgus end && (!goal || goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionStill)) { CUnit *target = AttackUnitsInReactRange(unit); if (target) { // Save current command to come back. COrder *savedOrder = NULL; if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = this->Clone(); } this->Finished = true; //Wyrmgus start // unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos)); unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos, target->MapLayer)); //Wyrmgus end if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } } } }
/* virtual */ void CAnimation_SpawnMissile::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); const int startx = ParseAnimInt(&unit, this->startXStr.c_str()); const int starty = ParseAnimInt(&unit, this->startYStr.c_str()); const int destx = ParseAnimInt(&unit, this->destXStr.c_str()); const int desty = ParseAnimInt(&unit, this->destYStr.c_str()); const int flags = ParseAnimFlags(unit, this->flagsStr.c_str()); const int offsetnum = ParseAnimInt(&unit, this->offsetNumStr.c_str()); const CUnit *goal = flags & ANIM_SM_RELTARGET ? unit.CurrentOrder()->GetGoal() : &unit; const int dir = ((goal->Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos moff = goal->Type->MissileOffsets[dir][!offsetnum ? 0 : offsetnum - 1]; PixelPos start; PixelPos dest; MissileType *mtype = MissileTypeByIdent(this->missileTypeStr); if (mtype == NULL) { return; } if (!goal || goal->Destroyed) { return; } if ((flags & ANIM_SM_PIXEL)) { start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + moff.x + startx; start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + moff.y + starty; } else { start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2 + moff.x; start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2 + moff.y; } if ((flags & ANIM_SM_TOTARGET)) { CUnit *target = goal->CurrentOrder()->GetGoal(); if (!target || target->Destroyed) { Assert(!mtype->AlwaysFire || mtype->Range); if (!target && mtype->AlwaysFire == false) { return; } } if (!target) { if (goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionAttackGround) { COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } else if (goal->CurrentAction() == UnitActionSpellCast) { COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } if (flags & ANIM_SM_PIXEL) { dest.x += destx; dest.y += desty; } else { dest.x += destx * PixelTileSize.x; dest.y += desty * PixelTileSize.y; } } else if (flags & ANIM_SM_PIXEL) { dest.x = target->GetMapPixelPosCenter().x + destx; dest.y = target->GetMapPixelPosCenter().y + desty; } else { dest.x = (target->tilePos.x + destx) * PixelTileSize.x; dest.y = (target->tilePos.y + desty) * PixelTileSize.y; dest += target->Type->GetPixelSize() / 2; } } else { if ((flags & ANIM_SM_PIXEL)) { dest.x = goal->GetMapPixelPosCenter().x + destx; dest.y = goal->GetMapPixelPosCenter().y + desty; } else { dest.x = (goal->tilePos.x + destx) * PixelTileSize.x; dest.y = (goal->tilePos.y + desty) * PixelTileSize.y; dest += goal->Type->GetPixelSize() / 2; } } Vec2i destTilePos = Map.MapPixelPosToTilePos(dest); const int dist = goal->MapDistanceTo(destTilePos); if ((flags & ANIM_SM_RANGED) && !(flags & ANIM_SM_PIXEL) && dist > goal->Stats->Variables[ATTACKRANGE_INDEX].Max && dist < goal->Type->MinAttackRange) { } else { Missile *missile = MakeMissile(*mtype, start, dest); if (flags & ANIM_SM_SETDIRECTION) { PixelPos posd; posd.x = Heading2X[goal->Direction / NextDirection]; posd.y = Heading2Y[goal->Direction / NextDirection]; missile->MissileNewHeadingFromXY(posd); } if (flags & ANIM_SM_DAMAGE) { missile->SourceUnit = &unit; } if (flags & ANIM_SM_TOTARGET) { missile->TargetUnit = goal->CurrentOrder()->GetGoal(); } } }