1
0
forked from 0ad/0ad

Pull Diplomacy out of cmpPlayer.

Who says only players should be able to conduct diplomacy?
Also separation of concerns, more maintainable files.

Differential revision: https://code.wildfiregames.com/D4921
Comments by: @elexis, @Stan
Refs. #5894

This was SVN commit r27722.
This commit is contained in:
Freagarach 2023-06-19 06:33:33 +00:00
parent 54c606e6aa
commit c917c39a0d
45 changed files with 664 additions and 450 deletions

View File

@ -11,7 +11,7 @@ DiplomacyDialogPlayerControl.prototype.StanceButtonManager = class
update(playerInactive)
{
let hidden = playerInactive || GetSimState().ceasefireActive || g_Players[g_ViewedPlayer].teamsLocked;
let hidden = playerInactive || GetSimState().ceasefireActive || g_Players[g_ViewedPlayer].teamLocked;
for (let button of this.buttons)
button.update(hidden);

View File

@ -9,7 +9,7 @@ LobbyRatingReport.prototype.Players = class
"playerStates": playerStates.map(playerState => playerState.state).join(",") + ",",
"civs": playerStates.map(playerState => playerState.civ).join(",") + ",",
"teams": playerStates.map(playerState => playerState.team).join(",") + ",",
"teamsLocked": String(playerStates.every(playerState => playerState.teamsLocked))
"teamsLocked": String(playerStates.every(playerState => playerState.teamLocked))
});
}
};

View File

@ -391,7 +391,7 @@ function updatePlayerData()
"a": playerState.color.a * 255
},
"team": playerState.team,
"teamsLocked": playerState.teamsLocked,
"teamLocked": playerState.teamLocked,
"cheatsEnabled": playerState.cheatsEnabled,
"state": playerState.state,
"isAlly": playerState.isAlly,

View File

@ -67,7 +67,7 @@ Trigger.prototype.CheckCaptureTheRelicCountdown = function()
}
let winningPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).GetAlliedVictory() ?
activePlayers.filter(playerID => relicOwners.every(owner => QueryPlayerIDInterface(playerID).IsMutualAlly(owner))) :
activePlayers.filter(playerID => relicOwners.every(owner => QueryPlayerIDInterface(playerID, IID_Diplomacy).IsMutualAlly(owner))) :
[relicOwners[0]];
// All relicOwners should be mutually allied

View File

@ -36,7 +36,7 @@ Trigger.prototype.WonderVictoryDiplomacyChanged = function(data)
let owner = this.wonderVictoryMessages[ent].playerID;
let otherPlayer = owner == data.player ? data.otherPlayer : data.player;
let newAllies = new Set(QueryPlayerIDInterface(owner).GetPlayersByDiplomacy("IsExclusiveMutualAlly"));
let newAllies = new Set(QueryPlayerIDInterface(owner, IID_Diplomacy).GetPlayersByDiplomacy("IsExclusiveMutualAlly"));
if (newAllies.has(otherPlayer) && !this.wonderVictoryMessages[ent].allies.has(otherPlayer) ||
!newAllies.has(otherPlayer) && this.wonderVictoryMessages[ent].allies.has(otherPlayer))
{
@ -53,7 +53,7 @@ Trigger.prototype.WonderVictoryStartTimer = function(ent, player)
{
let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
let allies = cmpEndGameManager.GetAlliedVictory() ?
QueryPlayerIDInterface(player).GetPlayersByDiplomacy("IsExclusiveMutualAlly") : [];
QueryPlayerIDInterface(player, IID_Diplomacy).GetPlayersByDiplomacy("IsExclusiveMutualAlly") : [];
let others = [-1];
for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID)

View File

@ -37,8 +37,8 @@ AlertRaiser.prototype.RaiseAlert = function()
return;
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
const cmpDiplomacy = QueryPlayerIDInterface(owner, IID_Diplomacy);
const mutualAllies = cmpDiplomacy ? cmpDiplomacy.GetMutualAllies() : [owner];
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// Store the number of available garrison spots so that units don't try to garrison in buildings that will be full
@ -98,8 +98,8 @@ AlertRaiser.prototype.EndOfAlert = function()
return;
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let mutualAllies = cmpPlayer ? cmpPlayer.GetMutualAllies() : [owner];
const cmpDiplomacy = QueryPlayerIDInterface(owner, IID_Diplomacy);
const mutualAllies = cmpDiplomacy ? cmpDiplomacy.GetMutualAllies() : [owner];
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// Units that are not garrisoned should go back to work

View File

@ -270,6 +270,7 @@ Attack.prototype.CanAttack = function(target, wantedTypes)
const entityOwner = cmpEntityPlayer.GetPlayerID();
const targetOwner = cmpTargetPlayer.GetPlayerID();
const cmpCapturable = QueryMiragedInterface(target, IID_Capturable);
const cmpDiplomacy = QueryPlayerIDInterface(entityOwner, IID_Diplomacy);
// Check if the relative height difference is larger than the attack range
// If the relative height is bigger, it means they will never be able to
@ -278,7 +279,7 @@ Attack.prototype.CanAttack = function(target, wantedTypes)
for (const type of types)
{
if (type != "Capture" && (!cmpEntityPlayer.IsEnemy(targetOwner) || !cmpHealth || !cmpHealth.GetHitpoints()))
if (type != "Capture" && (!cmpDiplomacy?.IsEnemy(targetOwner) || !cmpHealth || !cmpHealth.GetHitpoints()))
continue;
if (type == "Capture" && (!cmpCapturable || !cmpCapturable.CanCapture(entityOwner)))

View File

@ -123,6 +123,10 @@ Auras.prototype.CalculateAffectedPlayers = function(name)
if (!cmpPlayer || cmpPlayer.IsDefeated())
return;
const playerID = cmpPlayer.GetPlayerID();
const cmpDiplomacy = Engine.QueryInterface(this.entity, IID_Diplomacy) ??
QueryPlayerIDInterface(playerID, IID_Diplomacy);
let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
for (let i of cmpPlayerManager.GetAllPlayers())
{
@ -130,7 +134,7 @@ Auras.prototype.CalculateAffectedPlayers = function(name)
if (!cmpAffectedPlayer || cmpAffectedPlayer.IsDefeated())
continue;
if (affectedPlayers.some(p => p == "Player" ? cmpPlayer.GetPlayerID() == i : cmpPlayer["Is" + p](i)))
if (affectedPlayers.some(p => p == "Player" ? playerID == i : cmpDiplomacy["Is" + p](i)))
this.affectedPlayers[name].push(i);
}
};

View File

@ -175,11 +175,16 @@ BuildRestrictions.prototype.CheckPlacement = function()
if (!cmpTerritoryManager || !cmpPosition || !cmpPosition.IsInWorld())
return result; // Fail
const playerID = cmpPlayer.GetPlayerID();
const cmpDiplomacy = QueryPlayerIDInterface(playerID, IID_Diplomacy);
if (!cmpDiplomacy)
return result;
var pos = cmpPosition.GetPosition2D();
var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
var isConnected = !cmpTerritoryManager.IsTerritoryBlinking(pos.x, pos.y);
var isOwn = tileOwner == cmpPlayer.GetPlayerID();
var isMutualAlly = cmpPlayer.IsExclusiveMutualAlly(tileOwner);
var isOwn = tileOwner == playerID;
var isMutualAlly = cmpDiplomacy.IsExclusiveMutualAlly(tileOwner);
var isNeutral = tileOwner == 0;
var invalidTerritory = "";

View File

@ -111,11 +111,11 @@ BuildingAI.prototype.SetupRangeQuery = function()
this.enemyUnitsQuery = undefined;
}
var cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer)
const cmpDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return;
var enemies = cmpPlayer.GetEnemies();
const enemies = cmpDiplomacy.GetEnemies();
// Remove gaia.
if (enemies.length && enemies[0] == 0)
enemies.shift();
@ -148,8 +148,7 @@ BuildingAI.prototype.SetupGaiaRangeQuery = function()
this.gaiaUnitsQuery = undefined;
}
var cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer || !cmpPlayer.IsEnemy(0))
if (!QueryOwnerInterface(this.entity, IID_Diplomacy)?.IsEnemy(0))
return;
const range = cmpAttack.GetRange(attackType);

