Index: ps/trunk/binaries/data/config/default.cfg =================================================================== --- ps/trunk/binaries/data/config/default.cfg +++ ps/trunk/binaries/data/config/default.cfg @@ -348,6 +348,7 @@ enabletips = true ; Enable/Disable tips during gamesetup (for newcomers) assignplayers = everyone ; Whether to assign joining clients to free playerslots. Possible values: everyone, buddies, disabled. aidifficulty = 3 ; Difficulty level, from 0 (easiest) to 5 (hardest) +aibehavior = "random" ; Default behavior of the AI (random, generalist, aggressive or defensive) [gui.session] camerajump.threshold = 40 ; How close do we have to be to the actual location in order to jump back to the previous one? Index: ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js +++ ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js @@ -1,6 +1,11 @@ +var g_AIBehaviorList = [{ + "Name": "random", + "Title": translateWithContext("AI Behavior", "Random") +}].concat(g_Settings.AIBehaviors); + var g_PlayerSlot; -const g_AIDescriptions = [{ +var g_AIDescriptions = [{ "id": "", "data": { "name": translateWithContext("ai", "None"), @@ -8,28 +13,37 @@ } }].concat(g_Settings.AIDescriptions); +var g_AIControls = { + "aiSelection": { + "labels": g_AIDescriptions.map(ai => ai.data.name), + "selected": settings => g_AIDescriptions.findIndex(ai => ai.id == settings.id) + }, + "aiDifficulty": { + "labels": prepareForDropdown(g_Settings.AIDifficulties).Title, + "selected": settings => settings.difficulty + }, + "aiBehavior": { + "labels": prepareForDropdown(g_AIBehaviorList).Title, + "selected": settings => g_AIBehaviorList.findIndex(b => b.Name == settings.behavior) + } +}; + function init(settings) { // Remember the player ID that we change the AI settings for g_PlayerSlot = settings.playerSlot; - let aiSelection = Engine.GetGUIObjectByName("aiSelection"); - aiSelection.list = g_AIDescriptions.map(ai => ai.data.name); - aiSelection.selected = g_AIDescriptions.findIndex(ai => ai.id == settings.id); - aiSelection.hidden = !settings.isController; - - let aiSelectionText = Engine.GetGUIObjectByName("aiSelectionText"); - aiSelectionText.caption = aiSelection.list[aiSelection.selected]; - aiSelectionText.hidden = settings.isController; - - let aiDiff = Engine.GetGUIObjectByName("aiDifficulty"); - aiDiff.list = prepareForDropdown(g_Settings.AIDifficulties).Title; - aiDiff.selected = settings.difficulty; - aiDiff.hidden = !settings.isController; - - let aiDiffText = Engine.GetGUIObjectByName("aiDifficultyText"); - aiDiffText.caption = aiDiff.list[aiDiff.selected]; - aiDiffText.hidden = settings.isController; + for (let name in g_AIControls) + { + let control = Engine.GetGUIObjectByName(name); + control.list = g_AIControls[name].labels; + control.selected = g_AIControls[name].selected(settings); + control.hidden = !settings.isController; + + let label = Engine.GetGUIObjectByName(name + "Text"); + label.caption = control.list[control.selected]; + label.hidden = settings.isController; + } } function selectAI(idx) @@ -48,6 +62,7 @@ "id": g_AIDescriptions[idx].id, "name": g_AIDescriptions[idx].data.name, "difficulty": Engine.GetGUIObjectByName("aiDifficulty").selected, + "behavior": g_AIBehaviorList[Engine.GetGUIObjectByName("aiBehavior").selected].Name, "playerSlot": g_PlayerSlot }); } Index: ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.xml +++ ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.xml @@ -8,7 +8,7 @@ - + AI Configuration @@ -31,9 +31,17 @@ + + + AI Behavior: + + + + + - + Cancel Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js +++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js @@ -102,19 +102,19 @@ { if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu - playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s)"); + playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIbehavior)s %(AIname)s)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu - playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s, %(state)s)"); + playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIbehavior)s %(AIname)s, %(state)s)"); } else { if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu - playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIname)s)"); + playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIbehavior)s %(AIname)s)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu - playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIname)s, %(state)s)"); + playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIbehavior)s %(AIname)s, %(state)s)"); } } else @@ -174,7 +174,8 @@ translateWithContext("playerstate", "won"), "AIname": isAI ? translateAIName(playerData.AI) : "", - "AIdifficulty": isAI ? translateAIDifficulty(playerData.AIDiff) : "" + "AIdifficulty": isAI ? translateAIDifficulty(playerData.AIDiff) : "", + "AIbehavior": isAI ? translateAIBehavior(playerData.AIBehavior) : "" })); } Index: ps/trunk/binaries/data/mods/public/gui/common/settings.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/settings.js +++ ps/trunk/binaries/data/mods/public/gui/common/settings.js @@ -37,6 +37,7 @@ var settings = { "AIDescriptions": loadAIDescriptions(), "AIDifficulties": loadAIDifficulties(), + "AIBehaviors": loadAIBehaviors(), "Ceasefire": loadCeasefire(), "VictoryDurations": loadVictoryDuration(), "GameSpeeds": loadSettingValuesFile("game_speeds.json"), @@ -138,6 +139,25 @@ ]; } +function loadAIBehaviors() +{ + return [ + { + "Name": "generalist", + "Title": translateWithContext("aiBehavior", "Generalist"), + "Default": true + }, + { + "Name": "defensive", + "Title": translateWithContext("aiBehavior", "Defensive") + }, + { + "Name": "aggressive", + "Title": translateWithContext("aiBehavior", "Aggressive") + } + ]; +} + /** * Loads available victory times for victory conditions like Wonder and Capture the Relic. */ @@ -359,6 +379,17 @@ /** * Returns title or placeholder. * + * @param {string} aiBehavior - for example "defensive" + */ +function translateAIBehavior(aiBehavior) +{ + let behavior = g_Settings.AIBehaviors.find(b => b.Name == aiBehavior); + return behavior ? behavior.Title : translateWithContext("AI behavior", "Default"); +} + +/** + * Returns title or placeholder. + * * @param {string} mapType - for example "skirmish" * @returns {string} */ Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -955,9 +955,10 @@ "onPress": (playerIdx) => function() { openAIConfig(playerIdx); }, - "tooltip": (playerIdx) => sprintf(translate("Configure AI: %(name)s - %(difficulty)s."), { + "tooltip": (playerIdx) => sprintf(translate("Configure AI: %(difficulty)s %(behavior)s %(name)s."), { "name": translateAIName(g_GameAttributes.settings.PlayerData[playerIdx].AI), - "difficulty": translateAIDifficulty(g_GameAttributes.settings.PlayerData[playerIdx].AIDiff) + "difficulty": translateAIDifficulty(g_GameAttributes.settings.PlayerData[playerIdx].AIDiff), + "behavior": translateAIBehavior(g_GameAttributes.settings.PlayerData[playerIdx].AIBehavior), }), }, }; @@ -1016,6 +1017,7 @@ g_DefaultPlayerData = clone(g_Settings.PlayerDefaults.slice(1)); let aiDifficulty = +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"); + let aiBehavior = Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"); // Don't change the underlying defaults file, as Atlas uses that file too for (let i in g_DefaultPlayerData) @@ -1023,6 +1025,7 @@ g_DefaultPlayerData[i].Civ = "random"; g_DefaultPlayerData[i].Team = -1; g_DefaultPlayerData[i].AIDiff = aiDifficulty; + g_DefaultPlayerData[i].AIBehavior = aiBehavior; } deepfreeze(g_DefaultPlayerData); @@ -1924,10 +1927,13 @@ } g_GameAttributes.settings.PlayerData[i].Civ = chosenCiv; - // Pick one of the available botnames for the chosen civ + // Pick a random behavior and one of the available botnames for the chosen civ if (g_GameAttributes.mapType === "scenario" || !g_GameAttributes.settings.PlayerData[i].AI) continue; + if (g_GameAttributes.settings.PlayerData[i].AIBehavior == "random") + g_GameAttributes.settings.PlayerData[i].AIBehavior = pickRandom(g_Settings.AIBehaviors).Name; + let chosenName = pickRandom(g_CivData[chosenCiv].AINames); if (!g_IsNetworked) @@ -2105,7 +2111,8 @@ "isController": g_IsController, "playerSlot": playerSlot, "id": g_GameAttributes.settings.PlayerData[playerSlot].AI, - "difficulty": g_GameAttributes.settings.PlayerData[playerSlot].AIDiff + "difficulty": g_GameAttributes.settings.PlayerData[playerSlot].AIDiff, + "behavior": g_GameAttributes.settings.PlayerData[playerSlot].AIBehavior }); } @@ -2121,6 +2128,7 @@ g_GameAttributes.settings.PlayerData[ai.playerSlot].AI = ai.id; g_GameAttributes.settings.PlayerData[ai.playerSlot].AIDiff = ai.difficulty; + g_GameAttributes.settings.PlayerData[ai.playerSlot].AIBehavior = ai.behavior; updateGameAttributes(); } @@ -2180,6 +2188,7 @@ // Transfer the AI from the target slot to the current slot. g_GameAttributes.settings.PlayerData[playerID - 1].AI = g_GameAttributes.settings.PlayerData[newSlot].AI; g_GameAttributes.settings.PlayerData[playerID - 1].AIDiff = g_GameAttributes.settings.PlayerData[newSlot].AIDiff; + g_GameAttributes.settings.PlayerData[playerID - 1].AIBehavior = g_GameAttributes.settings.PlayerData[newSlot].AIBehavior; // Swap civilizations and colors if they aren't fixed if (g_GameAttributes.mapType != "scenario") Index: ps/trunk/binaries/data/mods/public/gui/options/options.json =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.json +++ ps/trunk/binaries/data/mods/public/gui/options/options.json @@ -369,7 +369,7 @@ }, { "type": "dropdown", - "label": "Default AI difficulty", + "label": "Default AI Difficulty", "tooltip": "Default difficulty of the AI.", "config": "gui.gamesetup.aidifficulty", "list": [ @@ -383,6 +383,18 @@ }, { "type": "dropdown", + "label": "Default AI Behavior", + "tooltip": "Default behavior of the AI.", + "config": "gui.gamesetup.aibehavior", + "list": [ + { "value": "Random", "label": "Random" }, + { "value": "Generalist", "label": "Generalist" }, + { "value": "Aggressive", "label": "Aggressive" }, + { "value": "Defensive", "label": "Defensive" } + ] + }, + { + "type": "dropdown", "label": "Assign Players", "tooltip": "Automatically assign joining clients to free player slots during the match setup.", "config": "gui.gamesetup.assignplayers", Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/_petrabot.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/_petrabot.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/_petrabot.js @@ -17,7 +17,7 @@ "transports": 1 // transport plans start at 1 because 0 might be used as none }; - this.Config = new m.Config(settings.difficulty); + this.Config = new m.Config(settings.difficulty, settings.behavior); this.savedEvents = {}; }; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -318,7 +318,7 @@ // creating plans after updating because an aborted plan might be reused in that case. let barracksNb = gameState.getOwnEntitiesByClass("Barracks", true).filter(API3.Filters.isBuilt()).length; - if (this.rushNumber < this.maxRushes && barracksNb >= 1) + if (this.rushNumber < this.maxRushes && barracksNb >= 1 && this.Config.behavior != "defensive") { if (unexecutedAttacks.Rush === 0) { @@ -338,7 +338,8 @@ } else if (unexecutedAttacks.Attack == 0 && unexecutedAttacks.HugeAttack == 0 && this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length < Math.min(2, 1 + Math.round(gameState.getPopulationMax()/100)) && - (this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length == 0 || gameState.getPopulationMax() - gameState.getPopulation() > 12)) + (this.startedAttacks.Attack.length + this.startedAttacks.HugeAttack.length == 0 || gameState.getPopulationMax() - gameState.getPopulation() > 12) && + (this.Config.behavior != "defensive" || gameState.ai.HQ.defenseManager.targetList.length)) { if (barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.getPhaseName(2))) || !gameState.ai.HQ.baseManagers[1]) // if we have no base ... nothing else to do than attack Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/config.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/config.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/config.js @@ -1,11 +1,14 @@ var PETRA = function(m) { -m.Config = function(difficulty) +m.Config = function(difficulty, behavior) { // 0 is sandbox, 1 is very easy, 2 is easy, 3 is medium, 4 is hard and 5 is very hard. this.difficulty = difficulty !== undefined ? difficulty : 3; + // for instance "generalist", "aggressive" or "defensive" + this.behavior = behavior || "generalist"; + // debug level: 0=none, 1=sanity checks, 2=debug, 3=detailed debug, -100=serializatio debug this.debug = 0; @@ -124,9 +127,9 @@ // initialize personality traits if (this.difficulty > 1) { - this.personality.aggressive = randFloat(0, 1); + this.personality.aggressive = this.behavior === "aggressive" ? randFloat(0.7, 1) : randFloat(0, 0.6); this.personality.cooperative = randFloat(0, 1); - this.personality.defensive = randFloat(0, 1); + this.personality.defensive = this.behavior === "defensive" ? randFloat(0.7, 1) : randFloat(0, 0.6); } else { Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json @@ -6,63 +6,72 @@ "Civ": "gaia", "Color": { "r": 255, "g": 255, "b": 255 }, "AI": "", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 1", "Civ": "athen", "Color": { "r": 21, "g": 55, "b": 149 }, "AI": "", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 2", "Civ": "cart", "Color": { "r": 150, "g": 20, "b": 20 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 3", "Civ": "gaul", "Color": { "r": 86 , "g": 180, "b": 31 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 4", "Civ": "iber", "Color": { "r": 231, "g": 200, "b": 5 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 5", "Civ": "mace", "Color": { "r": 50, "g": 170, "b": 170 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 6", "Civ": "maur", "Color": { "r": 160, "g": 80, "b": 200 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 7", "Civ": "pers", "Color": { "r": 220, "g": 115, "b": 16 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" }, { "Name": "Player 8", "Civ": "rome", "Color": { "r": 64, "g": 64, "b": 64 }, "AI": "petra", - "AIDiff": 3 + "AIDiff": 3, + "AIBehavior": "generalist" } ] } Index: ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js @@ -51,7 +51,7 @@ if (settings.PlayerData[i] && settings.PlayerData[i].AI && settings.PlayerData[i].AI != "") { let AIDiff = +settings.PlayerData[i].AIDiff; - cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i, AIDiff); + cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i, AIDiff, settings.PlayerData[i].AIBehavior); cmpPlayer.SetAI(true); AIDiff = Math.min(AIDiff, rate.length - 1); cmpPlayer.SetGatherRateMultiplier(rate[AIDiff]); Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp @@ -1367,6 +1367,7 @@ CStr name = aiArgs[i].AfterFirst(":"); scriptInterface.SetProperty(player, "AI", std::string(name)); scriptInterface.SetProperty(player, "AIDiff", 3); + scriptInterface.SetProperty(player, "AIBehavior", std::string("generalist")); scriptInterface.SetPropertyInt(playerData, playerID-offset, player); } } Index: ps/trunk/source/simulation2/components/CCmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpAIManager.cpp +++ ps/trunk/source/simulation2/components/CCmpAIManager.cpp @@ -82,9 +82,9 @@ { NONCOPYABLE(CAIPlayer); public: - CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, u8 difficulty, + CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, u8 difficulty, const std::wstring& behavior, shared_ptr scriptInterface) : - m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), + m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_Behavior(behavior), m_ScriptInterface(scriptInterface), m_Obj(scriptInterface->GetJSRuntime()) { } @@ -148,6 +148,8 @@ m_ScriptInterface->Eval(L"({})", &settings); m_ScriptInterface->SetProperty(settings, "player", m_Player, false); m_ScriptInterface->SetProperty(settings, "difficulty", m_Difficulty, false); + m_ScriptInterface->SetProperty(settings, "behavior", m_Behavior, false); + if (!m_UseSharedComponent) { ENSURE(m_Worker.m_HasLoadedEntityTemplates); @@ -188,6 +190,7 @@ std::wstring m_AIName; player_id_t m_Player; u8 m_Difficulty; + std::wstring m_Behavior; bool m_UseSharedComponent; // Take care to keep this declaration before heap rooted members. Destructors of heap rooted @@ -477,9 +480,9 @@ return true; } - bool AddPlayer(const std::wstring& aiName, player_id_t player, u8 difficulty) + bool AddPlayer(const std::wstring& aiName, player_id_t player, u8 difficulty, const std::wstring& behavior) { - shared_ptr ai(new CAIPlayer(*this, aiName, player, difficulty, m_ScriptInterface)); + shared_ptr ai(new CAIPlayer(*this, aiName, player, difficulty, behavior, m_ScriptInterface)); if (!ai->Initialise()) return false; @@ -495,7 +498,7 @@ bool RunGamestateInit(const shared_ptr& gameState, const Grid& passabilityMap, const Grid& territoryMap, const std::map& nonPathfindingPassClassMasks, const std::map& pathfindingPassClassMasks) { - // this will be run last by InitGame.Js, passing the full game representation. + // this will be run last by InitGame.js, passing the full game representation. // For now it will run for the shared Component. // This is NOT run during deserialization. JSContext* cx = m_ScriptInterface->GetContext(); @@ -692,6 +695,7 @@ serializer.String("name", m_Players[i]->m_AIName, 1, 256); serializer.NumberI32_Unbounded("player", m_Players[i]->m_Player); serializer.NumberU8_Unbounded("difficulty", m_Players[i]->m_Difficulty); + serializer.String("behavior", m_Players[i]->m_Behavior, 1, 256); serializer.NumberU32_Unbounded("num commands", (u32)m_Players[i]->m_Commands.size()); for (size_t j = 0; j < m_Players[i]->m_Commands.size(); ++j) @@ -763,10 +767,12 @@ std::wstring name; player_id_t player; u8 difficulty; + std::wstring behavior; deserializer.String("name", name, 1, 256); deserializer.NumberI32_Unbounded("player", player); deserializer.NumberU8_Unbounded("difficulty",difficulty); - if (!AddPlayer(name, player, difficulty)) + deserializer.String("behavior", behavior, 1, 256); + if (!AddPlayer(name, player, difficulty, behavior)) throw PSERROR_Deserialize_ScriptError(); u32 numCommands; @@ -999,11 +1005,11 @@ m_JustDeserialized = true; } - virtual void AddPlayer(const std::wstring& id, player_id_t player, u8 difficulty) + virtual void AddPlayer(const std::wstring& id, player_id_t player, u8 difficulty, const std::wstring& behavior) { LoadUsedEntityTemplates(); - m_Worker.AddPlayer(id, player, difficulty); + m_Worker.AddPlayer(id, player, difficulty, behavior); // AI players can cheat and see through FoW/SoD, since that greatly simplifies // their implementation. Index: ps/trunk/source/simulation2/components/ICmpAIManager.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpAIManager.h +++ ps/trunk/source/simulation2/components/ICmpAIManager.h @@ -30,7 +30,7 @@ * by @p id (corresponding to a subdirectory in simulation/ai/), * to control player @p player. */ - virtual void AddPlayer(const std::wstring& id, player_id_t player, uint8_t difficulty) = 0; + virtual void AddPlayer(const std::wstring& id, player_id_t player, uint8_t difficulty, const std::wstring&) = 0; virtual void SetRNGSeed(uint32_t seed) = 0; virtual void TryLoadSharedComponent() = 0; virtual void RunGamestateInit() = 0; Index: ps/trunk/source/simulation2/components/ICmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpAIManager.cpp +++ ps/trunk/source/simulation2/components/ICmpAIManager.cpp @@ -25,7 +25,7 @@ #include "ps/Filesystem.h" BEGIN_INTERFACE_WRAPPER(AIManager) -DEFINE_INTERFACE_METHOD_3("AddPlayer", void, ICmpAIManager, AddPlayer, std::wstring, player_id_t, uint8_t) +DEFINE_INTERFACE_METHOD_4("AddPlayer", void, ICmpAIManager, AddPlayer, std::wstring, player_id_t, uint8_t, std::wstring) DEFINE_INTERFACE_METHOD_1("SetRNGSeed", void, ICmpAIManager, SetRNGSeed, uint32_t) DEFINE_INTERFACE_METHOD_0("TryLoadSharedComponent", void, ICmpAIManager, TryLoadSharedComponent) DEFINE_INTERFACE_METHOD_0("RunGamestateInit", void, ICmpAIManager, RunGamestateInit)