/** ** 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; }
/** ** Find a free position close to startPos ** ** @param transporter ** @param unit Unit to unload. ** @param startPos Original search position ** @param maxrange maximum range to unload. ** @param res Unload position. ** ** @return True if a position was found, False otherwise. ** @note res is undefined if a position is not found. ** ** @bug FIXME: Place unit only on fields reachable from the transporter */ static bool FindUnloadPosition(const CUnit &transporter, const CUnit &unit, const Vec2i startPos, int maxRange, Vec2i *res) { Vec2i pos = startPos; pos.x -= unit.Type->TileWidth - 1; pos.y -= unit.Type->TileHeight - 1; int addx = transporter.Type->TileWidth + unit.Type->TileWidth - 1; int addy = transporter.Type->TileHeight + unit.Type->TileHeight - 1; --pos.x; for (int range = 0; range < maxRange; ++range) { for (int i = addy; i--; ++pos.y) { if (UnitCanBeAt(unit, pos)) { *res = pos; return true; } } ++addx; for (int i = addx; i--; ++pos.x) { if (UnitCanBeAt(unit, pos)) { *res = pos; return true; } } ++addy; for (int i = addy; i--; --pos.y) { if (UnitCanBeAt(unit, pos)) { *res = pos; return true; } } ++addx; for (int i = addx; i--; --pos.x) { if (UnitCanBeAt(unit, pos)) { *res = pos; return true; } } ++addy; } return false; }
/** ** Create a unit and place it on the map ** ** @param l Lua state. ** ** @return Returns the slot number of the made unit. */ static int CclCreateUnit(lua_State *l) { LuaCheckArgs(l, 3); lua_pushvalue(l, 1); CUnitType *unittype = CclGetUnitType(l); if (unittype == NULL) { LuaError(l, "Bad unittype"); } lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 3); lua_pushvalue(l, 2); const int playerno = TriggerGetPlayer(l); lua_pop(l, 1); if (playerno == -1) { printf("CreateUnit: You cannot use \"any\" in create-unit, specify a player\n"); LuaError(l, "bad player"); return 0; } if (Players[playerno].Type == PlayerNobody) { printf("CreateUnit: player %d does not exist\n", playerno); LuaError(l, "bad player"); return 0; } CUnit *unit = MakeUnit(*unittype, &Players[playerno]); if (unit == NULL) { DebugPrint("Unable to allocate unit"); return 0; } else { if (UnitCanBeAt(*unit, ipos) || (unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } UpdateForNewUnit(*unit, 0); lua_pushnumber(l, UnitNumber(*unit)); return 1; } }
/** ** Return true if position is a correct place to drop out units. ** ** @param transporter Transporter unit. ** @param pos position to drop out units. */ static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos) { const int maxUnloadRange = 1; if (!UnitCanBeAt(transporter, pos)) { return false; } Vec2i dummyPos; CUnit *unit = transporter.UnitInside; for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) { if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos)) { return true; } } // Check unit can be droped from here. return false; }
/** ** Move a unit on map. ** ** @param l Lua state. ** ** @return Returns the slot number of the made placed. */ static int CclMoveUnit(lua_State *l) { LuaCheckArgs(l, 2); lua_pushvalue(l, 1); CUnit *unit = CclGetUnit(l); lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 2); if (UnitCanBeAt(*unit, ipos)) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } lua_pushvalue(l, 1); return 1; }
/** ** Returns the next element of a path. ** ** @param unit Unit that wants the path element. ** @param pxd Pointer for the x direction. ** @param pyd Pointer for the y direction. ** ** @pre The unit's field flags must have been unmarked ** on the map; see UnmarkUnitFieldFlags. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ int NextPathElement(CUnit *unit, int *pxd, int *pyd) { int result; // Please use UnmarkUnitFieldFlags and MarkUnitFieldFlags // around NextPathElement calls. Assert((Map.Field(unit->X, unit->Y)->Flags & unit->Type->FieldFlags) == 0); // Attempt to use path cache // FIXME: If there is a goal, it may have moved, ruining the cache *pxd = 0; *pyd = 0; // Goal has moved, need to recalculate path or no cached path if (unit->Data.Move.Length <= 0 || (unit->Orders[0]->Goal && (unit->Orders[0]->Goal->X != unit->Orders[0]->X || unit->Orders[0]->Goal->Y != unit->Orders[0]->Y))) { result = NewPath(unit); if (result == PF_UNREACHABLE) { unit->Data.Move.Length = 0; return result; } if (result == PF_REACHED) { return result; } if (unit->Goal) { // Update Orders unit->Orders[0]->X = unit->Goal->X; unit->Orders[0]->Y = unit->Goal->Y; } } *pxd = Heading2X[(int)unit->Data.Move.Path[(int)unit->Data.Move.Length - 1]]; *pyd = Heading2Y[(int)unit->Data.Move.Path[(int)unit->Data.Move.Length - 1]]; result = unit->Data.Move.Length; unit->Data.Move.Length--; if (!UnitCanBeAt(unit, *pxd + unit->X, *pyd + unit->Y)) { // If obstructing unit is moving, wait for a bit. if (unit->Data.Move.Fast) { unit->Data.Move.Fast--; AstarDebugPrint("WAIT at %d\n" _C_ unit->Data.Move.Fast); result = PF_WAIT; } else { unit->Data.Move.Fast = 10; AstarDebugPrint("SET WAIT to 10\n"); result = PF_WAIT; } if (unit->Data.Move.Fast == 0 && result != 0) { AstarDebugPrint("WAIT expired\n"); result = NewPath(unit); if (result > 0) { *pxd = Heading2X[(int)unit->Data.Move.Path[(int)unit->Data.Move.Length - 1]]; *pyd = Heading2Y[(int)unit->Data.Move.Path[(int)unit->Data.Move.Length - 1]]; if (!UnitCanBeAt(unit, *pxd + unit->X, *pyd + unit->Y)) { // There may be unit in the way, Astar may allow you to walk onto it. result = PF_UNREACHABLE; *pxd = 0; *pyd = 0; } else { result = unit->Data.Move.Length; unit->Data.Move.Length--; } } } } if (result != PF_WAIT) { unit->Data.Move.Fast = 0; } return result; }
/** ** Returns the next element of a path. ** ** @param unit Unit that wants the path element. ** @param pxd Pointer for the x direction. ** @param pyd Pointer for the y direction. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ int NextPathElement(CUnit &unit, short int *pxd, short int *pyd) { PathFinderInput &input = unit.pathFinderData->input; PathFinderOutput &output = unit.pathFinderData->output; unit.CurrentOrder()->UpdatePathFinderData(input); // Attempt to use path cache // FIXME: If there is a goal, it may have moved, ruining the cache *pxd = 0; *pyd = 0; // Goal has moved, need to recalculate path or no cached path if (output.Length <= 0 || input.IsRecalculateNeeded()) { const int result = NewPath(input, output); if (result == PF_UNREACHABLE) { output.Length = 0; return result; } if (result == PF_REACHED) { return result; } } *pxd = Heading2X[(int)output.Path[(int)output.Length - 1]]; *pyd = Heading2Y[(int)output.Path[(int)output.Length - 1]]; const Vec2i dir(*pxd, *pyd); int result = output.Length; output.Length--; if (!UnitCanBeAt(unit, unit.tilePos + dir)) { // If obstructing unit is moving, wait for a bit. if (output.Fast) { output.Fast--; AstarDebugPrint("WAIT at %d\n" _C_ output.Fast); result = PF_WAIT; } else { output.Fast = 10; AstarDebugPrint("SET WAIT to 10\n"); result = PF_WAIT; } if (output.Fast == 0 && result != 0) { AstarDebugPrint("WAIT expired\n"); result = NewPath(input, output); if (result > 0) { *pxd = Heading2X[(int)output.Path[(int)output.Length - 1]]; *pyd = Heading2Y[(int)output.Path[(int)output.Length - 1]]; if (!UnitCanBeAt(unit, unit.tilePos + dir)) { // There may be unit in the way, Astar may allow you to walk onto it. result = PF_UNREACHABLE; *pxd = 0; *pyd = 0; } else { result = output.Length; output.Length--; } } } } if (result != PF_WAIT) { output.Fast = 0; } return result; }
/** ** 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; }