View File

@ -83,8 +83,8 @@ Capturable.prototype.Reduce = function(amount, playerID)
if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
return 0;
let cmpPlayerSource = QueryPlayerIDInterface(playerID);
if (!cmpPlayerSource)
const cmpDiplomacySource = QueryPlayerIDInterface(playerID, IID_Diplomacy);
if (!cmpDiplomacySource)
return 0;
// Before changing the value, activate Fogging if necessary to hide changes.
@ -92,7 +92,7 @@ Capturable.prototype.Reduce = function(amount, playerID)
if (cmpFogging)
cmpFogging.Activate();
let numberOfEnemies = this.capturePoints.filter((v, i) => v > 0 && cmpPlayerSource.IsEnemy(i)).length;
let numberOfEnemies = this.capturePoints.filter((v, i) => v > 0 && cmpDiplomacySource.IsEnemy(i)).length;
if (numberOfEnemies == 0)
return 0;
@ -105,7 +105,7 @@ Capturable.prototype.Reduce = function(amount, playerID)
numberOfEnemies = 0;
for (let i in this.capturePoints)
{
if (!this.capturePoints[i] || !cmpPlayerSource.IsEnemy(i))
if (!this.capturePoints[i] || !cmpDiplomacySource.IsEnemy(i))
continue;
if (this.capturePoints[i] > distributedAmount)
{
@ -138,14 +138,14 @@ Capturable.prototype.Reduce = function(amount, playerID)
*/
Capturable.prototype.CanCapture = function(playerID)
{
let cmpPlayerSource = QueryPlayerIDInterface(playerID);
const cmpDiplomacySource = QueryPlayerIDInterface(playerID, IID_Diplomacy);
if (!cmpPlayerSource)
warn(playerID + " has no player component defined on its id.");
if (!cmpDiplomacySource)
warn(playerID + " has no diplomacy component defined on its id.");
let capturePoints = this.GetCapturePoints();
let sourceEnemyCapturePoints = 0;
for (let i in this.GetCapturePoints())
if (cmpPlayerSource.IsEnemy(i))
if (cmpDiplomacySource.IsEnemy(i))
sourceEnemyCapturePoints += capturePoints[i];
return sourceEnemyCapturePoints > 0;
};

View File

@ -71,13 +71,13 @@ CeasefireManager.prototype.StartCeasefire = function(ceasefireTime)
// Save diplomacy
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
this.diplomacyBeforeCeasefire.push(QueryPlayerIDInterface(i).GetDiplomacy());
this.diplomacyBeforeCeasefire.push(QueryPlayerIDInterface(i, IID_Diplomacy).GetDiplomacy());
// Set every enemy (except gaia) to neutral
for (let i = 1; i < numPlayers; ++i)
for (let j = 1; j < numPlayers; ++j)
if (this.diplomacyBeforeCeasefire[i-1][j] < 0)
QueryPlayerIDInterface(i).SetNeutral(j);
QueryPlayerIDInterface(i, IID_Diplomacy).SetNeutral(j);
}
this.ceasefireIsActive = true;
@ -116,7 +116,7 @@ CeasefireManager.prototype.StopCeasefire = function()
// Reset diplomacies to original settings
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
QueryPlayerIDInterface(i).SetDiplomacy(this.diplomacyBeforeCeasefire[i-1]);
QueryPlayerIDInterface(i, IID_Diplomacy).SetDiplomacy(this.diplomacyBeforeCeasefire[i-1]);
this.ceasefireIsActive = false;
this.ceasefireTime = 0;

View File

@ -0,0 +1,347 @@
function Diplomacy() {}
Diplomacy.prototype.Schema =
"<element name='SharedLosTech' a:help='Allies will share los when this technology is researched. Leave empty to never share LOS.'>" +
"<text/>" +
"</element>" +
"<element name='SharedDropsitesTech' a:help='Allies will share dropsites when this technology is researched. Leave empty to never share dropsites.'>" +
"<text/>" +
"</element>";
Diplomacy.prototype.SerializableAttributes = [
"team",
"teamLocked",
"diplomacy",
"sharedDropsites",
];
Diplomacy.prototype.Serialize = function()
{
const state = {};
for (const key in this.SerializableAttributes)
if (this.hasOwnProperty(key))
state[key] = this[key];
return state;
};
Diplomacy.prototype.Deserialize = function(state)
{
for (const prop in state)
this[prop] = state[prop];
};
Diplomacy.prototype.Init = function()
{
// 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.
this.team = -1;
// Array of diplomatic stances for this player with respect to other players (including gaia and self).
this.diplomacy = [];
};
/**
* @param {Object} color - r, g, b values of the diplomacy colour.
*/
Diplomacy.prototype.SetDiplomacyColor = function(color)
{
this.diplomacyColor = { "r": color.r / 255, "g": color.g / 255, "b": color.b / 255, "a": 1 };
};
/**
* @return {Object} -
*/
Diplomacy.prototype.GetColor = function()
{
return this.diplomacyColor;
};
/**
* @return {number} -
*/
Diplomacy.prototype.GetTeam = function()
{
return this.team;
};
/**
* @param {number} team - The new team number, -1 for no team.
*/
Diplomacy.prototype.ChangeTeam = function(team)
{
if (this.teamLocked || this.team === team)
return;
const playerID = Engine.QueryInterface(this.entity, IID_Player)?.GetPlayerID();
if (!playerID)
return;
// ToDo: Fix this.
if (this.team !== -1)
warn("A change in teams is requested while the player already had a team, previous alliances are maintained.");
this.team = team;
if (this.team !== -1)
{
const numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 0; i < numPlayers; ++i)
{
const cmpDiplomacy = QueryPlayerIDInterface(i, IID_Diplomacy);
if (this.team !== cmpDiplomacy.GetTeam())
continue;
this.Ally(i);
cmpDiplomacy.Ally(playerID);
}
}
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": playerID,
"otherPlayer": null
});
};
Diplomacy.prototype.LockTeam = function()
{
this.teamLocked = true;
};
Diplomacy.prototype.UnLockTeam = function()
{
delete this.teamLocked;
};
/**
* @return {boolean} -
*/
Diplomacy.prototype.IsTeamLocked = function()
{
return !!this.teamLocked;
};
/**
* @return {number[]} - Current diplomatic stances.
*/
Diplomacy.prototype.GetDiplomacy = function()
{
return this.diplomacy.slice();
};
/**
* @param {number[]} dipl - The diplomacy array to set.
*/
Diplomacy.prototype.SetDiplomacy = function(dipl)
{
const playerID = Engine.QueryInterface(this.entity, IID_Player)?.GetPlayerID();
if (!playerID)
return
this.diplomacy = dipl.slice();
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": playerID,
"otherPlayer": null
});
};
/**
* Helper function for allying etc.
* @param {number} idx - The player number.
* @param {number} value - The diplomacy value.
*/
Diplomacy.prototype.SetDiplomacyIndex = function(idx, value)
{
if (!QueryPlayerIDInterface(idx)?.IsActive())
return;
const cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer?.IsActive())
return;
this.diplomacy[idx] = value;
const playerID = cmpPlayer.GetPlayerID();
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": playerID,
"otherPlayer": idx,
"value": value
});
};
/**
* Helper function for getting allies etc.
* @param {string} func - Name of the function to test.
* @return {number[]} - Player IDs matching the function.
*/
Diplomacy.prototype.GetPlayersByDiplomacy = function(func)
{
const players = [];
for (let i = 0; i < this.diplomacy.length; ++i)
if (this[func](i))
players.push(i);
return players;
};
/**
* @param {number} - id
*/
Diplomacy.prototype.Ally = function(id)
{
this.SetDiplomacyIndex(id, 1);
};
/**
* Check if given player is our ally.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsAlly = function(id)
{
return this.diplomacy[id] > 0;
};
/**
* @return {number[]} -
*/
Diplomacy.prototype.GetAllies = function()
{
return this.GetPlayersByDiplomacy("IsAlly");
};
/**
* Check if given player is our ally excluding ourself.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsExclusiveAlly = function(id)
{
return Engine.QueryInterface(this.entity, IID_Player)?.GetPlayerID() !== id && this.IsAlly(id);
};
/**
* Check if given player is our ally, and we are its ally.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsMutualAlly = function(id)
{
const playerID = Engine.QueryInterface(this.entity, IID_Player)?.GetPlayerID();
return this.IsAlly(id) && playerID && QueryPlayerIDInterface(id, IID_Diplomacy)?.IsAlly(playerID);
};
/**
* @return {number[]} -
*/
Diplomacy.prototype.GetMutualAllies = function()
{
return this.GetPlayersByDiplomacy("IsMutualAlly");
};
/**
* Check if given player is our ally, and we are its ally, excluding ourself.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsExclusiveMutualAlly = function(id)
{
const playerID = Engine.QueryInterface(this.entity, IID_Player)?.GetPlayerID();
return playerID && playerID !== id && this.IsMutualAlly(id);
};
/**
* @param {number} id -
*/
Diplomacy.prototype.SetEnemy = function(id)
{
this.SetDiplomacyIndex(id, -1);
};
/**
* Check if given player is our enemy.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsEnemy = function(id)
{
return this.diplomacy[id] < 0;
};
/**
* @return {number[]} -
*/
Diplomacy.prototype.GetEnemies = function()
{
return this.GetPlayersByDiplomacy("IsEnemy");
};
/**
* @param {number} id -
*/
Diplomacy.prototype.SetNeutral = function(id)
{
this.SetDiplomacyIndex(id, 0);
};
/**
* Check if given player is neutral.
* @param {number} id -
* @return {boolean} -
*/
Diplomacy.prototype.IsNeutral = function(id)
{
return this.diplomacy[id] === 0;
};
/**
* @return {boolean} -
*/
Diplomacy.prototype.HasSharedDropsites = function()
{
return this.sharedDropsites;
};
/**
* @return {boolean} -
*/
Diplomacy.prototype.HasSharedLos = function()
{
const cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
return cmpTechnologyManager && cmpTechnologyManager.IsTechnologyResearched(this.template.SharedLosTech);
};
Diplomacy.prototype.UpdateSharedLos = function()
{
const playerID = Engine.QueryInterface(this.entity, IID_Player).GetPlayerID();
if (!playerID)
return;
Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager)?.
SetSharedLos(playerID, this.HasSharedLos() ? this.GetMutualAllies() : [playerID]);
};
Diplomacy.prototype.OnResearchFinished = function(msg)
{
if (msg.tech === this.template.SharedLosTech)
this.UpdateSharedLos();
else if (msg.tech === this.template.SharedDropsitesTech)
this.sharedDropsites = true;
};
Diplomacy.prototype.OnDiplomacyChanged = function(msg)
{
this.UpdateSharedLos();
if (msg.otherPlayer === null)
return;
const cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer || cmpPlayer.GetPlayerID() != msg.otherPlayer)
return;
// Mutual worsening of relations.
if (this.diplomacy[msg.player] > msg.value)
this.SetDiplomacyIndex(msg.player, msg.value);
};
Engine.RegisterComponentType(IID_Diplomacy, "Diplomacy", Diplomacy);

