function PlayerManager() {} PlayerManager.prototype.Schema = ""; PlayerManager.prototype.Init = function() { // List of player entity IDs. this.playerEntities = []; }; /** * @param {string} templateName - The template name of the player to add. * @return {number} - The player's ID (player number). */ PlayerManager.prototype.AddPlayer = function(templateName) { const ent = Engine.AddEntity(templateName); const id = this.playerEntities.length; Engine.QueryInterface(ent, IID_Player).SetPlayerID(id); this.playerEntities.push(ent); const newDiplo = []; for (let i = 0; i < id; i++) { Engine.QueryInterface(this.GetPlayerByID(i), IID_Diplomacy).diplomacy[id] = -1; newDiplo[i] = -1; } newDiplo[id] = 1; Engine.QueryInterface(ent, IID_Diplomacy).SetDiplomacy(newDiplo); Engine.BroadcastMessage(MT_PlayerEntityChanged, { "player": id, "from": INVALID_ENTITY, "to": ent }); return id; }; /** * To avoid possible problems, * we first remove all entities from this player, and add them back after the replacement. * Note: This should only be called during setup/init and not during the game * @param {number} id - The player number to replace. * @param {string} newTemplateName - The new template name for the player. */ PlayerManager.prototype.ReplacePlayerTemplate = function(id, newTemplateName) { const ent = Engine.AddEntity(newTemplateName); const entities = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(id); for (const e of entities) Engine.QueryInterface(e, IID_Ownership)?.SetOwner(INVALID_PLAYER); const oldent = this.playerEntities[id]; const oldCmpPlayer = Engine.QueryInterface(oldent, IID_Player); const newCmpPlayer = Engine.QueryInterface(ent, IID_Player); newCmpPlayer.SetPlayerID(id); this.playerEntities[id] = ent; newCmpPlayer.SetColor(oldCmpPlayer.GetColor()); Engine.QueryInterface(ent, IID_Diplomacy).SetDiplomacy(Engine.QueryInterface(oldent, IID_Diplomacy).GetDiplomacy()); Engine.BroadcastMessage(MT_PlayerEntityChanged, { "player": id, "from": oldent, "to": ent }); for (const e of entities) Engine.QueryInterface(e, IID_Ownership)?.SetOwner(id); Engine.DestroyEntity(oldent); Engine.FlushDestroyedEntities(); }; /** * Returns the player entity ID for the given player ID. * The player ID must be valid (else there will be an error message). */ PlayerManager.prototype.GetPlayerByID = function(id) { if (id in this.playerEntities) return this.playerEntities[id]; // Observers don't have player data. if (id == INVALID_PLAYER) return INVALID_ENTITY; const stack = new Error().stack.trimRight().replace(/^/mg, ' '); // indent each line warn("GetPlayerByID: no player defined for id '"+id+"'\n"+stack); return INVALID_ENTITY; }; /** * Returns the number of players including gaia. */ PlayerManager.prototype.GetNumPlayers = function() { return this.playerEntities.length; }; /** * Returns IDs of all players including gaia. */ PlayerManager.prototype.GetAllPlayers = function() { const players = []; for (let i = 0; i < this.playerEntities.length; ++i) players.push(i); return players; }; /** * Returns IDs of all players excluding gaia. */ PlayerManager.prototype.GetNonGaiaPlayers = function() { const players = []; for (let i = 1; i < this.playerEntities.length; ++i) players.push(i); return players; }; /** * Returns IDs of all players excluding gaia that are not defeated nor have won. */ PlayerManager.prototype.GetActivePlayers = function() { return this.GetNonGaiaPlayers().filter(playerID => Engine.QueryInterface(this.GetPlayerByID(playerID), IID_Player).IsActive() ); }; /** * Note: This should only be called during setup/init and not during a match * since it doesn't change the owned entities. */ PlayerManager.prototype.RemoveLastPlayer = function() { if (!this.playerEntities.length) return; const lastId = this.playerEntities.pop(); Engine.BroadcastMessage(MT_PlayerEntityChanged, { "player": this.playerEntities.length + 1, "from": lastId, "to": INVALID_ENTITY }); Engine.DestroyEntity(lastId); }; Engine.RegisterSystemComponentType(IID_PlayerManager, "PlayerManager", PlayerManager);