diff --git a/data/RTTR/campaigns/roman/MISS200.lua b/data/RTTR/campaigns/roman/MISS200.lua index 64ea3b4cb5..737ca24235 100644 --- a/data/RTTR/campaigns/roman/MISS200.lua +++ b/data/RTTR/campaigns/roman/MISS200.lua @@ -175,6 +175,10 @@ rttr:RegisterTranslations( }, }) +function getNumPlayers() + return 1 +end + -- format mission texts -- BUG: NewLine at the end is wrongly interpreted, adding 2x Space resolves this issue function MissionText(e) @@ -205,13 +209,6 @@ function onSettingsReady() rttr:GetPlayer(0):SetNation(NAT_ROMANS) -- nation rttr:GetPlayer(0):SetColor(0) -- 0:blue, 1:red, 2:yellow, - - rttr:GetPlayer(1):Close() - rttr:GetPlayer(2):Close() - rttr:GetPlayer(3):Close() - rttr:GetPlayer(4):Close() - rttr:GetPlayer(5):Close() - rttr:GetPlayer(6):Close() end function getAllowedChanges() diff --git a/data/RTTR/campaigns/roman/MISS206.lua b/data/RTTR/campaigns/roman/MISS206.lua index 7e878824db..d050eda176 100644 --- a/data/RTTR/campaigns/roman/MISS206.lua +++ b/data/RTTR/campaigns/roman/MISS206.lua @@ -189,8 +189,12 @@ function addPlayerBld(p, onLoad) rttr:GetPlayer(p):DisableBuilding(BLD_SHIPYARD, false) rttr:GetPlayer(p):DisableBuilding(BLD_HARBORBUILDING, false) - if(p == 2) then - if onLoad then return end + if onLoad then return end + + if(p == 1) then + rttr:GetPlayer(p):PlaceHQ(112, 82) -- !SET_HOUSE 24, 112, 82 + elseif(p == 2) then + rttr:GetPlayer(p):PlaceHQ(40, 54) -- !SET_HOUSE 24, 40, 54 rttr:GetPlayer(p):AIConstructionOrder(43, 59, BLD_FORTRESS) end end diff --git a/doc/lua/functions.md b/doc/lua/functions.md index e142d2eb26..b7b65ba3ec 100644 --- a/doc/lua/functions.md +++ b/doc/lua/functions.md @@ -343,6 +343,10 @@ Ignored if the current player is not controlled by AI. **GetHQPos()** Return x,y of the players HQ +**PlaceHQ(x, y)** +Places this player's headquarters at map location {x, y}. +Does nothing if the player already has a valid headquarters position (e.g. set by the map or using a previous PlaceHQ call). + **Surrender(destroyBuildings)** Let the player give up either keeping its buildings or destroying them. Can be called multiple times e.g. to destroy buildings later. diff --git a/libs/s25main/GameLobby.cpp b/libs/s25main/GameLobby.cpp index e1f3496b5b..11be3ae5dc 100644 --- a/libs/s25main/GameLobby.cpp +++ b/libs/s25main/GameLobby.cpp @@ -24,3 +24,8 @@ unsigned GameLobby::getNumPlayers() const { return players_.size(); } + +void GameLobby::setNumPlayers(unsigned num) +{ + players_.resize(num); +} diff --git a/libs/s25main/GameLobby.h b/libs/s25main/GameLobby.h index 171152d721..d97cf01e67 100644 --- a/libs/s25main/GameLobby.h +++ b/libs/s25main/GameLobby.h @@ -21,6 +21,7 @@ class GameLobby const JoinPlayerInfo& getPlayer(unsigned playerId) const; const std::vector& getPlayers() const { return players_; } unsigned getNumPlayers() const; + void setNumPlayers(unsigned num); GlobalGameSettings& getSettings() { return ggs_; } const GlobalGameSettings& getSettings() const { return ggs_; } diff --git a/libs/s25main/GamePlayer.cpp b/libs/s25main/GamePlayer.cpp index d00de03b98..39fb077a05 100644 --- a/libs/s25main/GamePlayer.cpp +++ b/libs/s25main/GamePlayer.cpp @@ -14,6 +14,7 @@ #include "WineLoader.h" #include "addons/const_addons.h" #include "buildings/noBuildingSite.h" +#include "buildings/nobHQ.h" #include "buildings/nobHarborBuilding.h" #include "buildings/nobMilitary.h" #include "buildings/nobUsual.h" @@ -411,6 +412,19 @@ void GamePlayer::RemoveBuildingSite(noBuildingSite* bldSite) buildings.Remove(bldSite); } +bool GamePlayer::IsHQTent() const +{ + if(const nobHQ* hq = GetHQ()) + return hq->IsTent(); + return false; +} + +void GamePlayer::SetHQIsTent(bool isTent) +{ + if(nobHQ* hq = const_cast(GetHQ())) + hq->SetIsTent(isTent); +} + void GamePlayer::AddBuilding(noBuilding* bld, BuildingType bldType) { RTTR_Assert(bld->GetPlayer() == GetPlayerId()); @@ -1400,6 +1414,12 @@ void GamePlayer::TestDefeat() Surrender(); } +const nobHQ* GamePlayer::GetHQ() const +{ + const MapPoint& hqPos = GetHQPos(); + return hqPos.isValid() ? GetGameWorld().GetSpecObj(hqPos) : nullptr; +} + void GamePlayer::Surrender() { if(isDefeated) diff --git a/libs/s25main/GamePlayer.h b/libs/s25main/GamePlayer.h index 59ee843473..3f71b505bd 100644 --- a/libs/s25main/GamePlayer.h +++ b/libs/s25main/GamePlayer.h @@ -31,6 +31,7 @@ class noShip; class nobBaseMilitary; class nobBaseWarehouse; class nobHarborBuilding; +class nobHQ; class nobMilitary; class nofCarrier; class nofFlagWorker; @@ -91,6 +92,9 @@ class GamePlayer : public GamePlayerInfo const GameWorld& GetGameWorld() const { return world; } const MapPoint& GetHQPos() const { return hqPos; } + bool IsHQTent() const; + void SetHQIsTent(bool isTent); + void AddBuilding(noBuilding* bld, BuildingType bldType); void RemoveBuilding(noBuilding* bld, BuildingType bldType); void AddBuildingSite(noBuildingSite* bldSite); @@ -426,6 +430,7 @@ class GamePlayer : public GamePlayerInfo bool FindWarehouseForJob(Job job, noRoadNode* goal) const; /// Prüft, ob der Spieler besiegt wurde void TestDefeat(); + const nobHQ* GetHQ() const; ////////////////////////////////////////////////////////////////////////// /// Unsynchronized state (e.g. lua, gui...) diff --git a/libs/s25main/desktops/dskGameLobby.cpp b/libs/s25main/desktops/dskGameLobby.cpp index 10ac875659..bd3392a9cf 100644 --- a/libs/s25main/desktops/dskGameLobby.cpp +++ b/libs/s25main/desktops/dskGameLobby.cpp @@ -32,6 +32,7 @@ #include "ingameWindows/iwMsgbox.h" #include "lua/LuaInterfaceSettings.h" #include "network/GameClient.h" +#include "network/GameServer.h" #include "ogl/FontStyle.h" #include "gameData/GameConsts.h" #include "gameData/const_gui_ids.h" @@ -102,6 +103,13 @@ dskGameLobby::dskGameLobby(ServerType serverType, std::shared_ptr gam RTTR_Assert(gameLobby_->isHost()); LOG.write(_("Lua was disabled by the script itself\n")); lua.reset(); + } else + { + if(const auto num = lua->GetNumPlayersFromScript()) + { + GAMESERVER.SetNumPlayers(num); + gameLobby_->setNumPlayers(num); + } } if(!lua && gameLobby_->isHost()) lobbyController->RemoveLuaScript(); diff --git a/libs/s25main/lua/LuaInterfaceSettings.cpp b/libs/s25main/lua/LuaInterfaceSettings.cpp index 8f29d5658f..5b24778a53 100644 --- a/libs/s25main/lua/LuaInterfaceSettings.cpp +++ b/libs/s25main/lua/LuaInterfaceSettings.cpp @@ -247,3 +247,13 @@ bool LuaInterfaceSettings::IsMapPreviewEnabled() } return true; } + +unsigned LuaInterfaceSettings::GetNumPlayersFromScript() +{ + kaguya::LuaRef func = lua["getNumPlayers"]; + if(func.type() == LUA_TFUNCTION) + { + return func.call(); + } + return 0; +} diff --git a/libs/s25main/lua/LuaInterfaceSettings.h b/libs/s25main/lua/LuaInterfaceSettings.h index a577a3e1ed..931f50c91e 100644 --- a/libs/s25main/lua/LuaInterfaceSettings.h +++ b/libs/s25main/lua/LuaInterfaceSettings.h @@ -36,6 +36,7 @@ class LuaInterfaceSettings : public LuaInterfaceGameBase /// Get addons that are allowed to be changed std::vector GetAllowedAddons(); bool IsMapPreviewEnabled(); + unsigned GetNumPlayersFromScript(); private: IGameLobbyController& lobbyServerController_; diff --git a/libs/s25main/lua/LuaPlayer.cpp b/libs/s25main/lua/LuaPlayer.cpp index 3dd5158883..7984ca2ee7 100644 --- a/libs/s25main/lua/LuaPlayer.cpp +++ b/libs/s25main/lua/LuaPlayer.cpp @@ -9,6 +9,7 @@ #include "ai/AIPlayer.h" #include "buildings/nobBaseWarehouse.h" #include "buildings/nobHQ.h" +#include "factories/BuildingFactory.h" #include "helpers/EnumRange.h" #include "helpers/toString.h" #include "lua/LuaHelpers.h" @@ -46,6 +47,7 @@ void LuaPlayer::Register(kaguya::State& state) .addFunction("GetNumPeople", &LuaPlayer::GetNumPeople) .addFunction("GetStatisticsValue", &LuaPlayer::GetStatisticsValue) .addFunction("AIConstructionOrder", &LuaPlayer::AIConstructionOrder) + .addFunction("PlaceHQ", &LuaPlayer::PlaceHQ) .addFunction("ModifyHQ", &LuaPlayer::ModifyHQ) .addFunction("GetHQPos", &LuaPlayer::GetHQPos) .addFunction("IsDefeated", &LuaPlayer::IsDefeated) @@ -259,15 +261,22 @@ bool LuaPlayer::AIConstructionOrder(unsigned x, unsigned y, lua::SafeEnum(hqPos); - if(hq) - hq->SetIsTent(isTent); - } + player.SetHQIsTent(isTent); } bool LuaPlayer::IsDefeated() const diff --git a/libs/s25main/lua/LuaPlayer.h b/libs/s25main/lua/LuaPlayer.h index 3335084a51..f15aa6bf78 100644 --- a/libs/s25main/lua/LuaPlayer.h +++ b/libs/s25main/lua/LuaPlayer.h @@ -10,6 +10,7 @@ #include "gameTypes/BuildingType.h" #include "gameTypes/GoodTypes.h" #include "gameTypes/JobTypes.h" +#include "gameTypes/MapCoordinates.h" #include "gameTypes/PactTypes.h" #include "gameTypes/StatisticTypes.h" #include @@ -50,6 +51,7 @@ class LuaPlayer : public LuaPlayerBase unsigned GetNumPeople(lua::SafeEnum job) const; unsigned GetStatisticsValue(lua::SafeEnum stat) const; bool AIConstructionOrder(unsigned x, unsigned y, lua::SafeEnum bld); + void PlaceHQ(MapCoord x, MapCoord y); void ModifyHQ(bool isTent); bool IsDefeated() const; void Surrender(bool destroyBlds); diff --git a/libs/s25main/network/GameClient.cpp b/libs/s25main/network/GameClient.cpp index f6bf470891..504c086dce 100644 --- a/libs/s25main/network/GameClient.cpp +++ b/libs/s25main/network/GameClient.cpp @@ -323,8 +323,10 @@ void GameClient::StartGame(const unsigned random_init) gameWorld.GetPlayer(i).MakeStartPacts(); MapLoader loader(gameWorld); - if(!loader.Load(mapinfo.filepath) - || (!mapinfo.luaFilepath.empty() && !loader.LoadLuaScript(*game, *this, mapinfo.luaFilepath))) + if((!mapinfo.luaFilepath.empty() && !loader.LoadLuaScript(*game, *this, mapinfo.luaFilepath)) + || !loader.Load(mapinfo.filepath)) // Do not reorder: load lua first, load map second. + // If the map is loaded first and it does not have a player HQ set, it + // will not load correctly, even though the HQ may be set using LUA. { OnError(ClientError::InvalidMap); return; diff --git a/libs/s25main/network/GameServer.cpp b/libs/s25main/network/GameServer.cpp index 4605f8a1c9..67dbea9263 100644 --- a/libs/s25main/network/GameServer.cpp +++ b/libs/s25main/network/GameServer.cpp @@ -526,6 +526,11 @@ bool GameServer::assignPlayersOfRandomTeams(std::vector& playerI return playerWasAssigned; } +void GameServer::SetNumPlayers(unsigned num) +{ + playerInfos.resize(num); +} + /** * startet das Spiel. */ diff --git a/libs/s25main/network/GameServer.h b/libs/s25main/network/GameServer.h index d4e921a7fd..cf8d3ea735 100644 --- a/libs/s25main/network/GameServer.h +++ b/libs/s25main/network/GameServer.h @@ -52,6 +52,8 @@ class GameServer : /// Assign players that do not have a fixed team, return true if any player was assigned. static bool assignPlayersOfRandomTeams(std::vector& playerInfos); + void SetNumPlayers(unsigned num); + private: bool StartGame(); diff --git a/libs/s25main/world/MapLoader.cpp b/libs/s25main/world/MapLoader.cpp index b3b60b0dcb..746f2a42d4 100644 --- a/libs/s25main/world/MapLoader.cpp +++ b/libs/s25main/world/MapLoader.cpp @@ -409,8 +409,9 @@ bool MapLoader::PlaceHQs(GameWorldBase& world, std::vector hqPositions // Does the HQ have a position? if(i >= hqPositions.size() || !hqPositions[i].isValid()) { - LOG.write(_("Player %u does not have a valid start position!")) % i; - return false; + LOG.write(_("Player %u does not have a valid start position!\n")) % i; + if(!world.HasLua()) // HQ can be placed in the script, so don't signal error + return false; } BuildingFactory::CreateBuilding(world, BuildingType::Headquarters, hqPositions[i], i,