Show the selection panels to observers / in replays, fixes #3731.

This allows observers to see the training queue, researched techs, unit
stance, constructable buildings, garrisoned units, market prices etc.
for all players without changing the perspective.
All buttons are disabled if the user isn't allowed to control the
affected player.
Notice updateUnitCommands() fills out the panel differently in case of
selecting an allied building.
Extends the GUIInterface methods "GetNeededResources",
"CheckTechnologyRequirements" and "IsTechnologyResearched" to optionally
take a different player-argument.

This was SVN commit r17617.
This commit is contained in:
elexis 2016-01-09 22:01:08 +00:00
parent 9b922ebadd
commit 6b710f35b0
6 changed files with 87 additions and 51 deletions

View File

@ -685,7 +685,7 @@ function handleInputBeforeGui(ev, hoveredObject)
if (result && result.cost)
{
placementSupport.tooltipMessage = getEntityCostTooltip(result);
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", result.cost);
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", { "cost": result.cost });
if (neededResources)
placementSupport.tooltipMessage += getNeededResourcesTooltip(neededResources);
}
@ -1081,7 +1081,7 @@ function handleInputAfterGui(ev)
else
{
// cancel if not enough resources
if (placementSupport.template && Engine.GuiInterfaceCall("GetNeededResources", GetTemplateData(placementSupport.template).cost))
if (placementSupport.template && Engine.GuiInterfaceCall("GetNeededResources", { "cost": GetTemplateData(placementSupport.template).cost }))
{
placementSupport.Reset();
inputState = INPUT_NORMAL;
@ -1414,8 +1414,8 @@ function addTrainingToQueue(selection, trainEntType, playerState)
else if (limits.canBeAddedCount == undefined ||
limits.canBeAddedCount > batchTrainingCount * appropriateBuildings.length)
{
if (Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(
template, batchTrainingCount + batchIncrementSize)))
if (Engine.GuiInterfaceCall("GetNeededResources", { "cost":
multiplyEntityCosts(template, batchTrainingCount + batchIncrementSize) }))
return;
batchTrainingCount += batchIncrementSize;
@ -1432,8 +1432,8 @@ function addTrainingToQueue(selection, trainEntType, playerState)
}
// Don't start a new batch if decrementing or unable to afford it.
if (decrement || Engine.GuiInterfaceCall("GetNeededResources",
multiplyEntityCosts(template, batchIncrementSize)))
if (decrement || Engine.GuiInterfaceCall("GetNeededResources", { "cost":
multiplyEntityCosts(template, batchIncrementSize) }))
return;
inputState = INPUT_BATCHTRAINING;

View File

@ -418,15 +418,6 @@ function updateSelectionDetails()
// Show basic details.
detailsPanel.hidden = false;
if (g_IsObserver)
{
// Observers don't need these displayed.
supplementalDetailsPanel.hidden = true;
commandsPanel.hidden = true;
}
else
{
// Fill out commands panel for specific unit selected (or first unit of primary group)
updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection);
}
// Fill out commands panel for specific unit selected (or first unit of primary group)
updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection);
}

View File