View File

@ -66,7 +66,7 @@ EndGameManager.prototype.MarkPlayerAndAlliesAsWon = function(playerID, victorySt
let winningPlayers = [playerID];
if (this.alliedVictory)
winningPlayers = cmpPlayer.GetMutualAllies(playerID).filter(
winningPlayers = QueryPlayerIDInterface(playerID, IID_Diplomacy).GetMutualAllies(playerID).filter(
player => QueryPlayerIDInterface(player).IsActive());
this.MarkPlayersAsWon(winningPlayers, victoryString, defeatString);
@ -148,7 +148,7 @@ EndGameManager.prototype.AlliedVictoryCheck = function()
if (!cmpPlayer.IsActive())
continue;
if (allies.length && !cmpPlayer.IsMutualAlly(allies[0]))
if (allies.length && !QueryPlayerIDInterface(playerID, IID_Diplomacy).IsMutualAlly(allies[0]))
return;
allies.push(playerID);

View File

@ -72,7 +72,7 @@ Gate.prototype.SetupRangeQuery = function(owner)
cmpRangeManager.DestroyActiveQuery(this.unitsQuery);
// Only allied units can make the gate open.
var players = QueryPlayerIDInterface(owner).GetAllies();
const players = QueryPlayerIDInterface(owner, IID_Diplomacy).GetAllies();
var range = this.GetPassRange();
if (range > 0)

View File

@ -3,6 +3,8 @@ function GuiInterface() {}
GuiInterface.prototype.Schema =
"<a:component type='system'/><empty/>";
GuiInterface.prototype.WHITE = { "r": 1, "g": 1, "b": 1 };
GuiInterface.prototype.Serialize = function()
{
// This component isn't network-synchronized for the biggest part,
@ -66,6 +68,7 @@ GuiInterface.prototype.GetSimulationState = function()
const cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
const cmpPlayerEntityLimits = Engine.QueryInterface(playerEnt, IID_EntityLimits);
const cmpIdentity = Engine.QueryInterface(playerEnt, IID_Identity);
const cmpDiplomacy = Engine.QueryInterface(playerEnt, IID_Diplomacy);
// Work out which phase we are in.
let phase = "";
@ -87,10 +90,10 @@ GuiInterface.prototype.GetSimulationState = function()
for (let j = 0; j < numPlayers; ++j)
{
allies[j] = cmpPlayer.IsAlly(j);
mutualAllies[j] = cmpPlayer.IsMutualAlly(j);
neutrals[j] = cmpPlayer.IsNeutral(j);
enemies[j] = cmpPlayer.IsEnemy(j);
allies[j] = cmpDiplomacy.IsAlly(j);
mutualAllies[j] = cmpDiplomacy.IsMutualAlly(j);
neutrals[j] = cmpDiplomacy.IsNeutral(j);
enemies[j] = cmpDiplomacy.IsEnemy(j);
}
ret.players.push({
@ -107,13 +110,13 @@ GuiInterface.prototype.GetSimulationState = function()
"resourceGatherers": cmpPlayer.GetResourceGatherers(),
"trainingBlocked": cmpPlayer.IsTrainingBlocked(),
"state": cmpPlayer.GetState(),
"team": cmpPlayer.GetTeam(),
"teamsLocked": cmpPlayer.GetLockTeams(),
"team": cmpDiplomacy.GetTeam(),
"teamLocked": cmpDiplomacy.IsTeamLocked(),
"cheatsEnabled": cmpPlayer.GetCheatsEnabled(),
"disabledTemplates": cmpPlayer.GetDisabledTemplates(),
"disabledTechnologies": cmpPlayer.GetDisabledTechnologies(),
"hasSharedDropsites": cmpPlayer.HasSharedDropsites(),
"hasSharedLos": cmpPlayer.HasSharedLos(),
"hasSharedDropsites": cmpDiplomacy.HasSharedDropsites(),
"hasSharedLos": cmpDiplomacy.HasSharedLos(),
"spyCostMultiplier": cmpPlayer.GetSpyCostMultiplier(),
"phase": phase,
"isAlly": allies,
@ -910,13 +913,13 @@ GuiInterface.prototype.UpdateDisplayedPlayerColors = function(player, data)
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i, IID_Player);
if (!cmpPlayer)
const cmpDiplomacy = QueryPlayerIDInterface(i, IID_Diplomacy);
if (!cmpDiplomacy)
continue;
cmpPlayer.SetDisplayDiplomacyColor(data.displayDiplomacyColors);
QueryPlayerIDInterface(i, IID_Player).SetDisplayDiplomacyColor(data.displayDiplomacyColors);
if (data.displayDiplomacyColors)
cmpPlayer.SetDiplomacyColor(data.displayedPlayerColors[i]);
cmpDiplomacy.SetDiplomacyColor(data.displayedPlayerColors[i]);
updateEntityColor(data.showAllStatusBars && (i == player || player == -1) ?
[IID_Minimap, IID_RangeOverlayRenderer, IID_RallyPointRenderer, IID_StatusBars] :
@ -947,10 +950,7 @@ GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
let color = playerColors[owner];
if (!color)
{
color = { "r": 1, "g": 1, "b": 1 };
let cmpPlayer = QueryPlayerIDInterface(owner);
if (cmpPlayer)
color = cmpPlayer.GetDisplayedColor();
color = QueryPlayerIDInterface(owner, IID_Player)?.GetDisplayedColor() || this.WHITE;
playerColors[owner] = color;
}

View File

@ -17,12 +17,6 @@ Player.prototype.Schema =
"</attribute>" +
"<text/>" +
"</element>" +
"<element name='SharedLosTech' a:help='Allies will share los when this technology is researched. Leave empty to never share LOS.'>" +
"<text/>" +
"</element>" +
"<element name='SharedDropsitesTech' a:help='Allies will share dropsites when this technology is researched. Leave empty to never share dropsites.'>" +
"<text/>" +
"</element>" +
"<element name='SpyCostMultiplier'>" +
"<ref name='nonNegativeDecimal'/>" +
"</element>";
@ -32,9 +26,6 @@ Player.prototype.STATE_ACTIVE = "active";
Player.prototype.STATE_DEFEATED = "defeated";
Player.prototype.STATE_WON = "won";
/**
* Don't serialize diplomacyColor or displayDiplomacyColor since they're modified by the GUI.
*/
Player.prototype.Serialize = function()
{
let state = {};
@ -42,8 +33,8 @@ Player.prototype.Serialize = function()
if (this.hasOwnProperty(key))
state[key] = this[key];
state.diplomacyColor = undefined;
state.displayDiplomacyColor = false;
// Modified by GUI, so don't serialise.
delete state.displayDiplomacyColor;
return state;
};
@ -62,8 +53,6 @@ Player.prototype.Init = function()
{
this.playerID = undefined;
this.color = undefined;
this.diplomacyColor = undefined;
this.displayDiplomacyColor = false;
this.popUsed = 0; // Population of units owned or trained by this player.
this.popBonuses = 0; // Sum of population bonuses of player's entities.
this.maxPop = 300; // Maximum population.
@ -71,11 +60,7 @@ Player.prototype.Init = function()
this.resourceCount = {};
this.resourceGatherers = {};
this.tradingGoods = []; // Goods for next trade-route and its probabilities * 100.
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.
this.teamsLocked = false;
this.state = this.STATE_ACTIVE;
this.diplomacy = []; // Array of diplomatic stances for this player with respect to other players (including gaia and self).
this.sharedDropsites = false;
this.formations = this.template.Formations._string.split(" ");
this.startCam = undefined;
this.controlAllUnits = false;
@ -134,11 +119,6 @@ Player.prototype.SetColor = function(r, g, b)
});
};
Player.prototype.SetDiplomacyColor = function(color)
{
this.diplomacyColor = { "r": color.r / 255, "g": color.g / 255, "b": color.b / 255, "a": 1 };
};
Player.prototype.SetDisplayDiplomacyColor = function(displayDiplomacyColor)
{
this.displayDiplomacyColor = displayDiplomacyColor;
@ -151,7 +131,7 @@ Player.prototype.GetColor = function()
Player.prototype.GetDisplayedColor = function()
{
return this.displayDiplomacyColor ? this.diplomacyColor : this.color;
return this.displayDiplomacyColor ? Engine.QueryInterface(this.entity, IID_Diplomacy).GetColor() : this.color;
};
// Try reserving num population slots. Returns 0 on success or number of missing slots otherwise.
@ -543,101 +523,6 @@ Player.prototype.SetState = function(newState, message)
Engine.PostMessage(this.entity, won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
};
Player.prototype.GetTeam = function()
{
return this.team;
};
Player.prototype.SetTeam = function(team)
{
if (this.teamsLocked)
return;
this.team = team;
// Set all team members as allies.
if (this.team != -1)
{
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 0; i < numPlayers; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i);
if (this.team != cmpPlayer.GetTeam())
continue;
this.SetAlly(i);
cmpPlayer.SetAlly(this.playerID);
}
}
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": this.playerID,
"otherPlayer": null
});
};
Player.prototype.SetLockTeams = function(value)
{
this.teamsLocked = value;
};
Player.prototype.GetLockTeams = function()
{
return this.teamsLocked;
};
Player.prototype.GetDiplomacy = function()
{
return this.diplomacy.slice();
};
Player.prototype.SetDiplomacy = function(dipl)
{
this.diplomacy = dipl.slice();
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": this.playerID,
"otherPlayer": null
});
};
Player.prototype.SetDiplomacyIndex = function(idx, value)
{
let cmpPlayer = QueryPlayerIDInterface(idx);
if (!cmpPlayer)
return;
if (!this.IsActive() || !cmpPlayer.IsActive())
return;
this.diplomacy[idx] = value;
Engine.BroadcastMessage(MT_DiplomacyChanged, {
"player": this.playerID,
"otherPlayer": cmpPlayer.GetPlayerID()
});
// Mutual worsening of relations.
if (cmpPlayer.diplomacy[this.playerID] > value)
cmpPlayer.SetDiplomacyIndex(this.playerID, value);
};
Player.prototype.UpdateSharedLos = function()
{
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
let cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
if (!cmpRangeManager || !cmpTechnologyManager)
return;
if (!cmpTechnologyManager.IsTechnologyResearched(this.template.SharedLosTech))
{
cmpRangeManager.SetSharedLos(this.playerID, [this.playerID]);
return;
}
cmpRangeManager.SetSharedLos(this.playerID, this.GetMutualAllies());
};
Player.prototype.GetFormations = function()
{
return this.formations;
@ -668,16 +553,6 @@ Player.prototype.HasStartingCamera = function()
return this.startCam !== undefined;
};
Player.prototype.HasSharedLos = function()
{
let cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
return cmpTechnologyManager && cmpTechnologyManager.IsTechnologyResearched(this.template.SharedLosTech);
};
Player.prototype.HasSharedDropsites = function()
{
return this.sharedDropsites;
};
Player.prototype.SetControlAllUnits = function(c)
{
this.controlAllUnits = c;
@ -698,94 +573,6 @@ Player.prototype.IsAI = function()
return this.isAI;
};
Player.prototype.GetPlayersByDiplomacy = function(func)
{
let players = [];
for (let i = 0; i < this.diplomacy.length; ++i)
if (this[func](i))
players.push(i);
return players;
};
Player.prototype.SetAlly = function(id)
{
this.SetDiplomacyIndex(id, 1);
};
/**
* Check if given player is our ally.
*/
Player.prototype.IsAlly = function(id)
{
return this.diplomacy[id] > 0;
};
Player.prototype.GetAllies = function()
{
return this.GetPlayersByDiplomacy("IsAlly");
};
/**
* Check if given player is our ally excluding ourself
*/
Player.prototype.IsExclusiveAlly = function(id)
{
return this.playerID != id && this.IsAlly(id);
};
/**
* Check if given player is our ally, and we are its ally
*/
Player.prototype.IsMutualAlly = function(id)
{
let cmpPlayer = QueryPlayerIDInterface(id);
return this.IsAlly(id) && cmpPlayer && cmpPlayer.IsAlly(this.playerID);
};
Player.prototype.GetMutualAllies = function()
{
return this.GetPlayersByDiplomacy("IsMutualAlly");
};
/**
* Check if given player is our ally, and we are its ally, excluding ourself
*/
Player.prototype.IsExclusiveMutualAlly = function(id)
{
return this.playerID != id && this.IsMutualAlly(id);
};
Player.prototype.SetEnemy = function(id)
{
this.SetDiplomacyIndex(id, -1);
};
/**
* Check if given player is our enemy
*/
Player.prototype.IsEnemy = function(id)
{
return this.diplomacy[id] < 0;
};
Player.prototype.GetEnemies = function()
{
return this.GetPlayersByDiplomacy("IsEnemy");
};
Player.prototype.SetNeutral = function(id)
{
this.SetDiplomacyIndex(id, 0);
};
/**
* Check if given player is neutral
*/
Player.prototype.IsNeutral = function(id)
{
return this.diplomacy[id] == 0;
};
/**
* Do some map dependant initializations
*/
@ -841,19 +628,6 @@ Player.prototype.OnGlobalOwnershipChanged = function(msg)
}
};
Player.prototype.OnResearchFinished = function(msg)
{
if (msg.tech == this.template.SharedLosTech)
this.UpdateSharedLos();
else if (msg.tech == this.template.SharedDropsitesTech)
this.sharedDropsites = true;
};
Player.prototype.OnDiplomacyChanged = function()
{
this.UpdateSharedLos();
};
Player.prototype.OnValueModification = function(msg)
{
if (msg.component != "Player")
@ -983,7 +757,7 @@ Player.prototype.OnGlobalPlayerDefeated = function(msg)
if (!cmpSound)
return;
const soundGroup = cmpSound.GetSoundGroup(this.playerID === msg.playerId ? "defeated" : this.IsAlly(msg.playerId) ? "defeated_ally" : this.HasWon() ? "won" : "defeated_enemy");
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");
if (soundGroup)
Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager).PlaySoundGroupForPlayer(soundGroup, this.playerID);
};

