/** ** Can the unit 'src' reach the place x,y. ** ** @param src Unit for the path. ** @param x Map X tile position. ** @param y Map Y tile position. ** @param w Width of Goal ** @param h Height of Goal ** @param minrange min range to the tile ** @param range Range to the tile. ** ** @pre The unit's field flags must have been marked ** on the map; see MarkUnitFieldFlags. ** ** @return Distance to place. */ int PlaceReachable(const CUnit *src, int x, int y, int w, int h, int minrange, int range) { // Please do not use UnmarkUnitFieldFlags and MarkUnitFieldFlags // around PlaceReachable calls. Assert((Map.Field(src->X, src->Y)->Flags & src->Type->FieldFlags) != 0); UnmarkUnitFieldFlags(src); int i = AStarFindPath(src->X, src->Y, x, y, w, h, src->Type->TileWidth, src->Type->TileHeight, minrange, range, NULL, 0, (void *)src); MarkUnitFieldFlags(src); switch (i) { case PF_FAILED: case PF_UNREACHABLE: case PF_REACHED: i = 0; break; case PF_WAIT: Assert(0); i = 0; break; case PF_MOVE: break; default: break; } return i; }
/** ** Move in a random direction ** ** @return true if the unit moves, false otherwise */ static bool MoveRandomly(CUnit &unit) { if (unit.Type->RandomMovementProbability == false || ((SyncRand() % 100) > unit.Type->RandomMovementProbability)) { return false; } // pick random location Vec2i pos = unit.tilePos; pos.x += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; pos.y += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; // restrict to map Map.Clamp(pos); // move if possible if (pos != unit.tilePos) { UnmarkUnitFieldFlags(unit); if (UnitCanBeAt(unit, pos)) { MarkUnitFieldFlags(unit); CommandMove(unit, pos, FlushCommands); return true; } MarkUnitFieldFlags(unit); } return false; }
/** ** Can build unit-type at this point. ** ** @param unit Worker that want to build the building or NULL. ** @param type Building unit-type. ** @param pos tile map position. ** @param real Really build, or just placement ** ** @return OnTop, parent unit, builder on true, NULL false. ** */ CUnit *CanBuildUnitType(const CUnit *unit, const CUnitType &type, const Vec2i &pos, int real) { // Terrain Flags don't matter if building on top of a unit. CUnit *ontop = CanBuildHere(unit, type, pos); if (ontop == NULL) { return NULL; } if (ontop != (CUnit *)1 && ontop != unit) { return ontop; } // Remove unit that is building! if (unit) { UnmarkUnitFieldFlags(*unit); } CPlayer *player = NULL; if (unit && unit->Player->Type == PlayerPerson) { player = unit->Player; } int testmask; unsigned int index = pos.y * Map.Info.MapWidth; for (int h = 0; h < type.TileHeight; ++h) { for (int w = type.TileWidth; w--;) { /* first part of if (!CanBuildOn(x + w, y + h, testmask)) */ if (!Map.Info.IsPointOnMap(pos.x + w, pos.y + h)) { h = type.TileHeight; ontop = NULL; break; } if (player && !real) { //testmask = MapFogFilterFlags(player, x + w, y + h, type.MovementMask); testmask = MapFogFilterFlags(*player, index + pos.x + w, type.MovementMask); } else { testmask = type.MovementMask; } /*secound part of if (!CanBuildOn(x + w, y + h, testmask)) */ const CMapField &mf = *Map.Field(index + pos.x + w); if (mf.CheckMask(testmask)) { h = type.TileHeight; ontop = NULL; break; } if (player && !mf.playerInfo.IsExplored(*player)) { h = type.TileHeight; ontop = NULL; break; } } index += Map.Info.MapWidth; } if (unit) { MarkUnitFieldFlags(*unit); } // We can build here: check distance to gold mine/oil patch! return ontop; }
inline void operator()(CUnit *const dest) { /* Only resource depots */ if (dest->Type->CanStore[resource] && dest->IsAliveOnMap() && dest->CurrentAction() != UnitActionBuilt) { // Unit in range? if (NEARLOCATION) { int d = dest->MapDistanceTo(u_near.loc); // // Take this depot? // if (d <= range && d < best_dist) { best_depot = dest; best_dist = d; } } else { int d; const CUnit *worker = u_near.worker; if (!worker->Container) { d = worker->MapDistanceTo(*dest); } else { d = worker->Container->MapDistanceTo(*dest); } // Use Circle, not square :) if (d > range) { return; } if (best_dist == INT_MAX) { best_depot = dest; } // calck real travel distance if (worker->Container) { UnmarkUnitFieldFlags(*worker->Container); } d = UnitReachable(*worker, *dest, 1); if (worker->Container) { MarkUnitFieldFlags(*worker->Container); } // // Take this depot? // if (d && d < best_dist) { best_depot = dest; best_dist = d; } } } }
/** ** Unit moves! Generic function called from other actions. ** ** @param unit Pointer to unit. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ int DoActionMove(CUnit &unit) { Vec2i posd; // movement in tile. int d; Assert(unit.CanMove()); if (!unit.Moving && (unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) { Assert(!unit.Anim.Unbreakable); // FIXME: So units flying up and down are not affected. unit.IX = 0; unit.IY = 0; UnmarkUnitFieldFlags(unit); d = NextPathElement(unit, &posd.x, &posd.y); MarkUnitFieldFlags(unit); switch (d) { case PF_UNREACHABLE: // Can't reach, stop if (unit.Player->AiEnabled) { AiCanNotMove(unit); } unit.Moving = 0; return d; case PF_REACHED: // Reached goal, stop unit.Moving = 0; return d; case PF_WAIT: // No path, wait // Reset frame to still frame while we wait // FIXME: Unit doesn't animate. unit.Frame = unit.Type->StillFrame; UnitUpdateHeading(unit); unit.Wait = 10; unit.Moving = 0; return d; default: // On the way moving unit.Moving = 1; break; } if (unit.Type->UnitType == UnitTypeNaval) { // Boat (un)docking? const CMapField &mf_cur = *Map.Field(unit.Offset); const CMapField &mf_next = *Map.Field(unit.tilePos + posd); if (mf_cur.WaterOnMap() && mf_next.CoastOnMap()) { PlayUnitSound(unit, VoiceDocking); } else if (mf_cur.CoastOnMap() && mf_next.WaterOnMap()) { PlayUnitSound(unit, VoiceDocking); // undocking } } Vec2i pos = unit.tilePos + posd; unit.MoveToXY(pos); // Remove unit from the current selection if (unit.Selected && !Map.Field(pos)->playerInfo.IsTeamVisible(*ThisPlayer)) { if (NumSelected == 1) { // Remove building cursor CancelBuildingMode(); } if (!ReplayRevealMap) { UnSelectUnit(unit); SelectionChanged(); } } unit.IX = -posd.x * PixelTileSize.x; unit.IY = -posd.y * PixelTileSize.y; unit.Frame = unit.Type->StillFrame; UnitHeadingFromDeltaXY(unit, posd); } else { posd.x = Heading2X[unit.Direction / NextDirection]; posd.y = Heading2Y[unit.Direction / NextDirection]; d = unit.pathFinderData->output.Length + 1; } unit.pathFinderData->output.Cycles++;//reset have to be manualy controled by caller. int move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move, Map.Field(unit.Offset)->Cost); unit.IX += posd.x * move; unit.IY += posd.y * move; // Finished move animation, set Moving to 0 so we recalculate the path // next frame // FIXME: this is broken for subtile movement if (!unit.Anim.Unbreakable && !unit.IX && !unit.IY) { unit.Moving = 0; } return d; }
/** ** Move in a random direction ** ** @return true if the unit moves, false otherwise */ static bool MoveRandomly(CUnit &unit) { if (!unit.Type->RandomMovementProbability || SyncRand(100) > unit.Type->RandomMovementProbability) { return false; } // pick random location Vec2i pos = unit.tilePos; pos.x += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; pos.y += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; // restrict to map Map.Clamp(pos, unit.MapLayer->ID); // move if possible if (pos != unit.tilePos) { UnmarkUnitFieldFlags(unit); if (UnitCanBeAt(unit, pos, unit.MapLayer->ID)) { MarkUnitFieldFlags(unit); //Wyrmgus start //prefer terrains which this unit's species is native to; only go to other ones if is already in a non-native terrain type if (unit.Type->Species && std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(unit.tilePos, false, unit.MapLayer->ID)) != unit.Type->Species->Terrains.end()) { if (std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(pos, false, unit.MapLayer->ID)) == unit.Type->Species->Terrains.end()) { return false; } } if (unit.Type->BoolFlag[PEOPLEAVERSION_INDEX].value) { std::vector<CUnit *> table; SelectAroundUnit(unit, std::max(6, unit.Type->RandomMovementDistance), table, HasNotSamePlayerAs(*unit.Player)); if (!table.size()) { //only avoid going near a settled area if isn't already surrounded by civilizations' units //don't go near settled areas Vec2i minpos = pos; Vec2i maxpos = pos; minpos.x = pos.x - std::max(6, unit.Type->RandomMovementDistance); minpos.y = pos.y - std::max(6, unit.Type->RandomMovementDistance); maxpos.x = pos.x + std::max(6, unit.Type->RandomMovementDistance); maxpos.y = pos.y + std::max(6, unit.Type->RandomMovementDistance); std::vector<CUnit *> second_table; Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player)); if (second_table.size() > 0) { return false; } } else { //even if is already in a settled area, don't go to places adjacent to units owned by players other than the neutral player Vec2i minpos = pos; Vec2i maxpos = pos; minpos.x = pos.x - 1; minpos.y = pos.y - 1; maxpos.x = pos.x + 1; maxpos.y = pos.y + 1; std::vector<CUnit *> second_table; Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player)); if (second_table.size() > 0) { return false; } } } CommandMove(unit, pos, FlushCommands, unit.MapLayer->ID); return true; } MarkUnitFieldFlags(unit); } return false; }