LCOV - code coverage report
Current view: top level - simulation/components - Player.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 214 443 48.3 %
Date: 2023-04-02 12:52:40 Functions: 25 100 25.0 %

          Line data    Source code
       1             : function Player() {}
       2             : 
       3           6 : Player.prototype.Schema =
       4             :         "<element name='BarterMultiplier' a:help='Multipliers for barter prices.'>" +
       5             :                 "<interleave>" +
       6             :                         "<element name='Buy' a:help='Multipliers for the buy prices.'>" +
       7             :                                 Resources.BuildSchema("positiveDecimal") +
       8             :                         "</element>" +
       9             :                         "<element name='Sell' a:help='Multipliers for the sell prices.'>" +
      10             :                                 Resources.BuildSchema("positiveDecimal") +
      11             :                         "</element>" +
      12             :                 "</interleave>" +
      13             :         "</element>" +
      14             :         "<element name='Formations' a:help='Space-separated list of formations this player can use.'>" +
      15             :                 "<attribute name='datatype'>" +
      16             :                         "<value>tokens</value>" +
      17             :                 "</attribute>" +
      18             :                 "<text/>" +
      19             :         "</element>" +
      20             :         "<element name='SharedLosTech' a:help='Allies will share los when this technology is researched. Leave empty to never share LOS.'>" +
      21             :                 "<text/>" +
      22             :         "</element>" +
      23             :         "<element name='SharedDropsitesTech' a:help='Allies will share dropsites when this technology is researched. Leave empty to never share dropsites.'>" +
      24             :                 "<text/>" +
      25             :         "</element>" +
      26             :         "<element name='SpyCostMultiplier'>" +
      27             :                 "<ref name='nonNegativeDecimal'/>" +
      28             :         "</element>";
      29             : 
      30             : // The GUI expects these strings.
      31           6 : Player.prototype.STATE_ACTIVE = "active";
      32           6 : Player.prototype.STATE_DEFEATED = "defeated";
      33           6 : Player.prototype.STATE_WON = "won";
      34             : 
      35             : /**
      36             :  * Don't serialize diplomacyColor or displayDiplomacyColor since they're modified by the GUI.
      37             :  */
      38           6 : Player.prototype.Serialize = function()
      39             : {
      40           0 :         let state = {};
      41           0 :         for (let key in this)
      42           0 :                 if (this.hasOwnProperty(key))
      43           0 :                         state[key] = this[key];
      44             : 
      45           0 :         state.diplomacyColor = undefined;
      46           0 :         state.displayDiplomacyColor = false;
      47           0 :         return state;
      48             : };
      49             : 
      50           6 : Player.prototype.Deserialize = function(state)
      51             : {
      52           0 :         for (let prop in state)
      53           0 :                 this[prop] = state[prop];
      54             : };
      55             : 
      56             : /**
      57             :  * Which units will be shown with special icons at the top.
      58             :  */
      59           6 : var panelEntityClasses = "Hero Relic";
      60             : 
      61           6 : Player.prototype.Init = function()
      62             : {
      63           6 :         this.playerID = undefined;
      64           6 :         this.color = undefined;
      65           6 :         this.diplomacyColor = undefined;
      66           6 :         this.displayDiplomacyColor = false;
      67           6 :         this.popUsed = 0; // Population of units owned or trained by this player.
      68           6 :         this.popBonuses = 0; // Sum of population bonuses of player's entities.
      69           6 :         this.maxPop = 300; // Maximum population.
      70           6 :         this.trainingBlocked = false; // Indicates whether any training queue is currently blocked.
      71           6 :         this.resourceCount = {};
      72           6 :         this.resourceGatherers = {};
      73           6 :         this.tradingGoods = []; // Goods for next trade-route and its probabilities * 100.
      74           6 :         this.team = -1; // Team number of the player, players on the same team will always have ally diplomatic status. Also this is useful for team emblems, scoring, etc.
      75           6 :         this.teamsLocked = false;
      76           6 :         this.state = this.STATE_ACTIVE;
      77           6 :         this.diplomacy = [];    // Array of diplomatic stances for this player with respect to other players (including gaia and self).
      78           6 :         this.sharedDropsites = false;
      79           6 :         this.formations = this.template.Formations._string.split(" ");
      80           6 :         this.startCam = undefined;
      81           6 :         this.controlAllUnits = false;
      82           6 :         this.isAI = false;
      83           6 :         this.cheatsEnabled = false;
      84           6 :         this.panelEntities = [];
      85           6 :         this.resourceNames = {};
      86           6 :         this.disabledTemplates = {};
      87           6 :         this.disabledTechnologies = {};
      88           6 :         this.spyCostMultiplier = +this.template.SpyCostMultiplier;
      89           6 :         this.barterEntities = [];
      90           6 :         this.barterMultiplier = {
      91             :                 "buy": clone(this.template.BarterMultiplier.Buy),
      92             :                 "sell": clone(this.template.BarterMultiplier.Sell)
      93             :         };
      94             : 
      95             :         // Initial resources.
      96           6 :         let resCodes = Resources.GetCodes();
      97           6 :         for (let res of resCodes)
      98             :         {
      99          20 :                 this.resourceCount[res] = 300;
     100          20 :                 this.resourceNames[res] = Resources.GetResource(res).name;
     101          20 :                 this.resourceGatherers[res] = 0;
     102             :         }
     103             :         // Trading goods probability in steps of 5.
     104           6 :         let resTradeCodes = Resources.GetTradableCodes();
     105           6 :         let quotient = Math.floor(20 / resTradeCodes.length);
     106           6 :         let remainder = 20 % resTradeCodes.length;
     107           6 :         for (let i in resTradeCodes)
     108          20 :                 this.tradingGoods.push({
     109             :                         "goods": resTradeCodes[i],
     110             :                         "proba": 5 * (quotient + (+i < remainder ? 1 : 0))
     111             :                 });
     112             : };
     113             : 
     114           6 : Player.prototype.SetPlayerID = function(id)
     115             : {
     116           1 :         this.playerID = id;
     117             : };
     118             : 
     119           6 : Player.prototype.GetPlayerID = function()
     120             : {
     121           7 :         return this.playerID;
     122             : };
     123             : 
     124           6 : Player.prototype.SetColor = function(r, g, b)
     125             : {
     126           0 :         let colorInitialized = !!this.color;
     127             : 
     128           0 :         this.color = { "r": r / 255, "g": g / 255, "b": b / 255, "a": 1 };
     129             : 
     130             :         // Used in Atlas.
     131           0 :         if (colorInitialized)
     132           0 :                 Engine.BroadcastMessage(MT_PlayerColorChanged, {
     133             :                         "player": this.playerID
     134             :                 });
     135             : };
     136             : 
     137           6 : Player.prototype.SetDiplomacyColor = function(color)
     138             : {
     139           0 :         this.diplomacyColor = { "r": color.r / 255, "g": color.g / 255, "b": color.b / 255, "a": 1 };
     140             : };
     141             : 
     142           6 : Player.prototype.SetDisplayDiplomacyColor = function(displayDiplomacyColor)
     143             : {
     144           0 :         this.displayDiplomacyColor = displayDiplomacyColor;
     145             : };
     146             : 
     147           6 : Player.prototype.GetColor = function()
     148             : {
     149           0 :         return this.color;
     150             : };
     151             : 
     152           6 : Player.prototype.GetDisplayedColor = function()
     153             : {
     154           0 :         return this.displayDiplomacyColor ? this.diplomacyColor : this.color;
     155             : };
     156             : 
     157             : // Try reserving num population slots. Returns 0 on success or number of missing slots otherwise.
     158           6 : Player.prototype.TryReservePopulationSlots = function(num)
     159             : {
     160           0 :         if (num != 0 && num > (this.GetPopulationLimit() - this.popUsed))
     161           0 :                 return num - (this.GetPopulationLimit() - this.popUsed);
     162             : 
     163           0 :         this.popUsed += num;
     164           0 :         return 0;
     165             : };
     166             : 
     167           6 : Player.prototype.UnReservePopulationSlots = function(num)
     168             : {
     169           0 :         this.popUsed -= num;
     170             : };
     171             : 
     172           6 : Player.prototype.GetPopulationCount = function()
     173             : {
     174           1 :         return this.popUsed;
     175             : };
     176             : 
     177           6 : Player.prototype.AddPopulation = function(num)
     178             : {
     179           0 :         this.popUsed += num;
     180             : };
     181             : 
     182           6 : Player.prototype.SetPopulationBonuses = function(num)
     183             : {
     184           0 :         this.popBonuses = num;
     185             : };
     186             : 
     187           6 : Player.prototype.AddPopulationBonuses = function(num)
     188             : {
     189           0 :         this.popBonuses += num;
     190             : };
     191             : 
     192           6 : Player.prototype.GetPopulationLimit = function()
     193             : {
     194           1 :         return Math.min(this.GetMaxPopulation(), this.popBonuses);
     195             : };
     196             : 
     197           6 : Player.prototype.SetMaxPopulation = function(max)
     198             : {
     199           0 :         this.maxPop = max;
     200             : };
     201             : 
     202           6 : Player.prototype.GetMaxPopulation = function()
     203             : {
     204           1 :         return Math.round(ApplyValueModificationsToEntity("Player/MaxPopulation", this.maxPop, this.entity));
     205             : };
     206             : 
     207           6 : Player.prototype.CanBarter = function()
     208             : {
     209           4 :         return this.barterEntities.length > 0;
     210             : };
     211             : 
     212           6 : Player.prototype.GetBarterMultiplier = function()
     213             : {
     214           1 :         return this.barterMultiplier;
     215             : };
     216             : 
     217           6 : Player.prototype.GetSpyCostMultiplier = function()
     218             : {
     219           1 :         return this.spyCostMultiplier;
     220             : };
     221             : 
     222           6 : Player.prototype.GetPanelEntities = function()
     223             : {
     224           0 :         return this.panelEntities;
     225             : };
     226             : 
     227           6 : Player.prototype.IsTrainingBlocked = function()
     228             : {
     229           0 :         return this.trainingBlocked;
     230             : };
     231             : 
     232           6 : Player.prototype.BlockTraining = function()
     233             : {
     234           0 :         this.trainingBlocked = true;
     235             : };
     236             : 
     237           6 : Player.prototype.UnBlockTraining = function()
     238             : {
     239           0 :         this.trainingBlocked = false;
     240             : };
     241             : 
     242           6 : Player.prototype.SetResourceCounts = function(resources)
     243             : {
     244           0 :         for (let res in resources)
     245           0 :                 this.resourceCount[res] = resources[res];
     246             : };
     247             : 
     248           6 : Player.prototype.GetResourceCounts = function()
     249             : {
     250          40 :         return this.resourceCount;
     251             : };
     252             : 
     253           6 : Player.prototype.GetResourceGatherers = function()
     254             : {
     255           0 :         return this.resourceGatherers;
     256             : };
     257             : 
     258             : /**
     259             :  * @param {string} type - The generic type of resource to add the gatherer for.
     260             :  */
     261           6 : Player.prototype.AddResourceGatherer = function(type)
     262             : {
     263           0 :         ++this.resourceGatherers[type];
     264             : };
     265             : 
     266             : /**
     267             :  * @param {string} type - The generic type of resource to remove the gatherer from.
     268             :  */
     269           6 : Player.prototype.RemoveResourceGatherer = function(type)
     270             : {
     271           0 :         --this.resourceGatherers[type];
     272             : };
     273             : 
     274             : /**
     275             :  * Add resource of specified type to player.
     276             :  * @param {string} type - Generic type of resource.
     277             :  * @param {number} amount - Amount of resource, which should be added.
     278             :  */
     279           6 : Player.prototype.AddResource = function(type, amount)
     280             : {
     281           0 :         this.resourceCount[type] += +amount;
     282             : };
     283             : 
     284             : /**
     285             :  * Add resources to player.
     286             :  */
     287           6 : Player.prototype.AddResources = function(amounts)
     288             : {
     289          16 :         for (let type in amounts)
     290          16 :                 this.resourceCount[type] += +amounts[type];
     291             : };
     292             : 
     293           6 : Player.prototype.GetNeededResources = function(amounts)
     294             : {
     295             :         // Check if we can afford it all.
     296         304 :         let amountsNeeded = {};
     297         304 :         for (let type in amounts)
     298         307 :                 if (this.resourceCount[type] != undefined && amounts[type] > this.resourceCount[type])
     299           2 :                         amountsNeeded[type] = amounts[type] - Math.floor(this.resourceCount[type]);
     300             : 
     301         304 :         if (Object.keys(amountsNeeded).length == 0)
     302         302 :                 return undefined;
     303           2 :         return amountsNeeded;
     304             : };
     305             : 
     306           6 : Player.prototype.SubtractResourcesOrNotify = function(amounts)
     307             : {
     308         304 :         let amountsNeeded = this.GetNeededResources(amounts);
     309             : 
     310             :         // If we don't have enough resources, send a notification to the player.
     311         304 :         if (amountsNeeded)
     312             :         {
     313           2 :                 let parameters = {};
     314           2 :                 let i = 0;
     315           2 :                 for (let type in amountsNeeded)
     316             :                 {
     317           2 :                         ++i;
     318           2 :                         parameters["resourceType" + i] = this.resourceNames[type];
     319           2 :                         parameters["resourceAmount" + i] = amountsNeeded[type];
     320             :                 }
     321             : 
     322           2 :                 let msg = "";
     323             :                 // When marking strings for translations, you need to include the actual string,
     324             :                 // not some way to derive the string.
     325           2 :                 if (i < 1)
     326           0 :                         warn("Amounts needed but no amounts given?");
     327           2 :                 else if (i == 1)
     328           2 :                         msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s");
     329           0 :                 else if (i == 2)
     330           0 :                         msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s");
     331           0 :                 else if (i == 3)
     332           0 :                         msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s");
     333           0 :                 else if (i == 4)
     334           0 :                         msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s, %(resourceAmount4)s %(resourceType4)s");
     335             :                 else
     336           0 :                         warn("Localisation: Strings are not localised for more than 4 resources");
     337             : 
     338             :                 // Send as time-notification.
     339           2 :                 let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     340           2 :                 cmpGUIInterface.PushNotification({
     341             :                         "players": [this.playerID],
     342             :                         "message": msg,
     343             :                         "parameters": parameters,
     344             :                         "translateMessage": true,
     345             :                         "translateParameters": {
     346             :                                 "resourceType1": "withinSentence",
     347             :                                 "resourceType2": "withinSentence",
     348             :                                 "resourceType3": "withinSentence",
     349             :                                 "resourceType4": "withinSentence"
     350             :                         }
     351             :                 });
     352           2 :                 return false;
     353             :         }
     354             : 
     355         302 :         for (let type in amounts)
     356         305 :                 this.resourceCount[type] -= amounts[type];
     357             : 
     358         302 :         return true;
     359             : };
     360             : 
     361           6 : Player.prototype.TrySubtractResources = function(amounts)
     362             : {
     363         304 :         if (!this.SubtractResourcesOrNotify(amounts))
     364           2 :                 return false;
     365             : 
     366         302 :         let cmpStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
     367         302 :         if (cmpStatisticsTracker)
     368           0 :                 for (let type in amounts)
     369           0 :                         cmpStatisticsTracker.IncreaseResourceUsedCounter(type, amounts[type]);
     370             : 
     371         302 :         return true;
     372             : };
     373             : 
     374           6 : Player.prototype.RefundResources = function(amounts)
     375             : {
     376           1 :         const cmpStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
     377           1 :         if (cmpStatisticsTracker)
     378           0 :                 for (const type in amounts)
     379           0 :                         cmpStatisticsTracker.IncreaseResourceUsedCounter(type, -amounts[type]);
     380             : 
     381           1 :         this.AddResources(amounts);
     382             : };
     383             : 
     384           6 : Player.prototype.GetNextTradingGoods = function()
     385             : {
     386           0 :         let value = randFloat(0, 100);
     387           0 :         let last = this.tradingGoods.length - 1;
     388           0 :         let sumProba = 0;
     389           0 :         for (let i = 0; i < last; ++i)
     390             :         {
     391           0 :                 sumProba += this.tradingGoods[i].proba;
     392           0 :                 if (value < sumProba)
     393           0 :                         return this.tradingGoods[i].goods;
     394             :         }
     395           0 :         return this.tradingGoods[last].goods;
     396             : };
     397             : 
     398           6 : Player.prototype.GetTradingGoods = function()
     399             : {
     400           0 :         let tradingGoods = {};
     401           0 :         for (let resource of this.tradingGoods)
     402           0 :                 tradingGoods[resource.goods] = resource.proba;
     403             : 
     404           0 :         return tradingGoods;
     405             : };
     406             : 
     407           6 : Player.prototype.SetTradingGoods = function(tradingGoods)
     408             : {
     409           0 :         let resTradeCodes = Resources.GetTradableCodes();
     410           0 :         let sumProba = 0;
     411           0 :         for (let resource in tradingGoods)
     412             :         {
     413           0 :                 if (resTradeCodes.indexOf(resource) == -1 || tradingGoods[resource] < 0)
     414             :                 {
     415           0 :                         error("Invalid trading goods: " + uneval(tradingGoods));
     416           0 :                         return;
     417             :                 }
     418           0 :                 sumProba += tradingGoods[resource];
     419             :         }
     420             : 
     421           0 :         if (sumProba != 100)
     422             :         {
     423           0 :                 error("Invalid trading goods probability: " + uneval(sumProba));
     424           0 :                 return;
     425             :         }
     426             : 
     427           0 :         this.tradingGoods = [];
     428           0 :         for (let resource in tradingGoods)
     429           0 :                 this.tradingGoods.push({
     430             :                         "goods": resource,
     431             :                         "proba": tradingGoods[resource]
     432             :                 });
     433             : };
     434             : 
     435             : /**
     436             :  * @param {string} message - The message to send in the chat. May be undefined.
     437             :  */
     438           6 : Player.prototype.Win = function(message)
     439             : {
     440           0 :         this.SetState(this.STATE_WON, message);
     441             : };
     442             : 
     443             : /**
     444             :  * @param {string} message - The message to send in the chat. May be undefined.
     445             :  */
     446           6 : Player.prototype.Defeat = function(message)
     447             : {
     448           0 :         this.SetState(this.STATE_DEFEATED, message);
     449             : };
     450             : 
     451             : /**
     452             :  * @return {string} - The string identified with the current state.
     453             :  */
     454           6 : Player.prototype.GetState = function()
     455             : {
     456           0 :         return this.state;
     457             : };
     458             : 
     459             : /**
     460             :  * @return {boolean} -
     461             :  */
     462           6 : Player.prototype.IsActive = function()
     463             : {
     464           0 :         return this.state === this.STATE_ACTIVE;
     465             : };
     466             : 
     467             : /**
     468             :  * @return {boolean} -
     469             :  */
     470           6 : Player.prototype.IsDefeated = function()
     471             : {
     472           0 :         return this.state === this.STATE_DEFEATED;
     473             : };
     474             : 
     475             : /**
     476             :  * @return {boolean} -
     477             :  */
     478           6 : Player.prototype.HasWon = function()
     479             : {
     480           0 :         return this.state === this.STATE_WON;
     481             : };
     482             : 
     483             : /**
     484             :  * @param {string} newState - Either "defeated" or "won".
     485             :  * @param {string|undefined} message - A string to be shown in chat, for example
     486             :  *     markForTranslation("%(player)s has been defeated (failed objective).").
     487             :  *     If it is undefined, the caller MUST send that GUI notification manually.
     488             :  */
     489           6 : Player.prototype.SetState = function(newState, message)
     490             : {
     491           0 :         if (!this.IsActive())
     492           0 :                 return;
     493             : 
     494           0 :         if (newState != this.STATE_WON && newState != this.STATE_DEFEATED)
     495             :         {
     496           0 :                 warn("Can't change playerstate to " + newState);
     497           0 :                 return;
     498             :         }
     499             : 
     500           0 :         if (!this.playerID)
     501             :         {
     502           0 :                 warn("Gaia can't change state.");
     503           0 :                 return;
     504             :         }
     505             : 
     506           0 :         this.state = newState;
     507             : 
     508           0 :         const won = this.HasWon();
     509           0 :         let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     510           0 :         if (won)
     511           0 :                 cmpRangeManager.SetLosRevealAll(this.playerID, true);
     512             :         else
     513             :         {
     514             :                 // Reassign all player's entities to Gaia.
     515           0 :                 let entities = cmpRangeManager.GetEntitiesByPlayer(this.playerID);
     516             : 
     517             :                 // The ownership change is done in two steps so that entities don't hit idle
     518             :                 // (and thus possibly look for "enemies" to attack) before nearby allies get
     519             :                 // converted to Gaia as well.
     520           0 :                 for (let entity of entities)
     521             :                 {
     522           0 :                         let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
     523           0 :                         cmpOwnership.SetOwnerQuiet(0);
     524             :                 }
     525             : 
     526             :                 // With the real ownership change complete, send OwnershipChanged messages.
     527           0 :                 for (let entity of entities)
     528           0 :                         Engine.PostMessage(entity, MT_OwnershipChanged, {
     529             :                                 "entity": entity,
     530             :                                 "from": this.playerID,
     531             :                                 "to": 0
     532             :                         });
     533             :         }
     534             : 
     535           0 :         if (message)
     536           0 :                 Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({
     537             :                         "type": won ? "won" : "defeat",
     538             :                         "players": [this.playerID],
     539             :                         "allies": [this.playerID],
     540             :                         "message": message
     541             :                 });
     542             : 
     543           0 :         Engine.PostMessage(this.entity, won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
     544             : };
     545             : 
     546           6 : Player.prototype.GetTeam = function()
     547             : {
     548           0 :         return this.team;
     549             : };
     550             : 
     551           6 : Player.prototype.SetTeam = function(team)
     552             : {
     553           0 :         if (this.teamsLocked)
     554           0 :                 return;
     555             : 
     556           0 :         this.team = team;
     557             : 
     558             :         // Set all team members as allies.
     559           0 :         if (this.team != -1)
     560             :         {
     561           0 :                 let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
     562           0 :                 for (let i = 0; i < numPlayers; ++i)
     563             :                 {
     564           0 :                         let cmpPlayer = QueryPlayerIDInterface(i);
     565           0 :                         if (this.team != cmpPlayer.GetTeam())
     566           0 :                                 continue;
     567             : 
     568           0 :                         this.SetAlly(i);
     569           0 :                         cmpPlayer.SetAlly(this.playerID);
     570             :                 }
     571             :         }
     572             : 
     573           0 :         Engine.BroadcastMessage(MT_DiplomacyChanged, {
     574             :                 "player": this.playerID,
     575             :                 "otherPlayer": null
     576             :         });
     577             : };
     578             : 
     579           6 : Player.prototype.SetLockTeams = function(value)
     580             : {
     581           0 :         this.teamsLocked = value;
     582             : };
     583             : 
     584           6 : Player.prototype.GetLockTeams = function()
     585             : {
     586           0 :         return this.teamsLocked;
     587             : };
     588             : 
     589           6 : Player.prototype.GetDiplomacy = function()
     590             : {
     591           1 :         return this.diplomacy.slice();
     592             : };
     593             : 
     594           6 : Player.prototype.SetDiplomacy = function(dipl)
     595             : {
     596           2 :         this.diplomacy = dipl.slice();
     597             : 
     598           2 :         Engine.BroadcastMessage(MT_DiplomacyChanged, {
     599             :                 "player": this.playerID,
     600             :                 "otherPlayer": null
     601             :         });
     602             : };
     603             : 
     604           6 : Player.prototype.SetDiplomacyIndex = function(idx, value)
     605             : {
     606           0 :         let cmpPlayer = QueryPlayerIDInterface(idx);
     607           0 :         if (!cmpPlayer)
     608           0 :                 return;
     609             : 
     610           0 :         if (!this.IsActive() || !cmpPlayer.IsActive())
     611           0 :                 return;
     612             : 
     613           0 :         this.diplomacy[idx] = value;
     614             : 
     615           0 :         Engine.BroadcastMessage(MT_DiplomacyChanged, {
     616             :                 "player": this.playerID,
     617             :                 "otherPlayer": cmpPlayer.GetPlayerID()
     618             :         });
     619             : 
     620             :         // Mutual worsening of relations.
     621           0 :         if (cmpPlayer.diplomacy[this.playerID] > value)
     622           0 :                 cmpPlayer.SetDiplomacyIndex(this.playerID, value);
     623             : };
     624             : 
     625           6 : Player.prototype.UpdateSharedLos = function()
     626             : {
     627           0 :         let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     628           0 :         let cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
     629           0 :         if (!cmpRangeManager || !cmpTechnologyManager)
     630           0 :                 return;
     631             : 
     632           0 :         if (!cmpTechnologyManager.IsTechnologyResearched(this.template.SharedLosTech))
     633             :         {
     634           0 :                 cmpRangeManager.SetSharedLos(this.playerID, [this.playerID]);
     635           0 :                 return;
     636             :         }
     637             : 
     638           0 :         cmpRangeManager.SetSharedLos(this.playerID, this.GetMutualAllies());
     639             : };
     640             : 
     641           6 : Player.prototype.GetFormations = function()
     642             : {
     643           0 :         return this.formations;
     644             : };
     645             : 
     646           6 : Player.prototype.SetFormations = function(formations)
     647             : {
     648           0 :         this.formations = formations;
     649             : };
     650             : 
     651           6 : Player.prototype.GetStartingCameraPos = function()
     652             : {
     653           0 :         return this.startCam.position;
     654             : };
     655             : 
     656           6 : Player.prototype.GetStartingCameraRot = function()
     657             : {
     658           0 :         return this.startCam.rotation;
     659             : };
     660             : 
     661           6 : Player.prototype.SetStartingCamera = function(pos, rot)
     662             : {
     663           0 :         this.startCam = { "position": pos, "rotation": rot };
     664             : };
     665             : 
     666           6 : Player.prototype.HasStartingCamera = function()
     667             : {
     668           0 :         return this.startCam !== undefined;
     669             : };
     670             : 
     671           6 : Player.prototype.HasSharedLos = function()
     672             : {
     673           0 :         let cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
     674           0 :         return cmpTechnologyManager && cmpTechnologyManager.IsTechnologyResearched(this.template.SharedLosTech);
     675             : };
     676           6 : Player.prototype.HasSharedDropsites = function()
     677             : {
     678           0 :         return this.sharedDropsites;
     679             : };
     680             : 
     681           6 : Player.prototype.SetControlAllUnits = function(c)
     682             : {
     683           0 :         this.controlAllUnits = c;
     684             : };
     685             : 
     686           6 : Player.prototype.CanControlAllUnits = function()
     687             : {
     688           0 :         return this.controlAllUnits;
     689             : };
     690             : 
     691           6 : Player.prototype.SetAI = function(flag)
     692             : {
     693           0 :         this.isAI = flag;
     694             : };
     695             : 
     696           6 : Player.prototype.IsAI = function()
     697             : {
     698           0 :         return this.isAI;
     699             : };
     700             : 
     701           6 : Player.prototype.GetPlayersByDiplomacy = function(func)
     702             : {
     703           2 :         let players = [];
     704           2 :         for (let i = 0; i < this.diplomacy.length; ++i)
     705          10 :                 if (this[func](i))
     706           4 :                         players.push(i);
     707           2 :         return players;
     708             : };
     709             : 
     710           6 : Player.prototype.SetAlly = function(id)
     711             : {
     712           0 :         this.SetDiplomacyIndex(id, 1);
     713             : };
     714             : 
     715             : /**
     716             :  * Check if given player is our ally.
     717             :  */
     718           6 : Player.prototype.IsAlly = function(id)
     719             : {
     720           6 :         return this.diplomacy[id] > 0;
     721             : };
     722             : 
     723           6 : Player.prototype.GetAllies = function()
     724             : {
     725           1 :         return this.GetPlayersByDiplomacy("IsAlly");
     726             : };
     727             : 
     728             : /**
     729             :  * Check if given player is our ally excluding ourself
     730             :  */
     731           6 : Player.prototype.IsExclusiveAlly = function(id)
     732             : {
     733           0 :         return this.playerID != id && this.IsAlly(id);
     734             : };
     735             : 
     736             : /**
     737             :  * Check if given player is our ally, and we are its ally
     738             :  */
     739           6 : Player.prototype.IsMutualAlly = function(id)
     740             : {
     741           0 :         let cmpPlayer = QueryPlayerIDInterface(id);
     742           0 :         return this.IsAlly(id) && cmpPlayer && cmpPlayer.IsAlly(this.playerID);
     743             : };
     744             : 
     745           6 : Player.prototype.GetMutualAllies = function()
     746             : {
     747           0 :         return this.GetPlayersByDiplomacy("IsMutualAlly");
     748             : };
     749             : 
     750             : /**
     751             :  * Check if given player is our ally, and we are its ally, excluding ourself
     752             :  */
     753           6 : Player.prototype.IsExclusiveMutualAlly = function(id)
     754             : {
     755           0 :         return this.playerID != id && this.IsMutualAlly(id);
     756             : };
     757             : 
     758           6 : Player.prototype.SetEnemy = function(id)
     759             : {
     760           0 :         this.SetDiplomacyIndex(id, -1);
     761             : };
     762             : 
     763             : /**
     764             :  * Check if given player is our enemy
     765             :  */
     766           6 : Player.prototype.IsEnemy = function(id)
     767             : {
     768           6 :         return this.diplomacy[id] < 0;
     769             : };
     770             : 
     771           6 : Player.prototype.GetEnemies = function()
     772             : {
     773           1 :         return this.GetPlayersByDiplomacy("IsEnemy");
     774             : };
     775             : 
     776           6 : Player.prototype.SetNeutral = function(id)
     777             : {
     778           0 :         this.SetDiplomacyIndex(id, 0);
     779             : };
     780             : 
     781             : /**
     782             :  * Check if given player is neutral
     783             :  */
     784           6 : Player.prototype.IsNeutral = function(id)
     785             : {
     786           0 :         return this.diplomacy[id] == 0;
     787             : };
     788             : 
     789             : /**
     790             :  * Do some map dependant initializations
     791             :  */
     792           6 : Player.prototype.OnGlobalInitGame = function(msg)
     793             : {
     794             :         // Replace the "{civ}" code with this civ ID.
     795           0 :         let disabledTemplates = this.disabledTemplates;
     796           0 :         this.disabledTemplates = {};
     797           0 :         const civ = Engine.QueryInterface(this.entity, IID_Identity).GetCiv();
     798           0 :         for (let template in disabledTemplates)
     799           0 :                 if (disabledTemplates[template])
     800           0 :                         this.disabledTemplates[template.replace(/\{civ\}/g, civ)] = true;
     801             : };
     802             : 
     803             : /**
     804             :  * Keep track of population effects of all entities that
     805             :  * become owned or unowned by this player.
     806             :  */
     807           6 : Player.prototype.OnGlobalOwnershipChanged = function(msg)
     808             : {
     809           4 :         if (msg.from != this.playerID && msg.to != this.playerID)
     810           0 :                 return;
     811             : 
     812           4 :         let cmpCost = Engine.QueryInterface(msg.entity, IID_Cost);
     813             : 
     814           4 :         if (msg.from == this.playerID)
     815             :         {
     816           1 :                 if (cmpCost)
     817           0 :                         this.popUsed -= cmpCost.GetPopCost();
     818             : 
     819           1 :                 let panelIndex = this.panelEntities.indexOf(msg.entity);
     820           1 :                 if (panelIndex >= 0)
     821           0 :                         this.panelEntities.splice(panelIndex, 1);
     822             : 
     823           1 :                 let barterIndex = this.barterEntities.indexOf(msg.entity);
     824           1 :                 if (barterIndex >= 0)
     825           1 :                         this.barterEntities.splice(barterIndex, 1);
     826             :         }
     827           4 :         if (msg.to == this.playerID)
     828             :         {
     829           3 :                 if (cmpCost)
     830           0 :                         this.popUsed += cmpCost.GetPopCost();
     831             : 
     832           3 :                 let cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
     833           3 :                 if (!cmpIdentity)
     834           0 :                         return;
     835             : 
     836           3 :                 if (MatchesClassList(cmpIdentity.GetClassesList(), panelEntityClasses))
     837           0 :                         this.panelEntities.push(msg.entity);
     838             : 
     839           3 :                 if (cmpIdentity.HasClass("Barter") && !Engine.QueryInterface(msg.entity, IID_Foundation))
     840           1 :                         this.barterEntities.push(msg.entity);
     841             :         }
     842             : };
     843             : 
     844           6 : Player.prototype.OnResearchFinished = function(msg)
     845             : {
     846           0 :         if (msg.tech == this.template.SharedLosTech)
     847           0 :                 this.UpdateSharedLos();
     848           0 :         else if (msg.tech == this.template.SharedDropsitesTech)
     849           0 :                 this.sharedDropsites = true;
     850             : };
     851             : 
     852           6 : Player.prototype.OnDiplomacyChanged = function()
     853             : {
     854           0 :         this.UpdateSharedLos();
     855             : };
     856             : 
     857           6 : Player.prototype.OnValueModification = function(msg)
     858             : {
     859           0 :         if (msg.component != "Player")
     860           0 :                 return;
     861             : 
     862           0 :         if (msg.valueNames.indexOf("Player/SpyCostMultiplier") != -1)
     863           0 :                 this.spyCostMultiplier = ApplyValueModificationsToEntity("Player/SpyCostMultiplier", +this.template.SpyCostMultiplier, this.entity);
     864             : 
     865           0 :         if (msg.valueNames.some(mod => mod.startsWith("Player/BarterMultiplier/")))
     866           0 :                 for (let res in this.template.BarterMultiplier.Buy)
     867             :                 {
     868           0 :                         this.barterMultiplier.buy[res] = ApplyValueModificationsToEntity("Player/BarterMultiplier/Buy/"+res, +this.template.BarterMultiplier.Buy[res], this.entity);
     869           0 :                         this.barterMultiplier.sell[res] = ApplyValueModificationsToEntity("Player/BarterMultiplier/Sell/"+res, +this.template.BarterMultiplier.Sell[res], this.entity);
     870             :                 }
     871             : };
     872             : 
     873           6 : Player.prototype.SetCheatsEnabled = function(flag)
     874             : {
     875           0 :         this.cheatsEnabled = flag;
     876             : };
     877             : 
     878           6 : Player.prototype.GetCheatsEnabled = function()
     879             : {
     880           0 :         return this.cheatsEnabled;
     881             : };
     882             : 
     883           6 : Player.prototype.TributeResource = function(player, amounts)
     884             : {
     885           0 :         let cmpPlayer = QueryPlayerIDInterface(player);
     886           0 :         if (!cmpPlayer)
     887           0 :                 return;
     888             : 
     889           0 :         if (!this.IsActive() || !cmpPlayer.IsActive())
     890           0 :                 return;
     891             : 
     892           0 :         let resTribCodes = Resources.GetTributableCodes();
     893           0 :         for (let resCode in amounts)
     894           0 :                 if (resTribCodes.indexOf(resCode) == -1 ||
     895             :                     !Number.isInteger(amounts[resCode]) ||
     896             :                     amounts[resCode] < 0)
     897             :                 {
     898           0 :                         warn("Invalid tribute amounts: " + uneval(resCode) + ": " + uneval(amounts));
     899           0 :                         return;
     900             :                 }
     901             : 
     902           0 :         if (!this.SubtractResourcesOrNotify(amounts))
     903           0 :                 return;
     904           0 :         cmpPlayer.AddResources(amounts);
     905             : 
     906           0 :         let total = Object.keys(amounts).reduce((sum, type) => sum + amounts[type], 0);
     907           0 :         let cmpOurStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
     908           0 :         if (cmpOurStatisticsTracker)
     909           0 :                 cmpOurStatisticsTracker.IncreaseTributesSentCounter(total);
     910           0 :         let cmpTheirStatisticsTracker = QueryPlayerIDInterface(player, IID_StatisticsTracker);
     911           0 :         if (cmpTheirStatisticsTracker)
     912           0 :                 cmpTheirStatisticsTracker.IncreaseTributesReceivedCounter(total);
     913             : 
     914           0 :         let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     915           0 :         if (cmpGUIInterface)
     916           0 :                 cmpGUIInterface.PushNotification({
     917             :                         "type": "tribute",
     918             :                         "players": [player],
     919             :                         "donator": this.playerID,
     920             :                         "amounts": amounts
     921             :                 });
     922             : 
     923           0 :         Engine.BroadcastMessage(MT_TributeExchanged, {
     924             :                 "to": player,
     925             :                 "from": this.playerID,
     926             :                 "amounts": amounts
     927             :         });
     928             : };
     929             : 
     930           6 : Player.prototype.AddDisabledTemplate = function(template)
     931             : {
     932           0 :         this.disabledTemplates[template] = true;
     933           0 :         Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
     934             : };
     935             : 
     936           6 : Player.prototype.RemoveDisabledTemplate = function(template)
     937             : {
     938           0 :         this.disabledTemplates[template] = false;
     939           0 :         Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
     940             : };
     941             : 
     942           6 : Player.prototype.SetDisabledTemplates = function(templates)
     943             : {
     944           0 :         this.disabledTemplates = {};
     945           0 :         for (let template of templates)
     946           0 :                 this.disabledTemplates[template] = true;
     947           0 :         Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
     948             : };
     949             : 
     950           6 : Player.prototype.GetDisabledTemplates = function()
     951             : {
     952           0 :         return this.disabledTemplates;
     953             : };
     954             : 
     955           6 : Player.prototype.AddDisabledTechnology = function(tech)
     956             : {
     957           0 :         this.disabledTechnologies[tech] = true;
     958           0 :         Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
     959             : };
     960             : 
     961           6 : Player.prototype.RemoveDisabledTechnology = function(tech)
     962             : {
     963           0 :         this.disabledTechnologies[tech] = false;
     964           0 :         Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
     965             : };
     966             : 
     967           6 : Player.prototype.SetDisabledTechnologies = function(techs)
     968             : {
     969           0 :         this.disabledTechnologies = {};
     970           0 :         for (let tech of techs)
     971           0 :                 this.disabledTechnologies[tech] = true;
     972           0 :         Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
     973             : };
     974             : 
     975           6 : Player.prototype.GetDisabledTechnologies = function()
     976             : {
     977           2 :         return this.disabledTechnologies;
     978             : };
     979             : 
     980           6 : Player.prototype.OnGlobalPlayerDefeated = function(msg)
     981             : {
     982           0 :         let cmpSound = Engine.QueryInterface(this.entity, IID_Sound);
     983           0 :         if (!cmpSound)
     984           0 :                 return;
     985             : 
     986           0 :         const soundGroup = cmpSound.GetSoundGroup(this.playerID === msg.playerId ? "defeated" : this.IsAlly(msg.playerId) ? "defeated_ally" : this.HasWon() ? "won" : "defeated_enemy");
     987           0 :         if (soundGroup)
     988           0 :                 Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager).PlaySoundGroupForPlayer(soundGroup, this.playerID);
     989             : };
     990             : 
     991           6 : Engine.RegisterComponentType(IID_Player, "Player", Player);

Generated by: LCOV version 1.14