View File

@ -17,18 +17,17 @@ PlayerManager.prototype.AddPlayer = function(templateName)
{
const ent = Engine.AddEntity(templateName);
const id = this.playerEntities.length;
const cmpPlayer = Engine.QueryInterface(ent, IID_Player);
cmpPlayer.SetPlayerID(id);
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_Player).diplomacy[id] = -1;
Engine.QueryInterface(this.GetPlayerByID(i), IID_Diplomacy).diplomacy[id] = -1;
newDiplo[i] = -1;
}
newDiplo[id] = 1;
cmpPlayer.SetDiplomacy(newDiplo);
Engine.QueryInterface(ent, IID_Diplomacy).SetDiplomacy(newDiplo);
Engine.BroadcastMessage(MT_PlayerEntityChanged, {
"player": id,
@ -61,7 +60,7 @@ PlayerManager.prototype.ReplacePlayerTemplate = function(id, newTemplateName)
this.playerEntities[id] = ent;
newCmpPlayer.SetColor(oldCmpPlayer.GetColor());
newCmpPlayer.SetDiplomacy(oldCmpPlayer.GetDiplomacy());
Engine.QueryInterface(ent, IID_Diplomacy).SetDiplomacy(Engine.QueryInterface(oldent, IID_Diplomacy).GetDiplomacy());
Engine.BroadcastMessage(MT_PlayerEntityChanged, {
"player": id,

View File

@ -373,12 +373,17 @@ ResourceGatherer.prototype.CanReturnResource = function(target, checkCarriedReso
return false;
}
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (cmpOwnership && IsOwnedByPlayer(cmpOwnership.GetOwner(), target))
const cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership)
return false;
const playerID = cmpOwnership.GetOwner();
if (IsOwnedByPlayer(playerID, target))
return true;
let cmpPlayer = QueryOwnerInterface(this.entity);
return cmpPlayer && cmpPlayer.HasSharedDropsites() && cmpResourceDropsite.IsShared() &&
cmpOwnership && IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target);
return QueryPlayerIDInterface(playerID, IID_Diplomacy)?.HasSharedDropsites() &&
cmpResourceDropsite.IsShared() &&
IsOwnedByMutualAllyOfPlayer(playerID, target);
};
/**

View File

@ -416,25 +416,27 @@ StatisticsTracker.prototype.GetPercentMapExplored = function()
*/
StatisticsTracker.prototype.GetTeamPercentMapExplored = function()
{
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer)
const cmpDiplomacy = Engine.QueryInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return 0;
let team = cmpPlayer.GetTeam();
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
const team = cmpDiplomacy.GetTeam();
const cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
// If teams are not locked, this statistic won't be displayed, so don't bother computing
if (team == -1 || !cmpPlayer.GetLockTeams())
return cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID());
let teamPlayers = [];
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
if (team == -1 || !cmpDiplomacy.IsTeamLocked())
{
let cmpOtherPlayer = QueryPlayerIDInterface(i);
if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team)
teamPlayers.push(i);
const cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer)
return 0;
return cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID());
}
const teamPlayers = [];
const numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
if (QueryPlayerIDInterface(i, IID_Diplomacy)?.GetTeam() === team)
teamPlayers.push(i);
return cmpRangeManager.GetUnionPercentMapExplored(teamPlayers);
};
@ -449,23 +451,25 @@ StatisticsTracker.prototype.GetPercentMapControlled = function()
StatisticsTracker.prototype.GetTeamPercentMapControlled = function()
{
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer)
const cmpDiplomacy = Engine.QueryInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return 0;
let team = cmpPlayer.GetTeam();
let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
if (team == -1 || !cmpPlayer.GetLockTeams())
const team = cmpDiplomacy.GetTeam();
const cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
if (team === -1 || !cmpDiplomacy.IsTeamLocked())
{
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
if (!cmpPlayer)
return 0;
return cmpTerritoryManager.GetTerritoryPercentage(cmpPlayer.GetPlayerID());
}
let teamPercent = 0;
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
const numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i)
{
let cmpOtherPlayer = QueryPlayerIDInterface(i);
if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team)
if (QueryPlayerIDInterface(i, IID_Diplomacy)?.GetTeam() === team)
teamPercent += cmpTerritoryManager.GetTerritoryPercentage(i);
}
return teamPercent;
};

