Line data Source code
1 : function PlayerManager() {} 2 : 3 0 : PlayerManager.prototype.Schema = 4 : "<a:component type='system'/><empty/>"; 5 : 6 0 : PlayerManager.prototype.Init = function() 7 : { 8 : // List of player entity IDs. 9 0 : this.playerEntities = []; 10 : }; 11 : 12 : /** 13 : * @param {string} templateName - The template name of the player to add. 14 : * @return {number} - The player's ID (player number). 15 : */ 16 0 : PlayerManager.prototype.AddPlayer = function(templateName) 17 : { 18 0 : const ent = Engine.AddEntity(templateName); 19 0 : const id = this.playerEntities.length; 20 0 : const cmpPlayer = Engine.QueryInterface(ent, IID_Player); 21 0 : cmpPlayer.SetPlayerID(id); 22 0 : this.playerEntities.push(ent); 23 : 24 0 : const newDiplo = []; 25 0 : for (let i = 0; i < id; i++) 26 : { 27 0 : Engine.QueryInterface(this.GetPlayerByID(i), IID_Player).diplomacy[id] = -1; 28 0 : newDiplo[i] = -1; 29 : } 30 0 : newDiplo[id] = 1; 31 0 : cmpPlayer.SetDiplomacy(newDiplo); 32 : 33 0 : Engine.BroadcastMessage(MT_PlayerEntityChanged, { 34 : "player": id, 35 : "from": INVALID_ENTITY, 36 : "to": ent 37 : }); 38 : 39 0 : return id; 40 : }; 41 : 42 : /** 43 : * To avoid possible problems, 44 : * we first remove all entities from this player, and add them back after the replacement. 45 : * Note: This should only be called during setup/init and not during the game 46 : * @param {number} id - The player number to replace. 47 : * @param {string} newTemplateName - The new template name for the player. 48 : */ 49 0 : PlayerManager.prototype.ReplacePlayerTemplate = function(id, newTemplateName) 50 : { 51 0 : const ent = Engine.AddEntity(newTemplateName); 52 0 : const entities = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(id); 53 0 : for (const e of entities) 54 0 : Engine.QueryInterface(e, IID_Ownership)?.SetOwner(INVALID_PLAYER); 55 : 56 0 : const oldent = this.playerEntities[id]; 57 0 : const oldCmpPlayer = Engine.QueryInterface(oldent, IID_Player); 58 0 : const newCmpPlayer = Engine.QueryInterface(ent, IID_Player); 59 : 60 0 : newCmpPlayer.SetPlayerID(id); 61 0 : this.playerEntities[id] = ent; 62 : 63 0 : newCmpPlayer.SetColor(oldCmpPlayer.GetColor()); 64 0 : newCmpPlayer.SetDiplomacy(oldCmpPlayer.GetDiplomacy()); 65 : 66 0 : Engine.BroadcastMessage(MT_PlayerEntityChanged, { 67 : "player": id, 68 : "from": oldent, 69 : "to": ent 70 : }); 71 : 72 0 : for (const e of entities) 73 0 : Engine.QueryInterface(e, IID_Ownership)?.SetOwner(id); 74 : 75 0 : Engine.DestroyEntity(oldent); 76 0 : Engine.FlushDestroyedEntities(); 77 : }; 78 : 79 : /** 80 : * Returns the player entity ID for the given player ID. 81 : * The player ID must be valid (else there will be an error message). 82 : */ 83 0 : PlayerManager.prototype.GetPlayerByID = function(id) 84 : { 85 0 : if (id in this.playerEntities) 86 0 : return this.playerEntities[id]; 87 : 88 : // Observers don't have player data. 89 0 : if (id == INVALID_PLAYER) 90 0 : return INVALID_ENTITY; 91 : 92 0 : const stack = new Error().stack.trimRight().replace(/^/mg, ' '); // indent each line 93 0 : warn("GetPlayerByID: no player defined for id '"+id+"'\n"+stack); 94 : 95 0 : return INVALID_ENTITY; 96 : }; 97 : 98 : /** 99 : * Returns the number of players including gaia. 100 : */ 101 0 : PlayerManager.prototype.GetNumPlayers = function() 102 : { 103 0 : return this.playerEntities.length; 104 : }; 105 : 106 : /** 107 : * Returns IDs of all players including gaia. 108 : */ 109 0 : PlayerManager.prototype.GetAllPlayers = function() 110 : { 111 0 : const players = []; 112 0 : for (let i = 0; i < this.playerEntities.length; ++i) 113 0 : players.push(i); 114 0 : return players; 115 : }; 116 : 117 : /** 118 : * Returns IDs of all players excluding gaia. 119 : */ 120 0 : PlayerManager.prototype.GetNonGaiaPlayers = function() 121 : { 122 0 : const players = []; 123 0 : for (let i = 1; i < this.playerEntities.length; ++i) 124 0 : players.push(i); 125 0 : return players; 126 : }; 127 : 128 : /** 129 : * Returns IDs of all players excluding gaia that are not defeated nor have won. 130 : */ 131 0 : PlayerManager.prototype.GetActivePlayers = function() 132 : { 133 0 : return this.GetNonGaiaPlayers().filter(playerID => 134 0 : Engine.QueryInterface(this.GetPlayerByID(playerID), IID_Player).IsActive() 135 : ); 136 : }; 137 : 138 : /** 139 : * Note: This should only be called during setup/init and not during a match 140 : * since it doesn't change the owned entities. 141 : */ 142 0 : PlayerManager.prototype.RemoveLastPlayer = function() 143 : { 144 0 : if (!this.playerEntities.length) 145 0 : return; 146 : 147 0 : const lastId = this.playerEntities.pop(); 148 0 : Engine.BroadcastMessage(MT_PlayerEntityChanged, { 149 : "player": this.playerEntities.length + 1, 150 : "from": lastId, 151 : "to": INVALID_ENTITY 152 : }); 153 0 : Engine.DestroyEntity(lastId); 154 : }; 155 : 156 0 : PlayerManager.prototype.SetMaxWorldPopulation = function(max) 157 : { 158 0 : this.maxWorldPopulation = max; 159 0 : this.RedistributeWorldPopulation(); 160 : }; 161 : 162 0 : PlayerManager.prototype.GetMaxWorldPopulation = function() 163 : { 164 0 : return this.maxWorldPopulation; 165 : }; 166 : 167 0 : PlayerManager.prototype.RedistributeWorldPopulation = function() 168 : { 169 0 : const worldPopulation = this.GetMaxWorldPopulation(); 170 0 : if (!worldPopulation) 171 0 : return; 172 : 173 0 : const activePlayers = this.GetActivePlayers(); 174 0 : if (!activePlayers.length) 175 0 : return; 176 : 177 0 : const newMaxPopulation = worldPopulation / activePlayers.length; 178 0 : for (const playerID of activePlayers) 179 0 : Engine.QueryInterface(this.GetPlayerByID(playerID), IID_Player).SetMaxPopulation(newMaxPopulation); 180 : }; 181 : 182 0 : PlayerManager.prototype.OnGlobalPlayerDefeated = function(msg) 183 : { 184 0 : this.RedistributeWorldPopulation(); 185 : }; 186 : 187 0 : Engine.RegisterSystemComponentType(IID_PlayerManager, "PlayerManager", PlayerManager);