@ -90,7 +90,7 @@ g_SelectionPanels.Alert = {
data.button.hidden = !data.unitEntState.alertRaiser.hasRaisedAlert;
data.icon.sprite = "stretched:session/icons/bell_level0.png";
}
data.button.enabled = !data.button.hidden;
data.button.enabled = !data.button.hidden && controlsPlayer(data.unitEntState.player);
},
};
@ -158,16 +158,24 @@ g_SelectionPanels.Barter = {
// do we have enough of this resource to sell?
var neededRes = {};
neededRes[data.item] = data.amountToSell;
var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", neededRes) != undefined ? "color:255 0 0 80:" : "";
var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
"cost": neededRes,
"player": data.unitEntState.player
}) ? "color:255 0 0 80:" : "";
// Let's see if we have enough resources to barter.
neededRes = {};
neededRes[g_barterSell] = data.amountToSell;
var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", neededRes) != undefined ? "color:255 0 0 80:" : "";
var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
"cost": neededRes,
"player": data.unitEntState.player
}) ? "color:255 0 0 80:" : "";
data.icon.Sell.sprite = canSellCurrent + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
data.icon.Buy.sprite = canBuyAny + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
data.button.Buy.hidden = data.isSelected;
data.button.Buy.enabled = controlsPlayer(data.unitEntState.player);
data.button.Sell.hidden = false;
data.selectionIcon.hidden = !data.isSelected;
},
@ -212,6 +220,7 @@ g_SelectionPanels.Command = {
"setGraphics": function(data)
{
data.icon.sprite = "stretched:session/icons/" + data.item.icon;
data.button.enabled = controlsPlayer(data.unitEntState.player);
},
"setPosition": function(data)
{
@ -262,6 +271,7 @@ g_SelectionPanels.AllyCommand = {
"setGraphics": function(data)
{
data.icon.sprite = "stretched:session/icons/" + data.item.icon;
data.button.enabled = data.item.count > 0;
},
"setPosition": function(data)
{
@ -293,12 +303,18 @@ g_SelectionPanels.Construction = {
data.template = GetTemplateData(data.entType);
if (!data.template) // abort if no template
return false;
data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.template.requiredTechnology);
data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
"tech": data.template.requiredTechnology,
"player": data.unitEntState.player
});
if (data.template.cost)
{
var totalCost = multiplyEntityCosts(data.template, 1);
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
}
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
"cost": multiplyEntityCosts(data.template, 1),
"player": data.unitEntState.player
});
data.limits = getEntityLimitAndCount(data.playerState, data.entType);
return true;
},
@ -342,6 +358,8 @@ g_SelectionPanels.Construction = {
data.button.enabled = false;
modifier += resourcesToAlphaMask(data.neededResources) +":";
}
else
data.button.enabled = controlsPlayer(data.unitEntState.player);
if (data.template.icon)
data.icon.sprite = modifier + "stretched:session/portraits/" + data.template.icon;
@ -394,7 +412,7 @@ g_SelectionPanels.Formation = {
},
"setGraphics": function(data)
{
data.button.enabled = data.formationOk;
data.button.enabled = data.formationOk && controlsPlayer(data.unitEntState.player);
var grayscale = data.formationOk ? "" : "grayscale:";
data.guiSelection.hidden = !data.formationSelected;
data.icon.sprite = "stretched:"+grayscale+"session/icons/"+data.formationInfo.icon;
@ -452,15 +470,12 @@ g_SelectionPanels.Garrison = {
var entplayer = GetEntityState(ents[0]).player;
data.button.sprite = "color:" + rgbToGuiColor(g_Players[entplayer].color) +":";
var player = Engine.GetPlayerID();
if(player != data.unitEntState.player && !g_DevSettings.controlAll)
if (!controlsPlayer(data.unitEntState.player) && !controlsPlayer(entplayer))
{
if (entplayer != player)
{
data.button.enabled = false;
grayscale = "grayscale:";
}
data.button.enabled = false;
grayscale = "grayscale:";
}
data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + data.template.icon;
},
};
@ -545,8 +560,9 @@ g_SelectionPanels.Gate = {
tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", { "cost":
multiplyEntityCosts(data.template, data.wallCount) });
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(data.template, data.wallCount));
if (data.neededResources)
tooltip += getNeededResourcesTooltip(data.neededResources);
}
@ -554,6 +570,7 @@ g_SelectionPanels.Gate = {
},
"setGraphics": function(data)
{
data.button.enabled = controlsPlayer(data.unitEntState.player);
var gateIcon;
if (data.item.gate)
{
@ -640,6 +657,8 @@ g_SelectionPanels.Pack = {
data.icon.sprite = "stretched:session/icons/unpack.png";
else
data.icon.sprite = "stretched:session/icons/pack.png";
data.button.enabled = controlsPlayer(data.unitEntState.player);
},
"setPosition": function(data)
{
@ -720,6 +739,8 @@ g_SelectionPanels.Queue = {
{
if (data.template.icon)
data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
data.button.enabled = controlsPlayer(data.unitEntState.player);
},
};
@ -779,11 +800,17 @@ g_SelectionPanels.Research = {
});
data.neededResources = data.template.map(function(t) {
return Engine.GuiInterfaceCall("GetNeededResources", t.cost);
return Engine.GuiInterfaceCall("GetNeededResources", {
"cost": t.cost,
"player": data.unitEntState.player
});
});
data.requirementsPassed = data.entType.map(function(e) {
return Engine.GuiInterfaceCall("CheckTechnologyRequirements",e);
return Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
"tech": e,
"player": data.unitEntState.player
});
});
data.pair = Engine.GetGUIObjectByName("unitResearchPair["+data.i+"]");
@ -806,7 +833,7 @@ g_SelectionPanels.Research = {
tooltip += "\n" + template.requirementsTooltip;
if (template.classRequirements)
{
var player = Engine.GetPlayerID();
var player = data.unitEntState.player;
var current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
var remaining = template.classRequirements.number - current;
tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { number: remaining});
@ -862,7 +889,7 @@ g_SelectionPanels.Research = {
modifier += resourcesToAlphaMask(data.neededResources[i]) + ":";
}
else
button.enabled = true;
button.enabled = controlsPlayer(data.unitEntState.player);
if (data.template[i].icon)
data.icon[i].sprite = modifier + "stretched:session/portraits/" + data.template[i].icon;
@ -970,6 +997,8 @@ g_SelectionPanels.Selection = {
{
if (data.template.icon)
data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
data.button.enabled = controlsPlayer(data.unitEntState.player);
},
};
@ -1005,6 +1034,7 @@ g_SelectionPanels.Stance = {
{
data.guiSelection.hidden = !data.stanceSelected;
data.icon.sprite = "stretched:session/icons/stances/"+data.item+".png";
data.button.enabled = controlsPlayer(data.unitEntState.player);
},
};
@ -1024,7 +1054,11 @@ g_SelectionPanels.Training = {
data.template = GetTemplateData(data.entType);
if (!data.template)
return false;
data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.template.requiredTechnology);
data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
"tech": data.template.requiredTechnology,
"player": data.unitEntState.player
});
var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.entType, data.selection);
@ -1038,7 +1072,10 @@ g_SelectionPanels.Training = {
if (data.template.cost)
{
var totalCosts = multiplyEntityCosts(data.template, data.trainNum);
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCosts);
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
"cost": totalCosts,
"player": data.unitEntState.player
});
}
return true;