View File

@ -35,6 +35,11 @@ TerritoryDecay.prototype.IsConnected = function()
if (!cmpPlayer)
return true;// something without ownership can't decay
const playerID = cmpPlayer.GetPlayerID();
const cmpDiplomacy = QueryPlayerIDInterface(playerID, IID_Diplomacy);
if (!cmpDiplomacy)
return true;
const decayTerritory = ApplyValueModificationsToEntity("TerritoryDecay/Territory", this.template.Territory, this.entity);
var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
@ -43,11 +48,11 @@ TerritoryDecay.prototype.IsConnected = function()
if (tileOwner == 0)
{
this.connectedNeighbours[0] = 1;
return cmpPlayer.GetPlayerID() == 0 || decayTerritory.indexOf("neutral") === -1;
return playerID == 0 || decayTerritory.indexOf("neutral") === -1;
}
var tileConnected = cmpTerritoryManager.IsConnected(pos.x, pos.y);
if (tileConnected && !cmpPlayer.IsMutualAlly(tileOwner))
if (tileConnected && !cmpDiplomacy.IsMutualAlly(tileOwner))
{
this.connectedNeighbours[tileOwner] = 1;
return decayTerritory.indexOf("enemy") === -1;
@ -58,7 +63,7 @@ TerritoryDecay.prototype.IsConnected = function()
// Special-case: if the tile is unconnected, non-own territory, decay towards gaia.
// TODO: this is not great, see #4749
if (cmpPlayer.GetPlayerID() != tileOwner)
if (playerID != tileOwner)
{
this.connectedNeighbours[0] = 1;
return false;
@ -68,7 +73,7 @@ TerritoryDecay.prototype.IsConnected = function()
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (var i = 1; i < numPlayers; ++i)
if (this.connectedNeighbours[i] > 0 && cmpPlayer.IsMutualAlly(i))
if (this.connectedNeighbours[i] > 0 && cmpDiplomacy.IsMutualAlly(i))
{
// don't decay if connected to a connected ally; disable blinking
cmpTerritoryManager.SetTerritoryBlinking(pos.x, pos.y, false);

View File

@ -181,10 +181,10 @@ Trader.prototype.CanTrade = function(target)
!(cmpTraderIdentity.HasClass("Ship") && cmpTargetMarket.HasType("naval")))
return false;
let cmpTraderPlayer = QueryOwnerInterface(this.entity, IID_Player);
let cmpTraderDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
let cmpTargetPlayer = QueryOwnerInterface(target, IID_Player);
return cmpTraderPlayer && cmpTargetPlayer && !cmpTraderPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID());
return cmpTraderPlayer && cmpTargetPlayer && !cmpTraderDiplomacy.IsEnemy(cmpTargetPlayer.GetPlayerID());
};
Trader.prototype.AddResources = function(ent, gain)

View File

