1 | function Player() {}
|
---|
2 |
|
---|
3 | 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='SpyCostMultiplier'>" +
|
---|
21 | "<ref name='nonNegativeDecimal'/>" +
|
---|
22 | "</element>";
|
---|
23 |
|
---|
24 | // The GUI expects these strings.
|
---|
25 | Player.prototype.STATE_ACTIVE = "active";
|
---|
26 | Player.prototype.STATE_DEFEATED = "defeated";
|
---|
27 | Player.prototype.STATE_WON = "won";
|
---|
28 |
|
---|
29 | Player.prototype.Serialize = function()
|
---|
30 | {
|
---|
31 | let state = {};
|
---|
32 | for (let key in this)
|
---|
33 | if (this.hasOwnProperty(key))
|
---|
34 | state[key] = this[key];
|
---|
35 |
|
---|
36 | // Modified by GUI, so don't serialise.
|
---|
37 | delete state.displayDiplomacyColor;
|
---|
38 | return state;
|
---|
39 | };
|
---|
40 |
|
---|
41 | Player.prototype.Deserialize = function(state)
|
---|
42 | {
|
---|
43 | for (let prop in state)
|
---|
44 | this[prop] = state[prop];
|
---|
45 | };
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * Which units will be shown with special icons at the top.
|
---|
49 | */
|
---|
50 | var panelEntityClasses = "Hero Relic";
|
---|
51 |
|
---|
52 | Player.prototype.Init = function()
|
---|
53 | {
|
---|
54 | this.playerID = undefined;
|
---|
55 | this.color = undefined;
|
---|
56 | this.popUsed = 0; // Population of units owned or trained by this player.
|
---|
57 | this.popBonuses = 0; // Sum of population bonuses of player's entities.
|
---|
58 | this.maxPop = 300; // Maximum population.
|
---|
59 | this.trainingBlocked = false; // Indicates whether any training queue is currently blocked.
|
---|
60 | this.resourceCount = {};
|
---|
61 | this.resourceGatherers = {};
|
---|
62 | this.tradingGoods = []; // Goods for next trade-route and its probabilities * 100.
|
---|
63 | this.state = this.STATE_ACTIVE;
|
---|
64 | this.formations = this.template.Formations._string.split(" ");
|
---|
65 | this.startCam = undefined;
|
---|
66 | this.controlAllUnits = false;
|
---|
67 | this.isAI = false;
|
---|
68 | this.cheatsEnabled = false;
|
---|
69 | this.panelEntities = [];
|
---|
70 | this.resourceNames = {};
|
---|
71 | this.disabledTemplates = {};
|
---|
72 | this.disabledTechnologies = {};
|
---|
73 | this.spyCostMultiplier = +this.template.SpyCostMultiplier;
|
---|
74 | this.barterEntities = [];
|
---|
75 | this.barterMultiplier = {
|
---|
76 | "buy": clone(this.template.BarterMultiplier.Buy),
|
---|
77 | "sell": clone(this.template.BarterMultiplier.Sell)
|
---|
78 | };
|
---|
79 |
|
---|
80 | // Initial resources.
|
---|
81 | let resCodes = Resources.GetCodes();
|
---|
82 | for (let res of resCodes)
|
---|
83 | {
|
---|
84 | this.resourceCount[res] = 300;
|
---|
85 | this.resourceNames[res] = Resources.GetResource(res).name;
|
---|
86 | this.resourceGatherers[res] = 0;
|
---|
87 | }
|
---|
88 | // Trading goods probability in steps of 5.
|
---|
89 | let resTradeCodes = Resources.GetTradableCodes();
|
---|
90 | let quotient = Math.floor(20 / resTradeCodes.length);
|
---|
91 | let remainder = 20 % resTradeCodes.length;
|
---|
92 | for (let i in resTradeCodes)
|
---|
93 | this.tradingGoods.push({
|
---|
94 | "goods": resTradeCodes[i],
|
---|
95 | "proba": 5 * (quotient + (+i < remainder ? 1 : 0))
|
---|
96 | });
|
---|
97 | };
|
---|
98 |
|
---|
99 | Player.prototype.SetPlayerID = function(id)
|
---|
100 | {
|
---|
101 | this.playerID = id;
|
---|
102 | };
|
---|
103 |
|
---|
104 | Player.prototype.GetPlayerID = function()
|
---|
105 | {
|
---|
106 | return this.playerID;
|
---|
107 | };
|
---|
108 |
|
---|
109 | Player.prototype.SetColor = function(r, g, b)
|
---|
110 | {
|
---|
111 | let colorInitialized = !!this.color;
|
---|
112 |
|
---|
113 | this.color = { "r": r / 255, "g": g / 255, "b": b / 255, "a": 1 };
|
---|
114 |
|
---|
115 | // Used in Atlas.
|
---|
116 | if (colorInitialized)
|
---|
117 | Engine.BroadcastMessage(MT_PlayerColorChanged, {
|
---|
118 | "player": this.playerID
|
---|
119 | });
|
---|
120 | };
|
---|
121 |
|
---|
122 | Player.prototype.SetDisplayDiplomacyColor = function(displayDiplomacyColor)
|
---|
123 | {
|
---|
124 | this.displayDiplomacyColor = displayDiplomacyColor;
|
---|
125 | };
|
---|
126 |
|
---|
127 | Player.prototype.GetColor = function()
|
---|
128 | {
|
---|
129 | return this.color;
|
---|
130 | };
|
---|
131 |
|
---|
132 | Player.prototype.GetDisplayedColor = function()
|
---|
133 | {
|
---|
134 | return this.displayDiplomacyColor ? Engine.QueryInterface(this.entity, IID_Diplomacy).GetColor() : this.color;
|
---|
135 | };
|
---|
136 |
|
---|
137 | // Try reserving num population slots. Returns 0 on success or number of missing slots otherwise.
|
---|
138 | Player.prototype.TryReservePopulationSlots = function(num)
|
---|
139 | {
|
---|
140 | if (num != 0 && num > (this.GetPopulationLimit() - this.popUsed))
|
---|
141 | return num - (this.GetPopulationLimit() - this.popUsed);
|
---|
142 |
|
---|
143 | this.popUsed += num;
|
---|
144 | return 0;
|
---|
145 | };
|
---|
146 |
|
---|
147 | Player.prototype.UnReservePopulationSlots = function(num)
|
---|
148 | {
|
---|
149 | this.popUsed -= num;
|
---|
150 | };
|
---|
151 |
|
---|
152 | Player.prototype.GetPopulationCount = function()
|
---|
153 | {
|
---|
154 | return this.popUsed;
|
---|
155 | };
|
---|
156 |
|
---|
157 | Player.prototype.AddPopulation = function(num)
|
---|
158 | {
|
---|
159 | this.popUsed += num;
|
---|
160 | };
|
---|
161 |
|
---|
162 | Player.prototype.SetPopulationBonuses = function(num)
|
---|
163 | {
|
---|
164 | this.popBonuses = num;
|
---|
165 | };
|
---|
166 |
|
---|
167 | Player.prototype.AddPopulationBonuses = function(num)
|
---|
168 | {
|
---|
169 | this.popBonuses += num;
|
---|
170 | };
|
---|
171 |
|
---|
172 | Player.prototype.GetPopulationLimit = function()
|
---|
173 | {
|
---|
174 | return Math.min(this.GetMaxPopulation(), this.popBonuses);
|
---|
175 | };
|
---|
176 |
|
---|
177 | Player.prototype.SetMaxPopulation = function(max)
|
---|
178 | {
|
---|
179 | this.maxPop = max;
|
---|
180 | };
|
---|
181 |
|
---|
182 | Player.prototype.GetMaxPopulation = function()
|
---|
183 | {
|
---|
184 | return Math.round(ApplyValueModificationsToEntity("Player/MaxPopulation", this.maxPop, this.entity));
|
---|
185 | };
|
---|
186 |
|
---|
187 | Player.prototype.CanBarter = function()
|
---|
188 | {
|
---|
189 | return this.barterEntities.length > 0;
|
---|
190 | };
|
---|
191 |
|
---|
192 | Player.prototype.GetBarterMultiplier = function()
|
---|
193 | {
|
---|
194 | return this.barterMultiplier;
|
---|
195 | };
|
---|
196 |
|
---|
197 | Player.prototype.GetSpyCostMultiplier = function()
|
---|
198 | {
|
---|
199 | return this.spyCostMultiplier;
|
---|
200 | };
|
---|
201 |
|
---|
202 | Player.prototype.GetPanelEntities = function()
|
---|
203 | {
|
---|
204 | return this.panelEntities;
|
---|
205 | };
|
---|
206 |
|
---|
207 | Player.prototype.IsTrainingBlocked = function()
|
---|
208 | {
|
---|
209 | return this.trainingBlocked;
|
---|
210 | };
|
---|
211 |
|
---|
212 | Player.prototype.BlockTraining = function()
|
---|
213 | {
|
---|
214 | this.trainingBlocked = true;
|
---|
215 | };
|
---|
216 |
|
---|
217 | Player.prototype.UnBlockTraining = function()
|
---|
218 | {
|
---|
219 | this.trainingBlocked = false;
|
---|
220 | };
|
---|
221 |
|
---|
222 | Player.prototype.SetResourceCounts = function(resources)
|
---|
223 | {
|
---|
224 | for (let res in resources)
|
---|
225 | this.resourceCount[res] = resources[res];
|
---|
226 | };
|
---|
227 |
|
---|
228 | Player.prototype.GetResourceCounts = function()
|
---|
229 | {
|
---|
230 | return this.resourceCount;
|
---|
231 | };
|
---|
232 |
|
---|
233 | Player.prototype.GetResourceGatherers = function()
|
---|
234 | {
|
---|
235 | return this.resourceGatherers;
|
---|
236 | };
|
---|
237 |
|
---|
238 | /**
|
---|
239 | * @param {string} type - The generic type of resource to add the gatherer for.
|
---|
240 | */
|
---|
241 | Player.prototype.AddResourceGatherer = function(type)
|
---|
242 | {
|
---|
243 | ++this.resourceGatherers[type];
|
---|
244 | };
|
---|
245 |
|
---|
246 | /**
|
---|
247 | * @param {string} type - The generic type of resource to remove the gatherer from.
|
---|
248 | */
|
---|
249 | Player.prototype.RemoveResourceGatherer = function(type)
|
---|
250 | {
|
---|
251 | --this.resourceGatherers[type];
|
---|
252 | };
|
---|
253 |
|
---|
254 | /**
|
---|
255 | * Add resource of specified type to player.
|
---|
256 | * @param {string} type - Generic type of resource.
|
---|
257 | * @param {number} amount - Amount of resource, which should be added.
|
---|
258 | */
|
---|
259 | Player.prototype.AddResource = function(type, amount)
|
---|
260 | {
|
---|
261 | this.resourceCount[type] += +amount;
|
---|
262 | };
|
---|
263 |
|
---|
264 | /**
|
---|
265 | * Add resources to player.
|
---|
266 | */
|
---|
267 | Player.prototype.AddResources = function(amounts)
|
---|
268 | {
|
---|
269 | for (let type in amounts)
|
---|
270 | this.resourceCount[type] += +amounts[type];
|
---|
271 | };
|
---|
272 |
|
---|
273 | Player.prototype.GetNeededResources = function(amounts)
|
---|
274 | {
|
---|
275 | // Check if we can afford it all.
|
---|
276 | let amountsNeeded = {};
|
---|
277 | for (let type in amounts)
|
---|
278 | if (this.resourceCount[type] != undefined && amounts[type] > this.resourceCount[type])
|
---|
279 | amountsNeeded[type] = amounts[type] - Math.floor(this.resourceCount[type]);
|
---|
280 |
|
---|
281 | if (Object.keys(amountsNeeded).length == 0)
|
---|
282 | return undefined;
|
---|
283 | return amountsNeeded;
|
---|
284 | };
|
---|
285 |
|
---|
286 | Player.prototype.SubtractResourcesOrNotify = function(amounts)
|
---|
287 | {
|
---|
288 | let amountsNeeded = this.GetNeededResources(amounts);
|
---|
289 |
|
---|
290 | // If we don't have enough resources, send a notification to the player.
|
---|
291 | if (amountsNeeded)
|
---|
292 | {
|
---|
293 | let parameters = {};
|
---|
294 | let i = 0;
|
---|
295 | for (let type in amountsNeeded)
|
---|
296 | {
|
---|
297 | ++i;
|
---|
298 | parameters["resourceType" + i] = this.resourceNames[type];
|
---|
299 | parameters["resourceAmount" + i] = amountsNeeded[type];
|
---|
300 | }
|
---|
301 |
|
---|
302 | let msg = "";
|
---|
303 | // When marking strings for translations, you need to include the actual string,
|
---|
304 | // not some way to derive the string.
|
---|
305 | if (i < 1)
|
---|
306 | warn("Amounts needed but no amounts given?");
|
---|
307 | else if (i == 1)
|
---|
308 | msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s");
|
---|
309 | else if (i == 2)
|
---|
310 | msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s");
|
---|
311 | else if (i == 3)
|
---|
312 | msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s");
|
---|
313 | else if (i == 4)
|
---|
314 | msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s, %(resourceAmount4)s %(resourceType4)s");
|
---|
315 | else
|
---|
316 | warn("Localisation: Strings are not localised for more than 4 resources");
|
---|
317 |
|
---|
318 | // Send as time-notification.
|
---|
319 | let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
---|
320 | cmpGUIInterface.PushNotification({
|
---|
321 | "players": [this.playerID],
|
---|
322 | "message": msg,
|
---|
323 | "parameters": parameters,
|
---|
324 | "translateMessage": true,
|
---|
325 | "translateParameters": {
|
---|
326 | "resourceType1": "withinSentence",
|
---|
327 | "resourceType2": "withinSentence",
|
---|
328 | "resourceType3": "withinSentence",
|
---|
329 | "resourceType4": "withinSentence"
|
---|
330 | }
|
---|
331 | });
|
---|
332 | return false;
|
---|
333 | }
|
---|
334 |
|
---|
335 | for (let type in amounts)
|
---|
336 | this.resourceCount[type] -= amounts[type];
|
---|
337 |
|
---|
338 | return true;
|
---|
339 | };
|
---|
340 |
|
---|
341 | Player.prototype.TrySubtractResources = function(amounts)
|
---|
342 | {
|
---|
343 | if (!this.SubtractResourcesOrNotify(amounts))
|
---|
344 | return false;
|
---|
345 |
|
---|
346 | let cmpStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
|
---|
347 | if (cmpStatisticsTracker)
|
---|
348 | for (let type in amounts)
|
---|
349 | cmpStatisticsTracker.IncreaseResourceUsedCounter(type, amounts[type]);
|
---|
350 |
|
---|
351 | return true;
|
---|
352 | };
|
---|
353 |
|
---|
354 | Player.prototype.RefundResources = function(amounts)
|
---|
355 | {
|
---|
356 | const cmpStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
|
---|
357 | if (cmpStatisticsTracker)
|
---|
358 | for (const type in amounts)
|
---|
359 | cmpStatisticsTracker.IncreaseResourceUsedCounter(type, -amounts[type]);
|
---|
360 |
|
---|
361 | this.AddResources(amounts);
|
---|
362 | };
|
---|
363 |
|
---|
364 | Player.prototype.GetNextTradingGoods = function()
|
---|
365 | {
|
---|
366 | let value = randFloat(0, 100);
|
---|
367 | let last = this.tradingGoods.length - 1;
|
---|
368 | let sumProba = 0;
|
---|
369 | for (let i = 0; i < last; ++i)
|
---|
370 | {
|
---|
371 | sumProba += this.tradingGoods[i].proba;
|
---|
372 | if (value < sumProba)
|
---|
373 | return this.tradingGoods[i].goods;
|
---|
374 | }
|
---|
375 | return this.tradingGoods[last].goods;
|
---|
376 | };
|
---|
377 |
|
---|
378 | Player.prototype.GetTradingGoods = function()
|
---|
379 | {
|
---|
380 | let tradingGoods = {};
|
---|
381 | for (let resource of this.tradingGoods)
|
---|
382 | tradingGoods[resource.goods] = resource.proba;
|
---|
383 |
|
---|
384 | return tradingGoods;
|
---|
385 | };
|
---|
386 |
|
---|
387 | Player.prototype.SetTradingGoods = function(tradingGoods)
|
---|
388 | {
|
---|
389 | let resTradeCodes = Resources.GetTradableCodes();
|
---|
390 | let sumProba = 0;
|
---|
391 | for (let resource in tradingGoods)
|
---|
392 | {
|
---|
393 | if (resTradeCodes.indexOf(resource) == -1 || tradingGoods[resource] < 0)
|
---|
394 | {
|
---|
395 | error("Invalid trading goods: " + uneval(tradingGoods));
|
---|
396 | return;
|
---|
397 | }
|
---|
398 | sumProba += tradingGoods[resource];
|
---|
399 | }
|
---|
400 |
|
---|
401 | if (sumProba != 100)
|
---|
402 | {
|
---|
403 | error("Invalid trading goods probability: " + uneval(sumProba));
|
---|
404 | return;
|
---|
405 | }
|
---|
406 |
|
---|
407 | this.tradingGoods = [];
|
---|
408 | for (let resource in tradingGoods)
|
---|
409 | this.tradingGoods.push({
|
---|
410 | "goods": resource,
|
---|
411 | "proba": tradingGoods[resource]
|
---|
412 | });
|
---|
413 | };
|
---|
414 |
|
---|
415 | /**
|
---|
416 | * @param {string} message - The message to send in the chat. May be undefined.
|
---|
417 | */
|
---|
418 | Player.prototype.Win = function(message)
|
---|
419 | {
|
---|
420 | this.SetState(this.STATE_WON, message);
|
---|
421 | };
|
---|
422 |
|
---|
423 | /**
|
---|
424 | * @param {string} message - The message to send in the chat. May be undefined.
|
---|
425 | */
|
---|
426 | Player.prototype.Defeat = function(message)
|
---|
427 | {
|
---|
428 | this.SetState(this.STATE_DEFEATED, message);
|
---|
429 | };
|
---|
430 |
|
---|
431 | /**
|
---|
432 | * @return {string} - The string identified with the current state.
|
---|
433 | */
|
---|
434 | Player.prototype.GetState = function()
|
---|
435 | {
|
---|
436 | return this.state;
|
---|
437 | };
|
---|
438 |
|
---|
439 | /**
|
---|
440 | * @return {boolean} -
|
---|
441 | */
|
---|
442 | Player.prototype.IsActive = function()
|
---|
443 | {
|
---|
444 | return this.state === this.STATE_ACTIVE;
|
---|
445 | };
|
---|
446 |
|
---|
447 | /**
|
---|
448 | * @return {boolean} -
|
---|
449 | */
|
---|
450 | Player.prototype.IsDefeated = function()
|
---|
451 | {
|
---|
452 | return this.state === this.STATE_DEFEATED;
|
---|
453 | };
|
---|
454 |
|
---|
455 | /**
|
---|
456 | * @return {boolean} -
|
---|
457 | */
|
---|
458 | Player.prototype.HasWon = function()
|
---|
459 | {
|
---|
460 | return this.state === this.STATE_WON;
|
---|
461 | };
|
---|
462 |
|
---|
463 | /**
|
---|
464 | * @param {string} newState - Either "defeated" or "won".
|
---|
465 | * @param {string|undefined} message - A string to be shown in chat, for example
|
---|
466 | * markForTranslation("%(player)s has been defeated (failed objective).").
|
---|
467 | * If it is undefined, the caller MUST send that GUI notification manually.
|
---|
468 | */
|
---|
469 | Player.prototype.SetState = function(newState, message)
|
---|
470 | {
|
---|
471 | if (!this.IsActive())
|
---|
472 | return;
|
---|
473 |
|
---|
474 | if (newState != this.STATE_WON && newState != this.STATE_DEFEATED)
|
---|
475 | {
|
---|
476 | warn("Can't change playerstate to " + newState);
|
---|
477 | return;
|
---|
478 | }
|
---|
479 |
|
---|
480 | if (!this.playerID)
|
---|
481 | {
|
---|
482 | warn("Gaia can't change state.");
|
---|
483 | return;
|
---|
484 | }
|
---|
485 |
|
---|
486 | this.state = newState;
|
---|
487 |
|
---|
488 | const won = this.HasWon();
|
---|
489 | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
---|
490 | if (won)
|
---|
491 | cmpRangeManager.SetLosRevealAll(this.playerID, true);
|
---|
492 | else
|
---|
493 | {
|
---|
494 | // Reassign all player's entities to Gaia.
|
---|
495 | let entities = cmpRangeManager.GetEntitiesByPlayer(this.playerID);
|
---|
496 |
|
---|
497 | // The ownership change is done in two steps so that entities don't hit idle
|
---|
498 | // (and thus possibly look for "enemies" to attack) before nearby allies get
|
---|
499 | // converted to Gaia as well.
|
---|
500 | for (let entity of entities)
|
---|
501 | {
|
---|
502 | let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
|
---|
503 | cmpOwnership.SetOwnerQuiet(0);
|
---|
504 | }
|
---|
505 |
|
---|
506 | // With the real ownership change complete, send OwnershipChanged messages.
|
---|
507 | for (let entity of entities)
|
---|
508 | Engine.PostMessage(entity, MT_OwnershipChanged, {
|
---|
509 | "entity": entity,
|
---|
510 | "from": this.playerID,
|
---|
511 | "to": 0
|
---|
512 | });
|
---|
513 | }
|
---|
514 |
|
---|
515 | if (message)
|
---|
516 | Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({
|
---|
517 | "type": won ? "won" : "defeat",
|
---|
518 | "players": [this.playerID],
|
---|
519 | "allies": [this.playerID],
|
---|
520 | "message": message
|
---|
521 | });
|
---|
522 |
|
---|
523 | Engine.PostMessage(this.entity, won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
|
---|
524 | };
|
---|
525 |
|
---|
526 | Player.prototype.GetFormations = function()
|
---|
527 | {
|
---|
528 | return this.formations;
|
---|
529 | };
|
---|
530 |
|
---|
531 | Player.prototype.SetFormations = function(formations)
|
---|
532 | {
|
---|
533 | this.formations = formations;
|
---|
534 | };
|
---|
535 |
|
---|
536 | Player.prototype.GetStartingCameraPos = function()
|
---|
537 | {
|
---|
538 | return this.startCam.position;
|
---|
539 | };
|
---|
540 |
|
---|
541 | Player.prototype.GetStartingCameraRot = function()
|
---|
542 | {
|
---|
543 | return this.startCam.rotation;
|
---|
544 | };
|
---|
545 |
|
---|
546 | Player.prototype.SetStartingCamera = function(pos, rot)
|
---|
547 | {
|
---|
548 | this.startCam = { "position": pos, "rotation": rot };
|
---|
549 | };
|
---|
550 |
|
---|
551 | Player.prototype.HasStartingCamera = function()
|
---|
552 | {
|
---|
553 | return this.startCam !== undefined;
|
---|
554 | };
|
---|
555 |
|
---|
556 | Player.prototype.SetControlAllUnits = function(c)
|
---|
557 | {
|
---|
558 | this.controlAllUnits = c;
|
---|
559 | };
|
---|
560 |
|
---|
561 | Player.prototype.CanControlAllUnits = function()
|
---|
562 | {
|
---|
563 | return this.controlAllUnits;
|
---|
564 | };
|
---|
565 |
|
---|
566 | Player.prototype.SetAI = function(flag)
|
---|
567 | {
|
---|
568 | this.isAI = flag;
|
---|
569 | };
|
---|
570 |
|
---|
571 | Player.prototype.IsAI = function()
|
---|
572 | {
|
---|
573 | return this.isAI;
|
---|
574 | };
|
---|
575 |
|
---|
576 | /**
|
---|
577 | * Do some map dependant initializations
|
---|
578 | */
|
---|
579 | Player.prototype.OnGlobalInitGame = function(msg)
|
---|
580 | {
|
---|
581 | // Replace the "{civ}" code with this civ ID.
|
---|
582 | let disabledTemplates = this.disabledTemplates;
|
---|
583 | this.disabledTemplates = {};
|
---|
584 | const civ = Engine.QueryInterface(this.entity, IID_Identity).GetCiv();
|
---|
585 | for (let template in disabledTemplates)
|
---|
586 | if (disabledTemplates[template])
|
---|
587 | this.disabledTemplates[template.replace(/\{civ\}/g, civ)] = true;
|
---|
588 | };
|
---|
589 |
|
---|
590 | /**
|
---|
591 | * Keep track of population effects of all entities that
|
---|
592 | * become owned or unowned by this player.
|
---|
593 | */
|
---|
594 | Player.prototype.OnGlobalOwnershipChanged = function(msg)
|
---|
595 | {
|
---|
596 | if (msg.from != this.playerID && msg.to != this.playerID)
|
---|
597 | return;
|
---|
598 |
|
---|
599 | let cmpCost = Engine.QueryInterface(msg.entity, IID_Cost);
|
---|
600 |
|
---|
601 | if (msg.from == this.playerID)
|
---|
602 | {
|
---|
603 | if (cmpCost)
|
---|
604 | this.popUsed -= cmpCost.GetPopCost();
|
---|
605 |
|
---|
606 | let panelIndex = this.panelEntities.indexOf(msg.entity);
|
---|
607 | if (panelIndex >= 0)
|
---|
608 | this.panelEntities.splice(panelIndex, 1);
|
---|
609 |
|
---|
610 | let barterIndex = this.barterEntities.indexOf(msg.entity);
|
---|
611 | if (barterIndex >= 0)
|
---|
612 | this.barterEntities.splice(barterIndex, 1);
|
---|
613 | }
|
---|
614 | if (msg.to == this.playerID)
|
---|
615 | {
|
---|
616 | if (cmpCost)
|
---|
617 | this.popUsed += cmpCost.GetPopCost();
|
---|
618 |
|
---|
619 | let cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
|
---|
620 | if (!cmpIdentity)
|
---|
621 | return;
|
---|
622 |
|
---|
623 | if (MatchesClassList(cmpIdentity.GetClassesList(), panelEntityClasses))
|
---|
624 | this.panelEntities.push(msg.entity);
|
---|
625 |
|
---|
626 | if (cmpIdentity.HasClass("Barter") && !Engine.QueryInterface(msg.entity, IID_Foundation))
|
---|
627 | this.barterEntities.push(msg.entity);
|
---|
628 | }
|
---|
629 | };
|
---|
630 |
|
---|
631 | Player.prototype.OnValueModification = function(msg)
|
---|
632 | {
|
---|
633 | if (msg.component != "Player")
|
---|
634 | return;
|
---|
635 |
|
---|
636 | if (msg.valueNames.indexOf("Player/SpyCostMultiplier") != -1)
|
---|
637 | this.spyCostMultiplier = ApplyValueModificationsToEntity("Player/SpyCostMultiplier", +this.template.SpyCostMultiplier, this.entity);
|
---|
638 |
|
---|
639 | if (msg.valueNames.some(mod => mod.startsWith("Player/BarterMultiplier/")))
|
---|
640 | for (let res in this.template.BarterMultiplier.Buy)
|
---|
641 | {
|
---|
642 | this.barterMultiplier.buy[res] = ApplyValueModificationsToEntity("Player/BarterMultiplier/Buy/"+res, +this.template.BarterMultiplier.Buy[res], this.entity);
|
---|
643 | this.barterMultiplier.sell[res] = ApplyValueModificationsToEntity("Player/BarterMultiplier/Sell/"+res, +this.template.BarterMultiplier.Sell[res], this.entity);
|
---|
644 | }
|
---|
645 | };
|
---|
646 |
|
---|
647 | Player.prototype.SetCheatsEnabled = function(flag)
|
---|
648 | {
|
---|
649 | this.cheatsEnabled = flag;
|
---|
650 | };
|
---|
651 |
|
---|
652 | Player.prototype.GetCheatsEnabled = function()
|
---|
653 | {
|
---|
654 | return this.cheatsEnabled;
|
---|
655 | };
|
---|
656 |
|
---|
657 | Player.prototype.TributeResource = function(player, amounts)
|
---|
658 | {
|
---|
659 | let cmpPlayer = QueryPlayerIDInterface(player);
|
---|
660 | if (!cmpPlayer)
|
---|
661 | return;
|
---|
662 |
|
---|
663 | if (!this.IsActive() || !cmpPlayer.IsActive())
|
---|
664 | return;
|
---|
665 |
|
---|
666 | let resTribCodes = Resources.GetTributableCodes();
|
---|
667 | for (let resCode in amounts)
|
---|
668 | if (resTribCodes.indexOf(resCode) == -1 ||
|
---|
669 | !Number.isInteger(amounts[resCode]) ||
|
---|
670 | amounts[resCode] < 0)
|
---|
671 | {
|
---|
672 | warn("Invalid tribute amounts: " + uneval(resCode) + ": " + uneval(amounts));
|
---|
673 | return;
|
---|
674 | }
|
---|
675 |
|
---|
676 | if (!this.SubtractResourcesOrNotify(amounts))
|
---|
677 | return;
|
---|
678 | cmpPlayer.AddResources(amounts);
|
---|
679 |
|
---|
680 | let total = Object.keys(amounts).reduce((sum, type) => sum + amounts[type], 0);
|
---|
681 | let cmpOurStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
|
---|
682 | if (cmpOurStatisticsTracker)
|
---|
683 | cmpOurStatisticsTracker.IncreaseTributesSentCounter(total);
|
---|
684 | let cmpTheirStatisticsTracker = QueryPlayerIDInterface(player, IID_StatisticsTracker);
|
---|
685 | if (cmpTheirStatisticsTracker)
|
---|
686 | cmpTheirStatisticsTracker.IncreaseTributesReceivedCounter(total);
|
---|
687 |
|
---|
688 | let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
---|
689 | if (cmpGUIInterface)
|
---|
690 | cmpGUIInterface.PushNotification({
|
---|
691 | "type": "tribute",
|
---|
692 | "players": [player],
|
---|
693 | "donator": this.playerID,
|
---|
694 | "amounts": amounts
|
---|
695 | });
|
---|
696 |
|
---|
697 | Engine.BroadcastMessage(MT_TributeExchanged, {
|
---|
698 | "to": player,
|
---|
699 | "from": this.playerID,
|
---|
700 | "amounts": amounts
|
---|
701 | });
|
---|
702 | };
|
---|
703 |
|
---|
704 | Player.prototype.AddDisabledTemplate = function(template)
|
---|
705 | {
|
---|
706 | this.disabledTemplates[template] = true;
|
---|
707 | Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
|
---|
708 | };
|
---|
709 |
|
---|
710 | Player.prototype.RemoveDisabledTemplate = function(template)
|
---|
711 | {
|
---|
712 | this.disabledTemplates[template] = false;
|
---|
713 | Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
|
---|
714 | };
|
---|
715 |
|
---|
716 | Player.prototype.SetDisabledTemplates = function(templates)
|
---|
717 | {
|
---|
718 | this.disabledTemplates = {};
|
---|
719 | for (let template of templates)
|
---|
720 | this.disabledTemplates[template] = true;
|
---|
721 | Engine.BroadcastMessage(MT_DisabledTemplatesChanged, { "player": this.playerID });
|
---|
722 | };
|
---|
723 |
|
---|
724 | Player.prototype.GetDisabledTemplates = function()
|
---|
725 | {
|
---|
726 | return this.disabledTemplates;
|
---|
727 | };
|
---|
728 |
|
---|
729 | Player.prototype.AddDisabledTechnology = function(tech)
|
---|
730 | {
|
---|
731 | this.disabledTechnologies[tech] = true;
|
---|
732 | Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
|
---|
733 | };
|
---|
734 |
|
---|
735 | Player.prototype.RemoveDisabledTechnology = function(tech)
|
---|
736 | {
|
---|
737 | this.disabledTechnologies[tech] = false;
|
---|
738 | Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
|
---|
739 | };
|
---|
740 |
|
---|
741 | Player.prototype.SetDisabledTechnologies = function(techs)
|
---|
742 | {
|
---|
743 | this.disabledTechnologies = {};
|
---|
744 | for (let tech of techs)
|
---|
745 | this.disabledTechnologies[tech] = true;
|
---|
746 | Engine.BroadcastMessage(MT_DisabledTechnologiesChanged, { "player": this.playerID });
|
---|
747 | };
|
---|
748 |
|
---|
749 | Player.prototype.GetDisabledTechnologies = function()
|
---|
750 | {
|
---|
751 | return this.disabledTechnologies;
|
---|
752 | };
|
---|
753 |
|
---|
754 | Player.prototype.OnGlobalPlayerDefeated = function(msg)
|
---|
755 | {
|
---|
756 | let cmpSound = Engine.QueryInterface(this.entity, IID_Sound);
|
---|
757 | if (!cmpSound)
|
---|
758 | return;
|
---|
759 |
|
---|
760 | const soundGroup = cmpSound.GetSoundGroup(this.playerID === msg.playerId ? "defeated" : Engine.QueryInterface(this.entity, IID_Diplomacy).IsAlly(msg.playerId) ? "defeated_ally" : this.HasWon() ? "won" : "defeated_enemy");
|
---|
761 | if (soundGroup)
|
---|
762 | Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager).PlaySoundGroupForPlayer(soundGroup, this.playerID);
|
---|
763 | };
|
---|
764 |
|
---|
765 | Engine.RegisterComponentType(IID_Player, "Player", Player);
|
---|