View File

@ -248,6 +248,14 @@ function selectViewPlayer(playerID)
openTrade();
}
/**
* Returns true if the current user can issue commands for that player.
*/
function controlsPlayer(playerID)
{
return Engine.GetPlayerID() == playerID || g_DevSettings.controlAll;
}
function updateTopPanel()
{
let playerID = Engine.GetPlayerID();

View File

@ -168,7 +168,7 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
var simState = GetSimState();
var playerState = simState.players[player];
if (entState.player == player || g_DevSettings.controlAll)
if (controlsPlayer(entState.player) || player == -1)
{
for (var guiName of g_PanelsOrder)
{
@ -178,7 +178,7 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
)
continue;
setupUnitPanel(guiName, entState, playerState);
setupUnitPanel(guiName, entState, simState.players[entState.player]);
}
supplementalDetailsPanel.hidden = false;

View File

@ -589,28 +589,28 @@ GuiInterface.prototype.GetTechnologyData = function(player, name)
return GetTechnologyDataHelper(template, cmpPlayer.GetCiv());
};
GuiInterface.prototype.IsTechnologyResearched = function(player, tech)
GuiInterface.prototype.IsTechnologyResearched = function(player, data)
{
if (!tech)
if (!data.tech)
return true;
let cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
let cmpTechnologyManager = QueryPlayerIDInterface(data.player || player, IID_TechnologyManager);
if (!cmpTechnologyManager)
return false;
return cmpTechnologyManager.IsTechnologyResearched(tech);
return cmpTechnologyManager.IsTechnologyResearched(data.tech);
};
// Checks whether the requirements for this technology have been met
GuiInterface.prototype.CheckTechnologyRequirements = function(player, tech)
GuiInterface.prototype.CheckTechnologyRequirements = function(player, data)
{
let cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
let cmpTechnologyManager = QueryPlayerIDInterface(data.player || player, IID_TechnologyManager);
if (!cmpTechnologyManager)
return false;
return cmpTechnologyManager.CanResearch(tech);
return cmpTechnologyManager.CanResearch(data.tech);
};
// Returns technologies that are being actively researched, along with
@ -652,9 +652,9 @@ GuiInterface.prototype.GetIncomingAttacks = function(player)
};
// Used to show a red square over GUI elements you can't yet afford.
GuiInterface.prototype.GetNeededResources = function(player, amounts)
GuiInterface.prototype.GetNeededResources = function(player, data)
{
return QueryPlayerIDInterface(player).GetNeededResources(amounts);
return QueryPlayerIDInterface(data.player || player).GetNeededResources(data.cost);
};
/**