Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg
+++ binaries/data/config/default.cfg
@@ -453,6 +453,7 @@
pingduration = 50.0 ; The duration for which an entity will be pinged after an attack notification
[gui.session.notifications]
+discovered = true ; Show a chat notification if you discovered a player (structure) for the first time
attack = true ; Show a chat notification if you are attacked by another player
tribute = true ; Show a chat notification if an ally tributes resources to another team member if teams are locked, and all tributes in observer mode
barter = true ; Show a chat notification to observers when a player bartered resources
Index: binaries/data/mods/public/audio/interface/alarm/alarm_discovered_player.xml
===================================================================
--- binaries/data/mods/public/audio/interface/alarm/alarm_discovered_player.xml
+++ binaries/data/mods/public/audio/interface/alarm/alarm_discovered_player.xml
@@ -0,0 +1,17 @@
+
+
+ 1
+ owner
+ 0.35
+ 100
+ 1
+ 0
+ 1
+ 1
+ 0.35
+ 0.30
+ 1
+ 1
+ audio/interface/alarm/
+ alarm_discovered_player.ogg
+
Index: binaries/data/mods/public/gui/options/options.json
===================================================================
--- binaries/data/mods/public/gui/options/options.json
+++ binaries/data/mods/public/gui/options/options.json
@@ -630,6 +630,12 @@
},
{
"type": "boolean",
+ "label": "Chat notification discovered",
+ "tooltip": "Show a chat notification if you spotted a player (structure).",
+ "config": "gui.session.notifications.discovered"
+ },
+ {
+ "type": "boolean",
"label": "Chat notification tribute",
"tooltip": "Show a chat notification if an ally tributes resources to another team member if teams are locked, and all tributes in observer mode.",
"config": "gui.session.notifications.tribute"
Index: binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
+++ binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
@@ -3,16 +3,22 @@
constructor(civData)
{
this.handlers = new Set();
+ const playerID = Engine.GetPlayerID();
+ this.hasViewPermission = Engine.GuiInterfaceCall("HasSpyTech", { "player": playerID }) || Engine.GuiInterfaceCall("GetState", { "player": playerID }) != "active";
+ warn(`hasSpyTech = ${ Engine.GuiInterfaceCall("HasSpyTech", { "player": playerID }) } isObserver = ${ Engine.GuiInterfaceCall("GetState", { "player": playerID }) != "active" }`);
+
let civList = Object.keys(civData).map(civ => ({
"name": civData[civ].Name,
"code": civ,
})).sort(sortNameIgnoreCase);
-
this.civSelectionHeading = Engine.GetGUIObjectByName("civSelectionHeading");
- this.civSelectionHeading.caption = this.Caption;
+ const defaultcivSelectionHeadingSize = this.civSelectionHeading["size"];
+ this.civSelectionHeading.caption = this.hasViewPermission ? this.Caption : translate("Espionage tech (civic center) is required to view other civilizations");
+ this.civSelectionHeading["size"] = this.hasViewPermission ? defaultcivSelectionHeadingSize : "0 10 100% 48";
this.civSelection = Engine.GetGUIObjectByName("civSelection");
+ this.civSelection.hidden = !this.hasViewPermission;
this.civSelection.list = civList.map(c => c.name);
this.civSelection.list_data = civList.map(c => c.code);
this.civSelection.onSelectionChange = () => this.onSelectionChange(this);
Index: binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js
===================================================================
--- binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js
+++ binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js
@@ -29,6 +29,29 @@
}
};
+ChatMessageFormatSimulation.discovered = class
+{
+ parse(msg)
+ {
+ if (msg.player != g_ViewedPlayer || Engine.ConfigDB_GetValue("user", "gui.session.notifications.discovered") != "true")
+ return "";
+
+ const message = translate("%(icon)s %(playerFound)s (%(diplomacy)s) has been spotted!");
+
+ return {
+ "text": sprintf(message, {
+ "icon": '[icon="icon_alert"]',
+ "playerFound": colorizePlayernameByID(msg.playerFound),
+ "diplomacy": msg.diplomacy
+ }),
+ "callback": ((target, position) => function() {
+ focusAttack({ "target": target, "position": position });
+ })(msg.target, msg.position),
+ "tooltip": translate("Click to focus location.")
+ };
+ }
+};
+
ChatMessageFormatSimulation.barter = class
{
parse(msg)
Index: binaries/data/mods/public/gui/session/diplomacy/playercontrols/DiplomacyPlayerText.js
===================================================================
--- binaries/data/mods/public/gui/session/diplomacy/playercontrols/DiplomacyPlayerText.js
+++ binaries/data/mods/public/gui/session/diplomacy/playercontrols/DiplomacyPlayerText.js
@@ -15,6 +15,8 @@
this.diplomacyPlayerTeam = Engine.GetGUIObjectByName("diplomacyPlayerTeam" + id);
this.diplomacyPlayerTheirs = Engine.GetGUIObjectByName("diplomacyPlayerTheirs" + id);
this.diplomacyPlayerOutcome = Engine.GetGUIObjectByName("diplomacyPlayerOutcome" + id);
+ this.seenPlayers = Engine.GuiInterfaceCall("GetSeenPlayers", { "player": g_ViewedPlayer });
+ this.HasEpionageTech = Engine.GuiInterfaceCall("HasSpyTech", { "player": g_ViewedPlayer });
this.init();
}
@@ -25,9 +27,11 @@
if (Engine.IsAtlasRunning())
return;
- this.diplomacyPlayerCiv.caption = g_CivData[g_Players[this.playerID].civ].Name;
+ this.diplomacyPlayerCiv.caption = this.HasEpionageTech ? g_CivData[g_Players[this.playerID].civ].Name : "?";
this.diplomacyPlayerName.tooltip = translateAISettings(g_InitAttributes.settings.PlayerData[this.playerID]);
+ this.diplomacyPlayerName.caption = g_Players[this.playerID].name;
+
// Apply offset
let rowSize = DiplomacyDialogPlayerControl.prototype.DiplomacyPlayerText.getRowHeight();
let size = this.diplomacyPlayer.size;
@@ -39,24 +43,46 @@
update()
{
+
+
setOutcomeIcon(g_Players[this.playerID].state, this.diplomacyPlayerOutcome);
- this.diplomacyPlayer.sprite = "color:" + g_DiplomacyColors.getPlayerColor(this.playerID, 32);
+ this.seenPlayers = Engine.GuiInterfaceCall("GetSeenPlayers", { "player": g_ViewedPlayer });
+ this.HasEpionageTech = Engine.GuiInterfaceCall("HasSpyTech", { "player": g_ViewedPlayer });
- this.diplomacyPlayerName.caption = colorizePlayernameByID(this.playerID);
+ warn(uneval(`DIPLOMACY VIEW PERMISSION `));
- this.diplomacyPlayerTeam.caption =
+ if (this.HasEpionageTech || this.playerID == g_ViewedPlayer || g_Players[this.playerID].isAlly[g_ViewedPlayer])
+ {
+ this.diplomacyPlayer.sprite = "color:" + g_DiplomacyColors.getPlayerColor(this.playerID, 32);
+ this.diplomacyPlayerName.caption = colorizePlayernameByID(this.playerID);
+ this.diplomacyPlayerCiv.caption = g_CivData[g_Players[this.playerID].civ].Name;
+
+ this.diplomacyPlayerTeam.caption =
g_Players[this.playerID].team >= 0 ?
g_Players[this.playerID].team + 1 :
translateWithContext("team", this.NoTeam);
- this.diplomacyPlayerTheirs.caption =
- this.playerID == g_ViewedPlayer ? "" :
- g_Players[this.playerID].isAlly[g_ViewedPlayer] ?
- translate(this.Ally) :
- g_Players[this.playerID].isNeutral[g_ViewedPlayer] ?
- translate(this.Neutral) :
- translate(this.Enemy);
+ this.diplomacyPlayerTheirs.caption =
+ this.playerID == g_ViewedPlayer ? "" :
+ g_Players[this.playerID].isAlly[g_ViewedPlayer] ?
+ translate(this.Ally) :
+ g_Players[this.playerID].isNeutral[g_ViewedPlayer] ?
+ translate(this.Neutral) :
+ translate(this.Enemy);
+ }
+ else if (this.seenPlayers.includes(this.playerID))
+ {
+ this.diplomacyPlayer.sprite = "color:" + g_DiplomacyColors.getPlayerColor(this.playerID, 32);
+ this.diplomacyPlayerName.caption = colorizePlayernameByID(this.playerID);
+ this.diplomacyPlayerCiv.caption = g_CivData[g_Players[this.playerID].civ].Name;
+ }
+ else
+ {
+ this.diplomacyPlayerTeam.caption = "?";
+ this.diplomacyPlayerTheirs.caption = "?";
+ }
+
}
};
Index: binaries/data/mods/public/gui/session/messages.js
===================================================================
--- binaries/data/mods/public/gui/session/messages.js
+++ binaries/data/mods/public/gui/session/messages.js
@@ -222,6 +222,36 @@
"targetIsDomesticAnimal": notification.targetIsDomesticAnimal
});
},
+ "discovered": function(notification, player)
+ {
+ if (player != g_ViewedPlayer)
+ return;
+
+ // Focus camera on attacks
+ if (g_FollowPlayer)
+ {
+ setCameraFollow(notification.target);
+
+ g_Selection.reset();
+ if (notification.target)
+ g_Selection.addList([notification.target]);
+ }
+
+ g_LastAttack = { "target": notification.target, "position": notification.position };
+
+ if (Engine.ConfigDB_GetValue("user", "gui.session.notifications.discovered") !== "true")
+ return;
+
+ addChatMessage({
+ "type": "discovered",
+ "player": player,
+ "playerFound": notification.playerFound,
+ "target": notification.target,
+ "position": notification.position,
+ "diplomacy": notification.diplomacy
+ });
+
+ },
"phase": function(notification, player)
{
addChatMessage({
Index: binaries/data/mods/public/gui/session/selection_details.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_details.js
+++ binaries/data/mods/public/gui/session/selection_details.js
@@ -48,15 +48,54 @@
}
}
-// Fills out information that most entities have
+/**
+ * @param {object} enstState - used to check the entity player id and entity type
+ * @returns {boolean}
+ * Check if the player has permission to view unit statistics.
+ * Returns true if the player has the Espionage tech, the entity is Gaia, Allied or a Resource.
+ * Or if the player is an observer or anything else but an active state.
+ */
+function CheckViewPermission(entState)
+{
+ const playerID = Engine.GetPlayerID();
+ const playerState = Engine.GuiInterfaceCall("GetState", { "player": playerID });
+
+ // observers
+ if (playerID == -1 || playerState != 'active')
+ {
+ warn(`SELECTION VIEW PERMISSION ${ playerID == -1 || playerState != 'active' } `);
+ return true;
+ }
+
+ const entityPlayerID = entState.player;
+ const technologyEnabled = Engine.GuiInterfaceCall("HasSpyTech", { "player": playerID });
+
+ warn(`SELECTION VIEW PERMISSION ${ (technologyEnabled || Engine.GuiInterfaceCall("GetState", { "player": playerID }) != "active" || g_Players[entityPlayerID].isAlly[playerID] || playerID == entityPlayerID || !!entState.resourceSupply) } `);
+
+ // If the player has the tech no need to check further, full permission granted.
+ if (technologyEnabled)
+ return true;
+
+
+ if (g_Players[entityPlayerID].isAlly[playerID] || playerID == entityPlayerID || !!entState.resourceSupply)
+ return true;
+
+ return false;
+}
+
+// Fills out information that most entities have, unless the CheckViewPermission() returns false.
function displaySingle(entState)
{
+ const hasViewPermission = CheckViewPermission(entState);
+
let template = GetTemplateData(entState.template);
- let primaryName = g_SpecificNamesPrimary ? template.name.specific : template.name.generic;
+ let primaryName = hasViewPermission ? g_SpecificNamesPrimary ? template.name.specific : template.name.generic
+ : sprintf(translate("Espionage tech required"));
let secondaryName;
if (g_ShowSecondaryNames)
- secondaryName = g_SpecificNamesPrimary ? template.name.generic : template.name.specific;
+ secondaryName = hasViewPermission ? g_SpecificNamesPrimary ? template.name.generic : template.name.specific
+ : sprintf(translate("can be researched at the civic center"));
// If packed, add that to the generic name (reduces template clutter).
if (template.pack && template.pack.state == "packed")
@@ -71,14 +110,14 @@
let civName = g_CivData[playerState.civ].Name;
let civEmblem = g_CivData[playerState.civ].Emblem;
- let playerName = playerState.name;
+ let playerName = hasViewPermission ? playerState.name : "?";
// Indicate disconnected players by prefixing their name
if (g_Players[entState.player].offline)
playerName = sprintf(translate("\\[OFFLINE] %(player)s"), { "player": playerName });
// Rank
- if (entState.identity && entState.identity.rank && entState.identity.classes)
+ if (entState.identity && entState.identity.rank && entState.identity.classes && hasViewPermission)
{
const rankObj = GetTechnologyData(entState.identity.rankTechName, playerState.civ);
Engine.GetGUIObjectByName("rankIcon").tooltip = sprintf(translate("%(rank)s Rank"), {
@@ -136,11 +175,11 @@
{
let unitHealthBar = Engine.GetGUIObjectByName("healthBar");
let healthSize = unitHealthBar.size;
- healthSize.rright = 100 * Math.max(0, Math.min(1, entState.hitpoints / entState.maxHitpoints));
+ healthSize.rright = hasViewPermission ? 100 * Math.max(0, Math.min(1, entState.hitpoints / entState.maxHitpoints)) : 100;
unitHealthBar.size = healthSize;
Engine.GetGUIObjectByName("healthStats").caption = sprintf(translate("%(hitpoints)s / %(maxHitpoints)s"), {
- "hitpoints": Math.ceil(entState.hitpoints),
- "maxHitpoints": Math.ceil(entState.maxHitpoints)
+ "hitpoints": hasViewPermission ? Math.ceil(entState.hitpoints) : "?",
+ "maxHitpoints": hasViewPermission ? Math.ceil(entState.maxHitpoints) : "?"
});
healthSection.size = sectionPosTop.size;
@@ -190,7 +229,7 @@
}
// Experience
- Engine.GetGUIObjectByName("experience").hidden = !entState.promotion;
+ Engine.GetGUIObjectByName("experience").hidden = !entState.promotion || !hasViewPermission;
if (entState.promotion)
{
let experienceBar = Engine.GetGUIObjectByName("experienceBar");
@@ -325,7 +364,7 @@
Engine.GetGUIObjectByName("icon").sprite = template.icon ? ("stretched:session/portraits/" + template.icon) : "BackgroundBlack";
if (template.icon)
Engine.GetGUIObjectByName("iconBorder").onPressRight = () => {
- showTemplateDetails(entState.template, playerState.civ);
+ hasViewPermission ? showTemplateDetails(entState.template, playerState.civ) : '';
};
let detailedTooltip = [
@@ -342,7 +381,7 @@
getUpkeepTooltip,
getLootTooltip
].map(func => func(entState)).filter(tip => tip).join("\n");
- if (detailedTooltip)
+ if (detailedTooltip && hasViewPermission)
{
Engine.GetGUIObjectByName("attackAndResistanceStats").hidden = false;
Engine.GetGUIObjectByName("attackAndResistanceStats").tooltip = detailedTooltip;
@@ -353,13 +392,13 @@
let iconTooltips = [];
iconTooltips.push(setStringTags(primaryName, g_TooltipTextFormats.namePrimaryBig));
- iconTooltips = iconTooltips.concat([
+ iconTooltips = hasViewPermission ? iconTooltips.concat([
getVisibleEntityClassesFormatted,
getAurasTooltip,
getEntityTooltip,
getTreasureTooltip,
showTemplateViewerOnRightClickTooltip
- ].map(func => func(template)));
+ ].map(func => func(template))) : iconTooltips.concat([].map(func => func(template)));
Engine.GetGUIObjectByName("iconBorder").tooltip = iconTooltips.filter(tip => tip).join("\n");
Index: binaries/data/mods/public/gui/session/setup.xml
===================================================================
--- binaries/data/mods/public/gui/session/setup.xml
+++ binaries/data/mods/public/gui/session/setup.xml
@@ -4,4 +4,8 @@
sprite="stretched:session/icons/focus-attacked.png"
size="14 14"
/>
+
Index: binaries/data/mods/public/simulation/components/Fogging.js
===================================================================
--- binaries/data/mods/public/simulation/components/Fogging.js
+++ binaries/data/mods/public/simulation/components/Fogging.js
@@ -164,6 +164,15 @@
return this.seen[player];
};
+
+/**
+ * @returns {array} - playerIDs seen done by exploring
+ */
+Fogging.prototype.GetSeenPlayers = function()
+{
+ return QueryOwnerInterface(this.entity).hasSeenPlayers;
+};
+
Fogging.prototype.OnOwnershipChanged = function(msg)
{
// Always activate fogging for non-Gaia entities.
@@ -202,6 +211,11 @@
{
this.miraged[msg.player] = false;
this.seen[msg.player] = true;
+
+ // add first contact with players to array and push a spotted player notification
+ const cmpPlayer = QueryPlayerIDInterface(msg.player);
+ if (cmpPlayer)
+ cmpPlayer.AddSeenPlayer(QueryOwnerInterface(msg.ent).GetPlayerID(), msg.ent);
}
if (msg.newVisibility == VIS_FOGGED && this.activated)
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -707,6 +707,36 @@
};
/**
+ * @returns {string} the 'active', 'defeated' or 'won' state of the player.
+ */
+GuiInterface.prototype.GetState = function(player)
+{
+ const cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
+ if (!cmpPlayer)
+ return false;
+
+ return cmpPlayer.GetState();
+};
+
+/**
+ * @returns {array} - of all players seen by exploring (fogging)
+ */
+GuiInterface.prototype.GetSeenPlayers = function(player)
+{
+ const cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
+ return cmpPlayer.GetSeenPlayers();
+};
+
+/**
+ * @param {number} - playerID
+ * @returns {bool} - checks if the player has the spy tech
+ */
+GuiInterface.prototype.HasSpyTech = function(player)
+{
+ return QueryPlayerIDInterface(player).HasSpyTech();
+};
+
+/**
* Returns a list of ongoing attacks against the player.
*/
GuiInterface.prototype.GetIncomingAttacks = function(player)
@@ -979,25 +1009,44 @@
return Array.from(this.entsWithAuraAndStatusBars);
};
+GuiInterface.prototype.CheckStatusBarsViewPermission = function(player, ent)
+{
+ const entPlayer = QueryOwnerInterface(ent).GetPlayerID();
+ const cmpPlayer = QueryPlayerIDInterface(player);
+
+ const isResource = Engine.QueryInterface(ent, IID_ResourceSupply) ? true : false;
+ const isObserver = cmpPlayer ? false : true;
+ const HasSpyTech = cmpPlayer ? cmpPlayer.HasSpyTech() : false;
+ const isAlly = cmpPlayer ? cmpPlayer.IsAlly(entPlayer) : false;
+
+ warn(uneval(`STATUSBAR VIEW PERMISSION ${ (player == entPlayer || HasSpyTech || isResource || isAlly || isObserver) } `));
+
+ if (player == entPlayer || HasSpyTech || isResource || isAlly || isObserver)
+ return true;
+
+ return false;
+};
+
GuiInterface.prototype.SetStatusBars = function(player, cmd)
{
let affectedEnts = new Set();
for (let ent of cmd.entities)
{
let cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars);
- if (!cmpStatusBars)
+ if (!cmpStatusBars || !this.CheckStatusBarsViewPermission(player, ent))
continue;
+
cmpStatusBars.SetEnabled(cmd.enabled, cmd.showRank, cmd.showExperience);
- let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
+ const cmpAuras = Engine.QueryInterface(ent, IID_Auras);
if (!cmpAuras)
continue;
- for (let name of cmpAuras.GetAuraNames())
+ for (const name of cmpAuras.GetAuraNames())
{
if (!cmpAuras.GetOverlayIcon(name))
continue;
- for (let e of cmpAuras.GetAffectedEntities(name))
+ for (const e of cmpAuras.GetAffectedEntities(name))
affectedEnts.add(e);
if (cmd.enabled)
this.entsWithAuraAndStatusBars.add(ent);
@@ -2080,6 +2129,9 @@
"CheckTechnologyRequirements": 1,
"GetStartedResearch": 1,
"GetBattleState": 1,
+ "GetState": 1,
+ "GetSeenPlayers": 1,
+ "HasSpyTech": 1,
"GetIncomingAttacks": 1,
"GetNeededResources": 1,
"GetNotifications": 1,
Index: binaries/data/mods/public/simulation/components/Player.js
===================================================================
--- binaries/data/mods/public/simulation/components/Player.js
+++ binaries/data/mods/public/simulation/components/Player.js
@@ -56,6 +56,7 @@
Player.prototype.Init = function()
{
this.playerID = undefined;
+ this.hasSeenPlayers = [];
this.color = undefined;
this.diplomacyColor = undefined;
this.displayDiplomacyColor = false;
@@ -78,6 +79,7 @@
this.cheatsEnabled = false;
this.panelEntities = [];
this.resourceNames = {};
+ this.hasSpyTech = false;
this.disabledTemplates = {};
this.disabledTechnologies = {};
this.spyCostMultiplier = +this.template.SpyCostMultiplier;
@@ -116,6 +118,55 @@
return this.playerID;
};
+/**
+ * Check if a player has been seen before, excluding gaia.
+ */
+Player.prototype.HasSeenPlayer = function(player)
+{
+ if (!this.GetSeenPlayers().includes(player) && player != this.GetPlayerID() && player != 0)
+ return false;
+
+ return true;
+};
+
+/**
+ * Add a seen player to the array and push a notification with sound about it with a location.
+ * A player is count as seen if you found an enemy structure.
+ * @param {number} player - needed for diplomacy
+ * @param {number} ent - needed for pushing notification of the spotted entity location
+ */
+Player.prototype.AddSeenPlayer = function(player, ent)
+{
+ if (this.HasSeenPlayer(player))
+ return;
+
+ const diplomacy = this.IsAlly(player) ? "Allied" : this.IsNeutral(player) ? "Neutral" : "Enemy";
+
+ Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({
+ "type": "discovered",
+ "target": ent,
+ "players": [this.GetPlayerID()],
+ "playerFound": player,
+ "position": Engine.QueryInterface(ent, IID_Position).GetPosition(),
+ "diplomacy": diplomacy
+ });
+
+ this.hasSeenPlayers.push(player);
+ Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager).PlaySoundGroupForPlayer("interface/alarm/alarm_discovered_player.xml", this.GetPlayerID());
+ warn(uneval(`DETECTED NEW PLAYER `));
+
+};
+
+Player.prototype.GetSeenPlayers = function()
+{
+ return this.hasSeenPlayers;
+};
+
+Player.prototype.HasSpyTech = function()
+{
+ return this.hasSpyTech;
+};
+
Player.prototype.SetColor = function(r, g, b)
{
let colorInitialized = !!this.color;
@@ -807,6 +858,8 @@
this.UpdateSharedLos();
else if (msg.tech == this.template.SharedDropsitesTech)
this.sharedDropsites = true;
+ else if (msg.tech == "unlock_spies")
+ this.hasSpyTech = true;
};
Player.prototype.OnDiplomacyChanged = function()
Index: binaries/data/mods/public/simulation/data/technologies/unlock_spies.json
===================================================================
--- binaries/data/mods/public/simulation/data/technologies/unlock_spies.json
+++ binaries/data/mods/public/simulation/data/technologies/unlock_spies.json
@@ -1,6 +1,6 @@
{
"genericName": "Espionage",
- "description": "Merchants' first goal was trading, but they also gathered information about the countries they crossed.",
+ "description": "Send spies to gather information about your enemy's infastructure and army. Providing you useful statistics about enemy units and structures",
"cost": {
"food": 500,
"metal": 500
@@ -9,6 +9,6 @@
"requirementsTooltip": "Unlocked in City Phase.",
"icon": "spy_trader.png",
"researchTime": 60,
- "tooltip": "Allows bribing the units of other players in order to share their vision.",
+ "tooltip": "Unlocks viewing enemy unit statistics and enemy diplomacy info.",
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
}