@ -3764,12 +3764,11 @@ UnitAI.prototype.SetupLOSRangeQuery = function(enable = true)
this.losRangeQuery = undefined;
}
let cmpPlayer = QueryOwnerInterface(this.entity);
// If we are being destructed (owner == -1), creating a range query is pointless.
if (!cmpPlayer)
const cmpDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return;
let players = cmpPlayer.GetEnemies();
const players = cmpDiplomacy.GetEnemies();
if (!players.length)
return;
@ -3798,13 +3797,12 @@ UnitAI.prototype.SetupHealRangeQuery = function(enable = true)
this.losHealRangeQuery = undefined;
}
let cmpPlayer = QueryOwnerInterface(this.entity);
// If we are being destructed (owner == -1), creating a range query is pointless.
if (!cmpPlayer)
const cmpDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return;
let players = cmpPlayer.GetAllies();
let range = this.GetQueryRange(IID_Heal);
const players = cmpDiplomacy.GetAllies();
const range = this.GetQueryRange(IID_Heal);
// Do not compensate for entity sizes: LOS doesn't, and UnitAI relies on that.
this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity,
@ -3830,13 +3828,12 @@ UnitAI.prototype.SetupAttackRangeQuery = function(enable = true)
this.losAttackRangeQuery = undefined;
}
let cmpPlayer = QueryOwnerInterface(this.entity);
// If we are being destructed (owner == -1), creating a range query is pointless.
if (!cmpPlayer)
const cmpDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
if (!cmpDiplomacy)
return;
// TODO: How to handle neutral players - Special query to attack military only?
let players = cmpPlayer.GetEnemies();
const players = cmpDiplomacy.GetEnemies();
if (!players.length)
return;
@ -4470,8 +4467,8 @@ UnitAI.prototype.FindNearestDropsite = function(genericType)
let maxDifference = 40;
let owner = cmpOwnership.GetOwner();
let cmpPlayer = QueryOwnerInterface(this.entity);
let players = cmpPlayer && cmpPlayer.HasSharedDropsites() ? cmpPlayer.GetMutualAllies() : [owner];
let cmpDiplomacy = QueryOwnerInterface(this.entity, IID_Diplomacy);
let players = cmpDiplomacy && cmpDiplomacy.HasSharedDropsites() ? cmpDiplomacy.GetMutualAllies() : [owner];
let nearestDropsites = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).ExecuteQuery(this.entity, 0, -1, players, IID_ResourceDropsite, false);
let isShip = Engine.QueryInterface(this.entity, IID_Identity).HasClass("Ship");

View File

@ -0,0 +1,7 @@
Engine.RegisterInterface("Diplomacy");
/**
* Message of the form { "player": number, "otherPlayer": number }
* sent from Diplomacy component when diplomacy changed for one player or between two players.
*/
Engine.RegisterMessageType("DiplomacyChanged");

View File

@ -1,9 +1,3 @@
/**
* Message of the form { "player": number, "otherPlayer": number }
* sent from Player component when diplomacy changed for one player or between two players.
*/
Engine.RegisterMessageType("DiplomacyChanged");
/**
* Message of the form {}
* sent from Player component.

View File

@ -26,6 +26,7 @@ Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Auras.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("interfaces/Formation.js");
Engine.LoadComponentScript("interfaces/Health.js");
@ -45,6 +46,9 @@ function attackComponentTest(defenderClass, isEnemy, test_function)
AddMock(playerEnt1, IID_Player, {
"GetPlayerID": () => 1,
});
AddMock(playerEnt1, IID_Diplomacy, {
"IsEnemy": () => isEnemy
});

View File

@ -1,6 +1,7 @@
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Auras.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/RangeOverlayManager.js");
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
@ -52,19 +53,25 @@ function testAuras(name, test_function)
});
AddMock(playerEnt[1], IID_Player, {
"IsAlly": id => id == playerID[1] || id == playerID[2],
"IsEnemy": id => id != playerID[1] || id != playerID[2],
"GetPlayerID": () => playerID[1],
"IsDefeated": () => playerDefeated[1]
});
AddMock(playerEnt[2], IID_Player, {
AddMock(playerEnt[1], IID_Diplomacy, {
"IsAlly": id => id == playerID[1] || id == playerID[2],
"IsEnemy": id => id != playerID[1] || id != playerID[2],
});
AddMock(playerEnt[2], IID_Player, {
"GetPlayerID": () => playerID[2],
"IsDefeated": () => playerDefeated[2]
});
AddMock(playerEnt[2], IID_Diplomacy, {
"IsAlly": id => id == playerID[1] || id == playerID[2],
"IsEnemy": id => id != playerID[1] || id != playerID[2],
});
AddMock(targetEnt, IID_Identity, {
"GetClassesList": () => ["CorrectClass", "OtherClass"]
});

View File

@ -1,6 +1,7 @@
Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Builder.js");
Engine.LoadComponentScript("interfaces/Cost.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Foundation.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Repairable.js");
@ -110,7 +111,7 @@ function testBuildingFoundation()
"Entities": { "_string": "" }
});
AddMock(playerEntityID, IID_Player, {
AddMock(playerEntityID, IID_Diplomacy, {
"IsAlly": (p) => p == playerId
});
@ -140,7 +141,7 @@ testBuildingFoundation();
function testRepairing()
{
AddMock(playerEntityID, IID_Player, {
AddMock(playerEntityID, IID_Diplomacy, {
"IsAlly": (p) => p == playerId
});

View File

@ -2,6 +2,7 @@ Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Auras.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
@ -48,19 +49,19 @@ function testCapturable(testData, test_function)
"Activate": () => {}
});
AddMock(10, IID_Player, {
AddMock(10, IID_Diplomacy, {
"IsEnemy": id => id != 0
});
AddMock(11, IID_Player, {
AddMock(11, IID_Diplomacy, {
"IsEnemy": id => id != 1 && id != 2
});
AddMock(12, IID_Player, {
AddMock(12, IID_Diplomacy, {
"IsEnemy": id => id != 1 && id != 2
});
AddMock(13, IID_Player, {
AddMock(13, IID_Diplomacy, {
"IsEnemy": id => id != 3
});

View File

@ -16,6 +16,7 @@ Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("Position.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/DelayedDamage.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Loot.js");
Engine.LoadComponentScript("interfaces/Promotion.js");
@ -77,7 +78,7 @@ function Test_Generic()
"direction": new Vector3D(1, 0, 0)
};
AddMock(atkPlayerEntity, IID_Player, {
AddMock(atkPlayerEntity, IID_Diplomacy, {
"GetEnemies": () => [targetOwner]
});
@ -161,7 +162,7 @@ function Test_Generic()
TestDamage();
atkPlayerEntity = 1;
AddMock(atkPlayerEntity, IID_Player, {
AddMock(atkPlayerEntity, IID_Diplomacy, {
"GetEnemies": () => [2, 3]
});
TS_ASSERT_UNEVAL_EQUALS(AttackHelper.GetPlayersToDamage(atkPlayerEntity, true), [0, 1, 2, 3, 4]);
@ -199,7 +200,7 @@ function TestLinearSplashDamage()
let hitEnts = new Set();
AddMock(attackerOwner, IID_Player, {
AddMock(attackerOwner, IID_Diplomacy, {
"GetEnemies": () => [2]
});
@ -295,7 +296,7 @@ function TestCircularSplashDamage()
return 1 - r * r / (radius * radius);
};
AddMock(attackerOwner, IID_Player, {
AddMock(attackerOwner, IID_Diplomacy, {
"GetEnemies": () => [2]
});
@ -474,7 +475,7 @@ function Test_MissileHit()
"IsInWorld": () => true,
});
AddMock(10, IID_Player, {
AddMock(10, IID_Diplomacy, {
"GetEnemies": () => [2]
});

View File

@ -0,0 +1,79 @@
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/PlayerManager.js");
Engine.LoadComponentScript("Diplomacy.js");
Engine.LoadHelperScript("Player.js");
const players = [10, 11];
ConstructComponent(players[0], "Diplomacy", null)
const cmpDiplomacy = ConstructComponent(players[1], "Diplomacy", {})
TS_ASSERT_EQUALS(cmpDiplomacy.GetTeam(), -1);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetAllies(), []);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetEnemies(), []);
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetNumPlayers": () => players.length,
"GetPlayerByID": (i) => players[i]
});
for (const player in players)
AddMock(players[player], IID_Player, {
"GetPlayerID": () => player,
"IsActive": () => true,
});
cmpDiplomacy.ChangeTeam(1);
TS_ASSERT_EQUALS(cmpDiplomacy.GetTeam(), 1);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetAllies(), [1]);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetEnemies(), []);
cmpDiplomacy.LockTeam();
cmpDiplomacy.ChangeTeam(2);
TS_ASSERT_EQUALS(cmpDiplomacy.GetTeam(), 1);
cmpDiplomacy.UnLockTeam();
cmpDiplomacy.SetEnemy(0);
TS_ASSERT(!cmpDiplomacy.IsAlly(0));
TS_ASSERT(!cmpDiplomacy.IsNeutral(0));
TS_ASSERT(cmpDiplomacy.IsEnemy(0));
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetAllies(), [1]);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetEnemies(), [0]);
cmpDiplomacy.Ally(0);
TS_ASSERT(cmpDiplomacy.IsAlly(0));
TS_ASSERT(!cmpDiplomacy.IsNeutral(0));
TS_ASSERT(!cmpDiplomacy.IsEnemy(0));
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetAllies(), [0, 1]);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetEnemies(), []);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetMutualAllies(), [1]);
cmpDiplomacy.SetNeutral(0);
TS_ASSERT(!cmpDiplomacy.IsAlly(0));
TS_ASSERT(cmpDiplomacy.IsNeutral(0));
TS_ASSERT(!cmpDiplomacy.IsEnemy(0));
// Mutual worsening of relations.
cmpDiplomacy.OnDiplomacyChanged({
"player": 0,
"otherPlayer": 1,
"value": -1
});
TS_ASSERT(cmpDiplomacy.IsEnemy(0));
cmpDiplomacy.SetDiplomacy([-1, 1, 0, 1, -1]);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetAllies(), [1, 3]);
TS_ASSERT_UNEVAL_EQUALS(cmpDiplomacy.GetEnemies(), [0, 4]);
// Check diplomacy is not editable outside of the component.
var diplo = cmpDiplomacy.GetDiplomacy();
diplo[0] = 1;
TS_ASSERT(cmpDiplomacy.IsEnemy(0));
diplo = [1, 1, 0];
cmpDiplomacy.SetDiplomacy(diplo);
diplo[1] = -1;
TS_ASSERT(cmpDiplomacy.IsAlly(1));

View File

@ -1,5 +1,6 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/Health.js");
@ -31,16 +32,14 @@ AddMock(garrisonHolderId, IID_Ownership, {
"GetOwner": () => player
});
AddMock(player, IID_Player, {
AddMock(player, IID_Diplomacy, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
"GetPlayerID": () => player
});
AddMock(friendlyPlayer, IID_Player, {
AddMock(friendlyPlayer, IID_Diplomacy, {
"IsAlly": id => true,
"IsMutualAlly": id => true,
"GetPlayerID": () => friendlyPlayer
});
AddMock(SYSTEM_ENTITY, IID_Timer, {

View File

@ -1,6 +1,7 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("Position.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/Health.js");
@ -50,16 +51,14 @@ AddMock(holder, IID_Ownership, {
"GetOwner": () => player
});
AddMock(player, IID_Player, {
AddMock(player, IID_Diplomacy, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
"GetPlayerID": () => player
});
AddMock(friendlyPlayer, IID_Player, {
AddMock(friendlyPlayer, IID_Diplomacy, {
"IsAlly": id => true,
"IsMutualAlly": id => true,
"GetPlayerID": () => friendlyPlayer
});
AddMock(SYSTEM_ENTITY, IID_Timer, {

View File

@ -1,3 +1,4 @@
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Gate.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Gate.js");

View File

@ -7,6 +7,7 @@ Engine.LoadComponentScript("interfaces/Builder.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/CeasefireManager.js");
Engine.LoadComponentScript("interfaces/DeathDamage.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/EndGameManager.js");
Engine.LoadComponentScript("interfaces/EntityLimits.js");
Engine.LoadComponentScript("interfaces/Formation.js");
@ -111,20 +112,23 @@ AddMock(100, IID_Player, {
"GetPanelEntities": function() { return []; },
"IsTrainingBlocked": function() { return false; },
"GetState": function() { return "active"; },
"GetTeam": function() { return -1; },
"GetLockTeams": function() { return false; },
"GetCheatsEnabled": function() { return false; },
"GetDiplomacy": function() { return [-1, 1]; },
"IsAlly": function() { return false; },
"IsMutualAlly": function() { return false; },
"IsNeutral": function() { return false; },
"IsEnemy": function() { return true; },
"GetDisabledTemplates": function() { return {}; },
"GetDisabledTechnologies": function() { return {}; },
"CanBarter": function() { return false; },
"GetSpyCostMultiplier": function() { return 1; },
"HasSharedDropsites": function() { return false; },
"HasSharedLos": function() { return false; }
"GetSpyCostMultiplier": function() { return 1; }
});
AddMock(100, IID_Diplomacy, {
"GetTeam": () => -1,
"IsTeamLocked": () => false,
"GetDiplomacy": () => [-1, 1],
"IsAlly": () => false,
"IsMutualAlly": () => false,
"IsNeutral": () => false,
"IsEnemy": () => true,
"HasSharedDropsites": () => false,
"HasSharedLos": () => false,
});
AddMock(100, IID_Identity, {
@ -204,20 +208,23 @@ AddMock(101, IID_Player, {
"GetPanelEntities": function() { return []; },
"IsTrainingBlocked": function() { return false; },
"GetState": function() { return "active"; },
"GetTeam": function() { return -1; },
"GetLockTeams": function() {return false; },
"GetCheatsEnabled": function() { return false; },
"GetDiplomacy": function() { return [-1, 1]; },
"IsAlly": function() { return true; },
"IsMutualAlly": function() {return false; },
"IsNeutral": function() { return false; },
"IsEnemy": function() { return false; },
"GetDisabledTemplates": function() { return {}; },
"GetDisabledTechnologies": function() { return {}; },
"CanBarter": function() { return false; },
"GetSpyCostMultiplier": function() { return 1; },
"HasSharedDropsites": function() { return false; },
"HasSharedLos": function() { return false; }
});
AddMock(101, IID_Diplomacy, {
"GetTeam": () => -1,
"IsTeamLocked": () => false,
"GetDiplomacy": () => [-1, 1],
"IsAlly": () => true,
"IsMutualAlly": () => false,
"IsNeutral": () => false,
"IsEnemy": () => false,
"HasSharedDropsites": () => false,
"HasSharedLos": () => false,
});
AddMock(101, IID_Identity, {
@ -305,7 +312,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
"trainingBlocked": false,
"state": "active",
"team": -1,
"teamsLocked": false,
"teamLocked": false,
"cheatsEnabled": false,
"disabledTemplates": {},
"disabledTechnologies": {},
@ -356,7 +363,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
"trainingBlocked": false,
"state": "active",
"team": -1,
"teamsLocked": false,
"teamLocked": false,
"cheatsEnabled": false,
"disabledTemplates": {},
"disabledTechnologies": {},
@ -417,7 +424,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
"trainingBlocked": false,
"state": "active",
"team": -1,
"teamsLocked": false,
"teamLocked": false,
"cheatsEnabled": false,
"disabledTemplates": {},
"disabledTechnologies": {},
@ -491,7 +498,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
"trainingBlocked": false,
"state": "active",
"team": -1,
"teamsLocked": false,
"teamLocked": false,
"cheatsEnabled": false,
"disabledTemplates": {},
"disabledTechnologies": {},

View File

@ -1,5 +1,6 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Formation.js");
Engine.LoadComponentScript("interfaces/Heal.js");
Engine.LoadComponentScript("interfaces/Health.js");
@ -39,11 +40,11 @@ AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": () => player
});
AddMock(player, IID_Player, {
AddMock(player, IID_Diplomacy, {
"IsAlly": (p) => p == player
});
AddMock(otherPlayer, IID_Player, {
AddMock(otherPlayer, IID_Diplomacy, {
"IsAlly": (p) => p == player
});

View File

@ -47,19 +47,6 @@ TS_ASSERT_EQUALS(cmpPlayer.GetPlayerID(), playerID);
TS_ASSERT_EQUALS(cmpPlayer.GetPopulationCount(), 0);
TS_ASSERT_EQUALS(cmpPlayer.GetPopulationLimit(), 0);
cmpPlayer.SetDiplomacy([-1, 1, 0, 1, -1]);
TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetAllies(), [1, 3]);
TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetEnemies(), [0, 4]);
var diplo = cmpPlayer.GetDiplomacy();
diplo[0] = 1;
TS_ASSERT(cmpPlayer.IsEnemy(0));
diplo = [1, 1, 0];
cmpPlayer.SetDiplomacy(diplo);
diplo[1] = -1;
TS_ASSERT(cmpPlayer.IsAlly(1));
TS_ASSERT_EQUALS(cmpPlayer.GetSpyCostMultiplier(), 1);
TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetBarterMultiplier(), {
"buy": {

View File

@ -1,4 +1,5 @@
Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/TurretHolder.js");
Engine.LoadComponentScript("interfaces/Turretable.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
@ -38,16 +39,14 @@ for (let entity of entitiesToTest)
});
}
AddMock(player, IID_Player, {
AddMock(player, IID_Diplomacy, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
"GetPlayerID": () => player
});
AddMock(alliedPlayer, IID_Player, {
AddMock(alliedPlayer, IID_Diplomacy, {
"IsAlly": id => true,
"IsMutualAlly": id => true,
"GetPlayerID": () => alliedPlayer
});
let cmpTurretHolder = ConstructComponent(turretHolderID, "TurretHolder", {

View File

@ -1,6 +1,7 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("Position.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Turretable.js");
Engine.LoadComponentScript("interfaces/TurretHolder.js");
@ -47,16 +48,14 @@ AddMock(holder, IID_Ownership, {
"GetOwner": () => player
});
AddMock(player, IID_Player, {
AddMock(player, IID_Diplomacy, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
"GetPlayerID": () => player
});
AddMock(friendlyPlayer, IID_Player, {
AddMock(friendlyPlayer, IID_Diplomacy, {
"IsAlly": id => true,
"IsMutualAlly": id => true,
"GetPlayerID": () => friendlyPlayer
});
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {

View File

@ -5,6 +5,7 @@ Engine.LoadComponentScript("interfaces/Auras.js");
Engine.LoadComponentScript("interfaces/Builder.js");
Engine.LoadComponentScript("interfaces/BuildingAI.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/Resistance.js");
Engine.LoadComponentScript("interfaces/Formation.js");
@ -144,7 +145,7 @@ function TestFormationExiting(mode)
"GetNumPlayers": function() { return 2; },
});
AddMock(playerEntity, IID_Player, {
AddMock(playerEntity, IID_Diplomacy, {
"IsAlly": function() { return false; },
"IsEnemy": function() { return true; },
"GetEnemies": function() { return [2]; },
@ -327,7 +328,7 @@ function TestMoveIntoFormationWhileAttacking()
"IsInTargetRange": (ent, target, min, max) => true
});
AddMock(playerEntity, IID_Player, {
AddMock(playerEntity, IID_Diplomacy, {
"IsAlly": function() { return false; },
"IsEnemy": function() { return true; },
"GetEnemies": function() { return [2]; },

View File

@ -218,7 +218,7 @@ AttackHelper.prototype.GetTotalAttackEffects = function(target, effectData, effe
AttackHelper.prototype.GetPlayersToDamage = function(attackerOwner, friendlyFire)
{
if (!friendlyFire)
return QueryPlayerIDInterface(attackerOwner).GetEnemies();
return QueryPlayerIDInterface(attackerOwner, IID_Diplomacy).GetEnemies();
return Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers();
};

View File

@ -88,28 +88,29 @@ var g_Commands = {
"diplomacy": function(player, cmd, data)
{
let cmpCeasefireManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager);
if (data.cmpPlayer.GetLockTeams() ||
cmpCeasefireManager && cmpCeasefireManager.IsCeasefireActive())
if (Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager).IsCeasefireActive())
return;
const cmpDiplomacy = QueryPlayerIDInterface(player, IID_Diplomacy);
if (!cmpDiplomacy || cmpDiplomacy.IsTeamLocked())
return;
switch(cmd.to)
{
case "ally":
data.cmpPlayer.SetAlly(cmd.player);
cmpDiplomacy.Ally(cmd.player);
break;
case "neutral":
data.cmpPlayer.SetNeutral(cmd.player);
cmpDiplomacy.SetNeutral(cmd.player);
break;
case "enemy":
data.cmpPlayer.SetEnemy(cmd.player);
cmpDiplomacy.SetEnemy(cmd.player);
break;
default:
warn("Invalid command: Could not set "+player+" diplomacy status of player "+cmd.player+" to "+cmd.to);
}
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGuiInterface.PushNotification({
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({
"type": "diplomacy",
"players": [player],
"targetPlayer": cmd.player,

View File

@ -107,9 +107,9 @@ function LoadPlayerSettings(settings, newPlayers)
// If diplomacy explicitly defined, use that; otherwise use teams.
const diplomacy = getPlayerSetting(i, "Diplomacy");
if (diplomacy !== undefined)
cmpPlayer.SetDiplomacy(diplomacy);
QueryPlayerIDInterface(i, IID_Diplomacy).SetDiplomacy(diplomacy);
else
cmpPlayer.SetTeam(getPlayerSetting(i, "Team") ?? -1);
QueryPlayerIDInterface(i, IID_Diplomacy).ChangeTeam(getPlayerSetting(i, "Team") ?? -1);
const formations = getPlayerSetting(i, "Formations");
if (formations)
@ -120,11 +120,11 @@ function LoadPlayerSettings(settings, newPlayers)
cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation);
}
// NOTE: We need to do the team locking here, as otherwise
// SetTeam can't ally the players.
// NOTE: We need to do the team locking here, as
// otherwise we can't ally the players above.
if (settings.LockTeams)
for (let i = 0; i < numPlayers; ++i)
QueryPlayerIDInterface(i).SetLockTeams(true);
QueryPlayerIDInterface(i, IID_Diplomacy).LockTeam();
}
function GetPlayerTemplateName(civ)
@ -232,21 +232,8 @@ function IsOwnedByMutualAllyOfEntity(entity, target)
function IsOwnedByEntityHelper(entity, target, check)
{
// Figure out which player controls us
let owner = 0;
let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership)
owner = cmpOwnership.GetOwner();
// Figure out which player controls the target entity
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(owner);
return cmpPlayer && cmpPlayer[check](targetOwner);
const owner = Engine.QueryInterface(entity, IID_Ownership)?.GetOwner() || 0;
return IsOwnedByHelper(owner, target, check);
}
/**
@ -288,14 +275,9 @@ function IsOwnedByEnemyOfPlayer(player, target)
function IsOwnedByHelper(player, target, check)
{
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(player);
return cmpPlayer && cmpPlayer[check](targetOwner);
const targetOwner = Engine.QueryInterface(target, IID_Ownership)?.GetOwner() || 0;
const cmpDiplomacy = QueryPlayerIDInterface(player, IID_Diplomacy);
return cmpDiplomacy && cmpDiplomacy[check](targetOwner);
}
Engine.RegisterGlobal("LoadPlayerSettings", LoadPlayerSettings);

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Entity>
<Diplomacy>
<SharedLosTech/>
<SharedDropsitesTech/>
</Diplomacy>
<Identity>
<Civ>gaia</Civ>
<GenericName>Gaia</GenericName>
@ -7,8 +11,6 @@
<Undeletable>true</Undeletable>
</Identity>
<Player>
<SharedLosTech/>
<SharedDropsitesTech/>
<SpyCostMultiplier>1.0</SpyCostMultiplier>
<BarterMultiplier>
<Buy>

View File

@ -13,6 +13,10 @@
<AlertnessPeaceThreshold>0</AlertnessPeaceThreshold>
<AlertnessMax>8</AlertnessMax>
</BattleDetection>
<Diplomacy>
<SharedLosTech>unlock_shared_los</SharedLosTech>
<SharedDropsitesTech>unlock_shared_dropsites</SharedDropsitesTech>
</Diplomacy>
<EntityLimits>
<Limits>
<Animal>50</Animal>
@ -70,8 +74,6 @@
<metal>1.0</metal>
</Sell>
</BarterMultiplier>
<SharedLosTech>unlock_shared_los</SharedLosTech>
<SharedDropsitesTech>unlock_shared_dropsites</SharedDropsitesTech>
<SpyCostMultiplier>1.0</SpyCostMultiplier>
<Formations datatype="tokens">
special/formations/null