1
0
forked from 0ad/0ad

Make the barter panel uniform with the others, clean up more gui code.

This was SVN commit r15375.
This commit is contained in:
sanderd17 2014-06-16 18:34:27 +00:00
parent ce3b11a3fe
commit 013ab4bda7
10 changed files with 498 additions and 413 deletions

View File

@ -203,6 +203,11 @@ function translateObjectKeys(object, keys) {
}
}
/**
* Function is used by the extract-messages tool.
* So it may only be used on a plain string,
* it won't have any effect on a calculated string.
*/
function markForTranslation(message) {
return message;
}

View File

@ -285,7 +285,7 @@ function determineAction(x, y, fromMinimap)
// if two actions are possible, the first one is taken
// so the most specific should appear first
var actions = Object.keys(unitActions).slice();
actions.sort(function(a, b) {return unitActions[a].specificness > unitActions[b].specificness;});
actions.sort(function(a, b) {return unitActions[a].specificness - unitActions[b].specificness;});
var actionInfo = undefined;
if (preSelectedAction != ACTION_NONE)
@ -1195,7 +1195,7 @@ function handleMinimapEvent(target)
// @param buildTemplate Template name of the entity the user wants to build
function startBuildingPlacement(buildTemplate, playerState)
{
if(getEntityLimitAndCount(playerState, buildTemplate)[2] == 0)
if(getEntityLimitAndCount(playerState, buildTemplate).canBeAddedCount == 0)
return;
// TODO: we should clear any highlight selection rings here. If the mouse was over an entity before going onto the GUI
@ -1314,24 +1314,26 @@ function getBuildingsWhichCanTrainEntity(entitiesToCheck, trainEntType)
function getEntityLimitAndCount(playerState, entType)
{
var r = {
"entLimit": undefined,
"entCount": undefined,
"entLimitChangers": undefined,
"canBeAddedCount": undefined
};
var template = GetTemplateData(entType);
var entCategory = null;
if (template.trainingRestrictions)
entCategory = template.trainingRestrictions.category;
else if (template.buildRestrictions)
entCategory = template.buildRestrictions.category;
var entLimit = undefined;
var entCount = undefined;
var entLimitChangers = undefined;
var canBeAddedCount = undefined;
if (entCategory && playerState.entityLimits[entCategory] != null)
{
entLimit = playerState.entityLimits[entCategory];
entCount = playerState.entityCounts[entCategory];
entLimitChangers = playerState.entityLimitChangers[entCategory];
canBeAddedCount = Math.max(entLimit - entCount, 0);
r.entLimit = playerState.entityLimits[entCategory] || Infinity;
r.entCount = playerState.entityCounts[entCategory] || 0;
r.entLimitChangers = playerState.entityLimitChangers[entCategory];
r.canBeAddedCount = Math.max(r.entLimit - r.entCount, 0);
}
return [entLimit, entCount, canBeAddedCount, entLimitChangers];
return r;
}
// Add the unit shown at position to the training queue for all entities in the selection
@ -1363,10 +1365,10 @@ function addTrainingToQueue(selection, trainEntType, playerState)
var appropriateBuildings = getBuildingsWhichCanTrainEntity(selection, trainEntType);
// Check trainEntType entity limit and count
var [trainEntLimit, trainEntCount, canBeTrainedCount] = getEntityLimitAndCount(playerState, trainEntType)
var limits = getEntityLimitAndCount(playerState, trainEntType);
// Batch training possible if we can train at least 2 units
var batchTrainingPossible = canBeTrainedCount == undefined || canBeTrainedCount > 1;
var batchTrainingPossible = limits.canBeAddedCount == undefined || limits.canBeAddedCount > 1;
var decrement = Engine.HotkeyIsPressed("selection.remove");
if (!decrement)
@ -1398,8 +1400,8 @@ function addTrainingToQueue(selection, trainEntType, playerState)
if (batchTrainingCount <= 0)
inputState = INPUT_NORMAL;
}
else if (canBeTrainedCount == undefined ||
canBeTrainedCount > batchTrainingCount * appropriateBuildings.length)
else if (limits.canBeAddedCount == undefined ||
limits.canBeAddedCount > batchTrainingCount * appropriateBuildings.length)
{
if (Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(
template, batchTrainingCount + batchIncrementSize)))
@ -1407,7 +1409,7 @@ function addTrainingToQueue(selection, trainEntType, playerState)
batchTrainingCount += batchIncrementSize;
}
batchTrainingEntityAllowedCount = canBeTrainedCount;
batchTrainingEntityAllowedCount = limits.canBeAddedCount;
return;
}
// Otherwise start a new one
@ -1426,7 +1428,7 @@ function addTrainingToQueue(selection, trainEntType, playerState)
inputState = INPUT_BATCHTRAINING;
batchTrainingEntities = selection;
batchTrainingType = trainEntType;
batchTrainingEntityAllowedCount = canBeTrainedCount;
batchTrainingEntityAllowedCount = limits.canBeAddedCount;
batchTrainingCount = batchIncrementSize;
}
else
@ -1434,8 +1436,8 @@ function addTrainingToQueue(selection, trainEntType, playerState)
// Non-batched - just create a single entity in each building
// (but no more than entity limit allows)
var buildingsForTraining = appropriateBuildings;
if (trainEntLimit)
buildingsForTraining = buildingsForTraining.slice(0, canBeTrainedCount);
if (limits.entLimit)
buildingsForTraining = buildingsForTraining.slice(0, limits.canBeAddedCount);
Engine.PostNetworkCommand({"type": "train", "template": trainEntType,
"count": 1, "entities": buildingsForTraining});
}
@ -1462,27 +1464,27 @@ function getTrainingBatchStatus(playerState, entity, trainEntType, selection)
{
nextBatchTrainingCount = batchTrainingCount;
currentBatchTrainingCount = batchTrainingCount;
var canBeTrainedCount = batchTrainingEntityAllowedCount;
var limits = {
"canBeAddedCount": batchTrainingEntityAllowedCount
};
}
else
{
var [trainEntLimit, trainEntCount, canBeTrainedCount] =
getEntityLimitAndCount(playerState, trainEntType);
var batchSize = Math.min(canBeTrainedCount, batchIncrementSize);
var limits = getEntityLimitAndCount(playerState, trainEntType);
}
// We need to calculate count after the next increment if it's possible
if (canBeTrainedCount == undefined ||
canBeTrainedCount > nextBatchTrainingCount * appropriateBuildings.length)
if (limits.canBeAddedCount == undefined ||
limits.canBeAddedCount > nextBatchTrainingCount * appropriateBuildings.length)
nextBatchTrainingCount += batchIncrementSize;
// If training limits don't allow us to train batchTrainingCount in each appropriate building
// train as many full batches as we can and remainer in one more building.
var buildingsCountToTrainFullBatch = appropriateBuildings.length;
var remainderToTrain = 0;
if (canBeTrainedCount !== undefined &&
canBeTrainedCount < nextBatchTrainingCount * appropriateBuildings.length)
if (limits.canBeAddedCount !== undefined &&
limits.canBeAddedCount < nextBatchTrainingCount * appropriateBuildings.length)
{
buildingsCountToTrainFullBatch = Math.floor(canBeTrainedCount / nextBatchTrainingCount);
remainderToTrain = canBeTrainedCount % nextBatchTrainingCount;
buildingsCountToTrainFullBatch = Math.floor(limits.canBeAddedCount / nextBatchTrainingCount);
remainderToTrain = limits.canBeAddedCount % nextBatchTrainingCount;
}
return [buildingsCountToTrainFullBatch, nextBatchTrainingCount, remainderToTrain, currentBatchTrainingCount];
}

View File

@ -61,6 +61,7 @@ EntityGroups.prototype.add = function(ents)
var templateName = entState.template;
var key = GetTemplateData(templateName).selectionGroupName || templateName;
// TODO ugly hack, just group them by player too.
// Prefix garrisoned unit's selection name with the player they belong to
var index = templateName.indexOf("&");
if (index != -1 && key.indexOf("&") == -1)
@ -139,6 +140,23 @@ EntityGroups.prototype.getEntsByName = function(templateName)
return ents;
};
/**
* get a list of entities grouped by templateName
*/
EntityGroups.prototype.getEntsGrouped = function()
{
var templateNames = this.getTemplateNames();
var list = [];
for (var t of templateNames)
{
list.push({
"ents": this.getEntsByName(t),
"template": t,
});
}
return list;
};
/**
* Gets all ents in every group except ones of the specified group
*/

View File

@ -428,8 +428,5 @@ function updateSelectionDetails()
{
// Fill out commands panel for specific unit selected (or first unit of primary group)
updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection);
// Show panels
supplementalDetailsPanel.hidden = false;
commandsPanel.hidden = false;
}
}

View File

@ -1,18 +1,128 @@
/**
* Contains the layout and button settings per selection panel
*
* addData is called first, and can be used to abort going any furter
* by returning false. Else it should always return true.
* getItems returns a list of basic items used to fill the panel.
* This method is obligated. If the items list is empty, the panel
* won't be rendered.
*
* Then there's a loop over all items provided. In the loop,
* the item and some other standard data is added to a data object.
*
* addData is used to add data to the data object that can be used in the
* content setter functions.
* The standard data is
* var data = {
* "i": index
* "item": item coming from the getItems function
* "selection": list of currently selected items
* "playerState": playerState
* "unitEntState": first selected entity state
* "rowLength": rowLength
* "numberOfItems": number of items that will be processed
* "button": gui Button object
* "affordableMask": gui Unaffordable overlay
* "icon": gui Icon object
* "guiSelection": gui button Selection overlay
* "countDisplay": gui caption space
* };
*
* Then, addData is called, and can be used to abort the processing
* of the current item by returning false.
* It should return true if you want the panel to be filled.
*
* addData is used to add data to the data object on top
* (or instead of) the standard data.
* addData is not obligated, the function will just continue
* with the content setters if no addData is present.
*
* After the addData, all functions starting with "set" are called.
* These are used to set various parts of content.
*/
var g_SelectionPanels = {};
// BARTER
g_SelectionPanels.Barter = {
"maxNumberOfItems": 4,
"rowLength": 4,
"getItems": function(unitEntState, selection)
{
if (!unitEntState.barterMarket)
return [];
// ["food", "wood", "stone", "metal"]
return BARTER_RESOURCES;
},
"addData": function(data)
{
// data.item is the resource name in this case
data.button = {};
data.icon = {};
data.amount = {};
for (var a of BARTER_ACTIONS)
{
data.button[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Button["+data.i+"]");
data.icon[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Icon["+data.i+"]");
data.amount[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Amount["+data.i+"]");
}
data.selection = Engine.GetGUIObjectByName("unitBarterSellSelection["+data.i+"]");
data.affordableMask = Engine.GetGUIObjectByName("unitBarterSellUnaffordable["+data.i+"]");
data.amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
if (Engine.HotkeyIsPressed("session.massbarter"))
data.amountToSell *= BARTER_BUNCH_MULTIPLIER;
data.isSelected = data.item == g_barterSell;
return true;
},
"setCountDisplay": function(data)
{
data.amount.Sell.caption = "-" + data.amountToSell;
var sellPrice = data.unitEntState.barterMarket.prices["sell"][g_barterSell];
var buyPrice = data.unitEntState.barterMarket.prices["buy"][data.item];
data.amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * data.amountToSell);
},
"setTooltip": function(data)
{
data.button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), {"resource": translate(data.item)});
data.button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), {"resource": translate(data.item)});
},
"setAction": function(data)
{
data.button.Sell.onPress = function() { g_barterSell = data.item; };
var exchangeResourcesParameters = {
"sell": g_barterSell,
"buy": data.item,
"amount": data.amountToSell
};
data.button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
},
"setGraphics": function(data)
{
var grayscale = data.isSelected ? "grayscale:" : "";
data.button.Buy.hidden = data.isSelected;
data.button.Sell.hidden = false;
for each (var icon in data.icon)
icon.sprite = "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
var neededRes = {};
neededRes[data.item] = data.amountToSell;
if (Engine.GuiInterfaceCall("GetNeededResources", neededRes))
data.affordableMask.hidden = false;
else
data.affordableMask.hidden = true;
data.selection.hidden = !data.isSelected;
},
"setPosition": function(data)
{
setPanelObjectPosition(data.button.Sell, data.i, data.rowLength);
setPanelObjectPosition(data.button.Buy, data.i + data.rowLength, data.rowLength);
},
},
// COMMAND
g_SelectionPanels.Command = {
"maxNumberOfItems": 6,
"getItems": function(unitEntState)
{
return getEntityCommandsList(unitEntState)
},
"setTooltip": function(data)
{
if (data.item.tooltip)
@ -20,12 +130,13 @@ g_SelectionPanels.Command = {
else
data.button.tooltip = toTitleCase(data.item.name);
},
"setAction": function(data)
{
data.button.onPress = function() { data.item.callback ? data.item.callback(data.item) : performCommand(data.unitEntState.id, data.item.name); };
},
"setCountDisplay": function(data)
{
var count = 0;
if (data.item.name == "unload-all")
count = data.garrisonGroups.getTotalCount();
data.countDisplay.caption = count || "";
data.countDisplay.caption = data.item.count || "";
},
"setGraphics": function(data)
{
@ -48,6 +159,11 @@ g_SelectionPanels.Command = {
// CONSTRUCTION
g_SelectionPanels.Construction = {
"maxNumberOfItems": 24,
"conflictsWith": ["Gate", "Pack", "Training"],
"getItems": function()
{
return getAllBuildableEntitiesFromSelection();
},
"addData": function(data)
{
data.entType = data.item;
@ -60,8 +176,13 @@ g_SelectionPanels.Construction = {
var totalCost = multiplyEntityCosts(data.template, 1);
data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
}
data.limits = getEntityLimitAndCount(data.playerState, data.entType);
return true;
},
"setAction": function(data)
{
data.button.onPress = function () { startBuildingPlacement(data.item, data.playerState); };
},
"setTooltip": function(data)
{
var tooltip = getEntityNamesFormatted(data.template);
@ -73,13 +194,7 @@ g_SelectionPanels.Construction = {
tooltip += "\n" + getEntityCostTooltip(data.template);
tooltip += getPopulationBonusTooltip(data.template);
var limits = getEntityLimitAndCount(data.playerState, data.entType);
data.entLimit = limits[0];
data.entCount = limits[1];
data.canBeAddedCount = limits[2];
data.entLimitChangers = limits[3];
tooltip += formatLimitString(data.entLimit, data.entCount, data.entLimitChangers);
tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
if (!data.technologyEnabled)
{
var techName = getEntityNames(GetTechnologyData(data.template.requiredTechnology));
@ -93,7 +208,7 @@ g_SelectionPanels.Construction = {
"setGraphics": function(data)
{
var grayscale = "";
if (!data.technologyEnabled || data.canBeAddedCount == 0)
if (!data.technologyEnabled || data.limits.canBeAddedCount == 0)
{
data.button.enabled = false;
grayscale = "grayscale:";
@ -115,6 +230,13 @@ g_SelectionPanels.Construction = {
g_SelectionPanels.Formation = {
"maxNumberOfItems": 16,
"rowLength": 4,
"conflictsWith": ["Garrison"],
"getItems": function(unitEntState)
{
if (!hasClass(unitEntState, "Unit") || hasClass(unitEntState, "Animal"))
return [];
return Engine.GuiInterfaceCall("GetAvailableFormations");
},
"addData": function(data)
{
data.formationInfo = Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", {"templateName": data.item});
@ -125,9 +247,13 @@ g_SelectionPanels.Formation = {
});
return true;
},
"setAction": function(data)
{
data.button.onPress = function() { performFormation(data.unitEntState.id, data.item); };
},
"setTooltip": function(data)
{
var tooltip = translate(data.formationInfo.name);
var tooltip = translate(data.formationInfo.name);
if (!data.formationOk && data.formationInfo.tooltip)
tooltip += "\n" + "[color=\"red\"]" + translate(data.formationInfo.tooltip) + "[/color]";
data.button.tooltip = tooltip;
@ -145,16 +271,33 @@ g_SelectionPanels.Formation = {
g_SelectionPanels.Garrison = {
"maxNumberOfItems": 12,
"rowLength": 4,
"getItems": function(unitEntState, selection)
{
if (!unitEntState.garrisonHolder)
return [];
var groups = new EntityGroups();
for (var ent of selection)
{
var state = GetEntityState(ent);
if (state.garrisonHolder)
groups.add(state.garrisonHolder.entities)
}
return groups.getEntsGrouped();
},
"addData": function(data)
{
data.entType = data.item;
data.entType = data.item.template;
data.template = GetTemplateData(data.entType);
if (!data.template)
return false;
data.name = getEntityNames(data.template);
data.count = data.garrisonGroups.getCount(data.item);
data.count = data.item.ents.length;
return true;
},
"setAction": function(data)
{
data.button.onPress = function() { unloadTemplate(data.item.template); };
},
"setTooltip": function(data)
{
var tooltip = sprintf(translate("Unload %(name)s"), { name: data.name }) + "\n";
@ -168,7 +311,7 @@ g_SelectionPanels.Garrison = {
"setGraphics": function(data)
{
var grayscale = "";
var ents = data.garrisonGroups.getEntsByName(data.item);
var ents = data.item.ents;
var entplayer = GetEntityState(ents[0]).player;
data.button.sprite = "colour: " + rgbToGuiColor(g_Players[entplayer].color);
@ -188,6 +331,70 @@ g_SelectionPanels.Garrison = {
// GATE
g_SelectionPanels.Gate = {
"maxNumberOfItems": 8,
"conflictsWith": ["Construction", "Pack", "Training"],
"getItems": function(unitEntState, selection)
{
if (unitEntState.foundation)
return [];
if (!hasClass(unitEntState, "LongWall") && !unitEntState.gate)
return [];
// Allow long wall pieces to be converted to gates
var longWallTypes = {};
var walls = [];
var gates = [];
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
{
var gateTemplate = getWallGateTemplate(state.id);
if (gateTemplate)
{
var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
if (!tooltipString)
{
warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
tooltipString = "";
}
walls.push({
"tooltip": translate(tooltipString),
"template": gateTemplate,
"callback": function (item) { transformWallToGate(item.template); }
});
}
// We only need one entity per type.
longWallTypes[state.template] = true;
}
else if (state.gate && !gates.length)
{
gates.push({
"gate": state.gate,
"tooltip": translate("Lock Gate"),
"locked": true,
"callback": function (item) { lockGate(item.locked); }
});
gates.push({
"gate": state.gate,
"tooltip": translate("Unlock Gate"),
"locked": false,
"callback": function (item) { lockGate(item.locked); }
});
}
// Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
else if (state.gate && state.gate.locked != gates[0].gate.locked)
for (var j = 0; j < gates.length; ++j)
delete gates[j].gate.locked;
}
// Place wall conversion options after gate lock/unlock icons.
var items = gates.concat(walls);
return items;
},
"setAction": function(data)
{
data.button.onPress = function() {data.item.callback(data.item); };
},
"setTooltip": function(data)
{
var tooltip = data.item.tooltip;
@ -229,7 +436,7 @@ g_SelectionPanels.Gate = {
var template = GetTemplateData(data.item.template);
if (!template)
return;
gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
data.guiSelection.hidden = true;
}
@ -240,6 +447,48 @@ g_SelectionPanels.Gate = {
// PACK
g_SelectionPanels.Pack = {
"maxNumberOfItems": 8,
"conflictsWith": ["Construction", "Gate", "Training"],
"getItems": function(unitEntState, selection)
{
if (!unitEntState.pack)
return [];
var checks = {};
for (var ent of selection)
{
var state = GetEntityState(ent);
if (!state.pack)
continue;
if (state.pack.progress == 0)
{
if (!state.pack.packed)
checks.packButton = true;
else if (state.pack.packed)
checks.unpackButton = true;
}
else
{
// Already un/packing - show cancel button
if (!state.pack.packed)
checks.packCancelButton = true;
else if (state.pack.packed)
checks.unpackCancelButton = true;
}
}
var items = [];
if (checks.packButton)
items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
if (checks.unpackButton)
items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } });
if (checks.packCancelButton)
items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } });
if (checks.unpackCancelButton)
items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
return items;
},
"setAction": function(data)
{
data.button.onPress = function() {data.item.callback(data.item); };
},
"setTooltip": function(data)
{
data.button.tooltip = data.item.tooltip;
@ -258,6 +507,20 @@ g_SelectionPanels.Pack = {
// QUEUE
g_SelectionPanels.Queue = {
"maxNumberOfItems": 16,
"getItems": function(unitEntState)
{
if (!unitEntState.production)
return [];
return unitEntState.production.queue;
},
"resizePanel": function(numberOfItems, rowLength)
{
var numRows = Math.ceil(numberOfItems / rowLength);
var panel = Engine.GetGUIObjectByName("unitQueuePanel");
var size = panel.size;
size.top = (UNIT_PANEL_BASE - ((numRows-1)*UNIT_PANEL_HEIGHT));
panel.size = size;
},
"addData": function(data)
{
// differentiate between units and techs
@ -275,6 +538,10 @@ g_SelectionPanels.Queue = {
data.progress = Math.round(data.item.progress*100) + "%";
return data.template;
},
"setAction": function(data)
{
data.button.onPress = function() { removeFromProductionQueue(data.unitEntState.id, data.item.id); };
},
"setTooltip": function(data)
{
var tooltip = getEntityNames(data.template);
@ -313,6 +580,15 @@ g_SelectionPanels.Queue = {
// RESEARCH
g_SelectionPanels.Research = {
"maxNumberOfItems": 8,
"getItems": function(unitEntState, selection)
{
if (!unitEntState.production)
return [];
// TODO 8 is the row lenght, make variable
if (getNumberOfRightPanelButtons() > 8 && selection.length > 1)
return [];
return unitEntState.production.technologies;
},
"hideItem": function(i, rowLength) // called when no item is found
{
Engine.GetGUIObjectByName("unitResearchButton["+i+"]").hidden = true;
@ -393,7 +669,7 @@ g_SelectionPanels.Research = {
data.button[i].tooltip = tooltip;
}
},
"setActions": function(data)
"setAction": function(data)
{
for (var i in data.entType)
{
@ -401,7 +677,9 @@ g_SelectionPanels.Research = {
var others = Object.keys(data.template);
others.splice(i, 1);
var button = data.button[i];
button.onpress = (function(e){ return function() { data.callback(e) } })(data.entType[i]);
// as we're in a loop, we need to limit the scope with a closure
// else the last value of the loop will be taken, rather than the current one
button.onpress = (function(template) { return function () { addResearchToQueue(data.unitEntState.id, template); }; })(data.entType[i]);
// on mouse enter, show a cross over the other icons
button.onmouseenter = (function(others, icons) {
return function() {
@ -445,7 +723,7 @@ g_SelectionPanels.Research = {
if (data.template[i].icon)
data.icon[i].sprite = "stretched:" + grayscale + "session/portraits/" + data.template[i].icon;
}
for (var button of data.buttonsToHide)
for (var button of data.buttonsToHide)
button.hidden = true;
// show the tech connector
data.pair.hidden = data.item.pair == null;
@ -462,6 +740,12 @@ g_SelectionPanels.Research = {
g_SelectionPanels.Selection = {
"maxNumberOfItems": 16,
"rowLength": 4,
"getItems": function(unitEntState, selection)
{
if (selection.length < 2)
return [];
return g_Selection.groups.getTemplateNames();
},
"addData": function(data)
{
data.entType = data.item;
@ -480,10 +764,10 @@ g_SelectionPanels.Selection = {
{
data.countDisplay.caption = data.count || "";
},
"setActions": function(data)
"setAction": function(data)
{
data.button.onpressright = (function(e){return function() {data.callback(e, true) } })(data.item);
data.button.onpress = (function(e){ return function() {data.callback(e, false) } })(data.item);
data.button.onpressright = function() { changePrimarySelectionGroup(data.item, true); };
data.button.onpress = function() { changePrimarySelectionGroup(data.item, false); };
},
"setGraphics": function(data)
{
@ -495,6 +779,12 @@ g_SelectionPanels.Selection = {
// STANCE
g_SelectionPanels.Stance = {
"maxNumberOfItems": 5,
"getItems": function(unitEntState)
{
if (!unitEntState.unitAI || !hasClass(unitEntState, "Unit") || hasClass(unitEntState, "Animal"))
return [];
return unitEntState.unitAI.possibleStances;
},
"addData": function(data)
{
data.stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
@ -503,6 +793,10 @@ g_SelectionPanels.Stance = {
});
return true;
},
"setAction": function(data)
{
data.button.onPress = function() { performStance(data.unitEntState, data.item); };
},
"setTooltip": function(data)
{
data.button.tooltip = getStanceDisplayName(data.item);
@ -517,6 +811,11 @@ g_SelectionPanels.Stance = {
// TRAINING
g_SelectionPanels.Training = {
"maxNumberOfItems": 24,
"conflictsWith": ["Construction", "Gate", "Pack"],
"getItems": function()
{
return getAllTrainableEntitiesFromSelection();
},
"addData": function(data)
{
data.entType = data.item;
@ -542,6 +841,10 @@ g_SelectionPanels.Training = {
return true;
},
"setAction": function(data)
{
data.button.onPress = function() { addTrainingToQueue(data.selection, data.item, data.playerState); };
},
"setCountDisplay": function(data)
{
var count = "";
@ -574,14 +877,9 @@ g_SelectionPanels.Training = {
tooltip += "\n" + getEntityCostTooltip(data.template, data.trainNum, data.unitEntState.id);
// TODO make the getEntityLimitAndCount method return something nicer than an array
var limits = getEntityLimitAndCount(data.playerState, data.entType);
data.entLimit = limits[0];
data.entCount = limits[1];
data.canBeAddedCount = limits[2];
data.entLimitChangers = limits[3];
data.limits = getEntityLimitAndCount(data.playerState, data.entType);
tooltip += formatLimitString(data.entLimit, data.entCount, data.entLimitChangers);
tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true")
{
if (data.template.health)
@ -608,3 +906,30 @@ g_SelectionPanels.Training = {
"setGraphics": g_SelectionPanels.Construction.setGraphics,
};
/**
* If two panels need the same space, so they collide,
* the one appearing first in the order is rendered.
*
* Note that the panel needs to appear in the list to get rendered.
*/
var g_PanelsOrder = [
// LEFT PANE
"Barter", // must always be visible on markets
"Garrison", // more important than Formation, as you want to see the garrisoned units in ships
"Formation",
"Stance", // normal together with formation
// RIGHT PANE
"Gate", // must always be shown on gates
"Pack", // must always be shown on packable entities
"Training", // lets hope training and construction never happen together
"Construction",
"Research", // normal together with training
// UNIQUE PANES (importance doesn't matter)
"Command",
"Queue",
"Selection",
];

View File

@ -860,21 +860,18 @@
<object ghost="true" style="resourceText" type="text" size="0 0 100% 20">
<translatableAttribute id="tooltip">Exchange resources:</translatableAttribute>
</object>
<object size="0 32 100% 78">
<object size="0 32 100% 124">
<repeat count="4">
<!-- sell -->
<object name="unitBarterSellButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold">
<object name="unitBarterSellIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
<object name="unitBarterSellUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 60"/>
<object name="unitBarterSellAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/>
<object name="unitBarterSellSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/>
</object>
</repeat>
</object>
<object size="0 78 100% 124">
<repeat count="4">
<!-- buy -->
<object name="unitBarterBuyButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold">
<object name="unitBarterBuyIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
<object name="unitBarterBuyUnaffordable[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="colour: 255 0 0 60"/>
<object name="unitBarterBuyAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/>
</object>
</repeat>

View File

@ -633,9 +633,18 @@ var entityCommands =
{
if (!entState.garrisonHolder)
return false;
var selection = g_Selection.toList();
var count = 0;
for (var ent of selection)
{
var state = GetEntityState(ent);
if (state.garrisonHolder)
count += state.garrisonHolder.entities.length;
}
return {
"tooltip": translate("Unload All"),
"icon": "garrison-out.png"
"icon": "garrison-out.png",
"count": count,
};
},
"execute": function(entState)

View File

@ -16,7 +16,12 @@ const UNIT_PANEL_BASE = -52; // QUEUE: The offset above the main panel (will oft
const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons
// Trading constants
const TRADING_RESOURCES = ["food", "wood", "stone", "metal"];
const TRADING_RESOURCES = [
markForTranslation("food"),
markForTranslation("wood"),
markForTranslation("stone"),
markForTranslation("metal")
];
// Barter constants
const BARTER_RESOURCE_AMOUNT_TO_SELL = 100;
@ -30,12 +35,15 @@ const GATE_ACTIONS = ["lock", "unlock"];
// The number of currently visible buttons (used to optimise showing/hiding)
var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0};
// Unit panels are panels with row(s) of buttons
var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Barter", "Construction", "Research", "Stance", "Command", "Gate", "Pack"];
// Indexes of resources to sell and buy on barter panel
var g_barterSell = 0;
// Resources to sell on barter panel
var g_barterSell = "food";
/**
* Set the position of a panel object according to the index,
* from left to right, from top to bottom.
* Will wrap around to subsequent rows if the index
* is larger than rowLength.
*/
function setPanelObjectPosition(object, index, rowLength, vMargin = 1, hMargin = 1)
{
var size = object.size;
@ -165,24 +173,23 @@ function getStanceDisplayName(name)
* unit.
*
* @param guiName Short identifier string of this panel; see constants defined at the top of this file.
* @param usedPanels Output object; usedPanels[guiName] will be set to 1 to indicate that this panel was used during this
* run of updateUnitCommands and should not be hidden. TODO: why is this done this way instead of having
* updateUnitCommands keep track of this?
* @param unitEntState Entity state of the (first) selected unit.
* @param items Panel-specific data to construct the icons with.
* @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
*/
function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
function setupUnitPanel(guiName, unitEntState, playerState)
{
if (!g_SelectionPanels[guiName])
{
error("unknown guiName used '" + guiName + "'");
return;
}
usedPanels[guiName] = 1;
var selection = g_Selection.toList();
var items = g_SelectionPanels[guiName].getItems(unitEntState, selection);
if (!items || !items.length)
return;
var numberOfItems = items.length;
var selection = g_Selection.toList();
var garrisonGroups = new EntityGroups();
// Determine how many buttons there should be
@ -193,26 +200,8 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
else
var rowLength = 8;
// TODO get this out of here
// Common code for garrison and 'unload all' button counts.
if (guiName == GARRISON || guiName == COMMAND)
{
for (var i = 0; i < selection.length; ++i)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
garrisonGroups.add(state.garrisonHolder.entities)
}
}
// Resize Queue panel if needed
if (guiName == QUEUE) // or garrison
{
var numRows = Math.ceil(numberOfItems / rowLength);
var panel = Engine.GetGUIObjectByName("unitQueuePanel");
var size = panel.size;
size.top = (UNIT_PANEL_BASE - ((numRows-1)*UNIT_PANEL_HEIGHT));
panel.size = size;
}
if (g_SelectionPanels[guiName].resizePanel)
g_SelectionPanels[guiName].resizePanel(numberOfItems, rowLength);
// Make buttons
for (var i = 0; i < numberOfItems; i++)
@ -220,9 +209,10 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
var item = items[i];
// If a tech has been researched it leaves an empty slot
if (!item && g_SelectionPanels[guiName].hideItem)
if (!item)
{
g_SelectionPanels[guiName].hideItem(i, rowLength);
if (g_SelectionPanels[guiName].hideItem)
g_SelectionPanels[guiName].hideItem(i, rowLength);
continue;
}
@ -234,14 +224,10 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
"selection": selection,
"playerState": playerState,
"unitEntState": unitEntState,
"callback": callback,
"rowLength": rowLength,
"numberOfItems": numberOfItems,
};
if (garrisonGroups)
data.garrisonGroups = garrisonGroups;
// add standard gui objects to the data
// depending on the actual XML, some of this may be undefined
data.button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
@ -252,15 +238,13 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
// DEFAULTS
data.button.hidden = false;
data.button.enabled = true;
data.button.tooltip = "";
data.button.caption = "";
// Items can have a callback element that overrides the normal
// caller-supplied callback function. Button Function
// (need nested functions to get the closure right)
data.button.onpress = (function(e){ return function() { e.callback ? e.callback(e) : callback(e) } })(item);
if (data.button)
{
data.button.hidden = false;
data.button.enabled = true;
data.button.tooltip = "";
data.button.caption = "";
}
if (data.affordableMask)
data.affordableMask.hidden = true; // actually used for the red "lack of resource" overlay, and darkening if unavailable. Sort of a hack.
@ -287,7 +271,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
setPanelObjectPosition(data.button, i, rowLength);
// TODO: we should require all entities to have icons, so this case never occurs
if (!data.icon.sprite)
if (data.icon && !data.icon.sprite)
data.icon.sprite = "bkFillBlack";
}
@ -301,6 +285,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
// remember the number of items
g_unitPanelButtons[guiName] = numberOfItems;
g_SelectionPanels[guiName].used = true;
}
function resourcesToAlphaMask(neededResources)
@ -313,83 +298,6 @@ function resourcesToAlphaMask(neededResources)
return "colour: 255 0 0 " + (alpha);
}
// Sets up "unit barter panel" - special case for setupUnitPanel
function setupUnitBarterPanel(unitEntState, playerState)
{
// Amount of player's resource to exchange
var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
if (Engine.HotkeyIsPressed("session.massbarter"))
amountToSell *= BARTER_BUNCH_MULTIPLIER;
// One pass for each resource
for (var i = 0; i < BARTER_RESOURCES.length; i++)
{
var resource = BARTER_RESOURCES[i];
// One pass for 'sell' row and another for 'buy'
for (var j = 0; j < 2; j++)
{
var action = BARTER_ACTIONS[j];
if (j == 0)
{
// Display the selection overlay
var selection = Engine.GetGUIObjectByName("unitBarter" + action + "Selection["+i+"]");
selection.hidden = !(i == g_barterSell);
}
// We gray out the not selected icons in 'sell' row
var grayscale = (j == 0 && i != g_barterSell) ? "grayscale:" : "";
var icon = Engine.GetGUIObjectByName("unitBarter" + action + "Icon["+i+"]");
var button = Engine.GetGUIObjectByName("unitBarter" + action + "Button["+i+"]");
button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46";
var amountToBuy;
// We don't display a button in 'buy' row if the same resource is selected in 'sell' row
if (j == 1 && i == g_barterSell)
{
button.hidden = true;
}
else
{
button.hidden = false;
button.tooltip = action + " " + resource;
icon.sprite = "stretched:"+grayscale+"session/icons/resources/" + resource + ".png";
var sellPrice = unitEntState.barterMarket.prices["sell"][BARTER_RESOURCES[g_barterSell]];
var buyPrice = unitEntState.barterMarket.prices["buy"][resource];
amountToBuy = "+" + Math.round(sellPrice / buyPrice * amountToSell);
}
var amount;
if (j == 0)
{
button.onpress = (function(i){ return function() { g_barterSell = i; } })(i);
if (i == g_barterSell)
{
amount = "-" + amountToSell;
var neededRes = {};
neededRes[resource] = amountToSell;
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", neededRes);
var hidden = neededResources ? false : true;
for (var ii = 0; ii < BARTER_RESOURCES.length; ii++)
{
var affordableMask = Engine.GetGUIObjectByName("unitBarterBuyUnaffordable["+ii+"]");
affordableMask.hidden = hidden;
}
}
else
amount = "";
}
else
{
var exchangeResourcesParameters = { "sell": BARTER_RESOURCES[g_barterSell], "buy": BARTER_RESOURCES[i], "amount": amountToSell };
button.onpress = (function(exchangeResourcesParameters){ return function() { exchangeResources(exchangeResourcesParameters); } })(exchangeResourcesParameters);
amount = amountToBuy;
}
Engine.GetGUIObjectByName("unitBarter" + action + "Amount["+i+"]").caption = amount;
}
}
}
/**
* Updates the right hand side "Unit Commands" panel. Runs in the main session loop via updateSelectionDetails().
* Delegates to setupUnitPanel to set up individual subpanels, appropriately activated depending on the selected
@ -402,8 +310,8 @@ function setupUnitBarterPanel(unitEntState, playerState)
*/
function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection)
{
// Panels that are active
var usedPanels = {};
for each (var panel in g_SelectionPanels)
panel.used = false;
// If the selection is friendly units, add the command panels
var player = Engine.GetPlayerID();
@ -415,218 +323,29 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
if (entState.player == player || g_DevSettings.controlAll)
{
if (selection.length > 1)
setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
function (entType, rightPressed) { changePrimarySelectionGroup(entType, rightPressed); } );
var commands = getEntityCommandsList(entState);
if (commands.length)
setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
function (item) { performCommand(entState.id, item.name); } );
if (entState.garrisonHolder)
for (var guiName of g_PanelsOrder)
{
var groups = new EntityGroups();
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
groups.add(state.garrisonHolder.entities)
}
if (
g_SelectionPanels[guiName].conflictsWith &&
g_SelectionPanels[guiName].conflictsWith.some(function (p) { return g_SelectionPanels[p].used; })
)
continue;
setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
function (item) { unloadTemplate(item); } );
setupUnitPanel(guiName, entState, playerState);
}
var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
{
setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
function (item) { performFormation(entState.id, item); } );
}
// TODO: probably should load the stance list from a data file,
// and/or vary depending on what units are selected
var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && stances.length)
{
setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
function (item) { performStance(entState.id, item); } );
}
Engine.GetGUIObjectByName("unitBarterPanel").hidden = !entState.barterMarket;
if (entState.barterMarket)
{
usedPanels["Barter"] = 1;
setupUnitBarterPanel(entState, playerState);
}
var buildableEnts = getAllBuildableEntitiesFromSelection();
var trainableEnts = getAllTrainableEntitiesFromSelection();
// Whether the GUI's right panel has been filled.
var rightUsed = true;
// The first selected entity's type has priority.
if (entState.buildEntities)
setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts,
function (trainEntType) { startBuildingPlacement(trainEntType, playerState); } );
else if (entState.production && entState.production.entities)
setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
else if (!entState.foundation && entState.gate || hasClass(entState, "LongWall"))
{
// Allow long wall pieces to be converted to gates
var longWallTypes = {};
var walls = [];
var gates = [];
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
{
var gateTemplate = getWallGateTemplate(state.id);
if (gateTemplate)
{
var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
if (!tooltipString)
{
warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
tooltipString = "";
}
walls.push({
"tooltip": translate(tooltipString),
"template": gateTemplate,
"callback": function (item) { transformWallToGate(item.template); }
});
}
// We only need one entity per type.
longWallTypes[state.template] = true;
}
else if (state.gate && !gates.length)
{
gates.push({
"gate": state.gate,
"tooltip": translate("Lock Gate"),
"locked": true,
"callback": function (item) { lockGate(item.locked); }
});
gates.push({
"gate": state.gate,
"tooltip": translate("Unlock Gate"),
"locked": false,
"callback": function (item) { lockGate(item.locked); }
});
}
// Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
else if (state.gate && state.gate.locked != gates[0].gate.locked)
for (var j = 0; j < gates.length; ++j)
delete gates[j].gate.locked;
}
// Place wall conversion options after gate lock/unlock icons.
var items = gates.concat(walls);
if (items.length)
setupUnitPanel(GATE, usedPanels, entState, playerState, items);
else
rightUsed = false;
}
else if (entState.pack)
{
var items = [];
var packButton = false;
var unpackButton = false;
var packCancelButton = false;
var unpackCancelButton = false;
for (var i in selection)
{
// Find un/packable entities
var state = GetEntityState(selection[i]);
if (state.pack)
{
if (state.pack.progress == 0)
{
if (!state.pack.packed)
packButton = true;
else if (state.pack.packed)
unpackButton = true;
}
else
{
// Already un/packing - show cancel button
if (!state.pack.packed)
packCancelButton = true;
else if (state.pack.packed)
unpackCancelButton = true;
}
}
}
if (packButton)
items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
if (unpackButton)
items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } });
if (packCancelButton)
items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } });
if (unpackCancelButton)
items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
if (items.length)
setupUnitPanel(PACK, usedPanels, entState, playerState, items);
else
rightUsed = false;
}
else
rightUsed = false;
if (!rightUsed)
{
// The right pane is empty. Fill the pane with a sane type.
// Prefer buildables for units and trainables for structures.
if (buildableEnts.length && (hasClass(entState, "Unit") || !trainableEnts.length))
setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts,
function (trainEntType) { startBuildingPlacement(trainEntType, playerState); });
else if (trainableEnts.length)
setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
}
// Show technologies if the active panel has at most one row of icons.
if (entState.production && entState.production.technologies.length)
{
var activepane = usedPanels[CONSTRUCTION] ? buildableEnts.length : trainableEnts.length;
if (selection.length == 1 || activepane <= 8)
setupUnitPanel(RESEARCH, usedPanels, entState, playerState, entState.production.technologies,
function (researchType) { addResearchToQueue(entState.id, researchType); } );
}
if (entState.production && entState.production.queue.length)
setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
function (item) { removeFromProductionQueue(entState.id, item.id); } );
supplementalDetailsPanel.hidden = false;
commandsPanel.hidden = false;
}
else if (playerState.isMutualAlly[entState.player]) // owned by allied player
{
if (entState.garrisonHolder)
{
var groups = new EntityGroups();
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
groups.add(state.garrisonHolder.entities)
}
setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
function (item) { unloadTemplate(item); } );
// TODO if there's a second panel needed for a different player
// we should consider adding the players list to g_SelectionPanels
setupUnitPanel(GARRISON, entState, playerState);
if (g_SelectionPanels["Garrison"].used)
supplementalDetailsPanel.hidden = false;
}
else
{
supplementalDetailsPanel.hidden = true;
}
commandsPanel.hidden = true;
}
@ -637,21 +356,14 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
}
// Hides / unhides Unit Panels (panels should be grouped by type, not by order, but we will leave that for another time)
var offset = 0;
for each (var panelName in g_unitPanels)
{
var panel = Engine.GetGUIObjectByName("unit" + panelName + "Panel");
if (usedPanels[panelName])
panel.hidden = false;
else
panel.hidden = true;
}
for (var panelName in g_SelectionPanels)
Engine.GetGUIObjectByName("unit" + panelName + "Panel").hidden = !g_SelectionPanels[panelName].used;
}
// Force hide commands panels
function hideUnitCommands()
{
for each (var panelName in g_unitPanels)
for (var panelName in g_SelectionPanels)
Engine.GetGUIObjectByName("unit" + panelName + "Panel").hidden = true;
}
@ -730,4 +442,18 @@ function getVisibleEntityClassesFormatted(template)
r += "[/font]";
}
return r;
};
}
function getNumberOfRightPanelButtons()
{
var sum = 0;
if (g_SelectionPanels["Construction"].used)
sum += g_unitPanelButtons["Construction"];
if (g_SelectionPanels["Training"].used)
sum += g_unitPanelButtons["Training"];
if (g_SelectionPanels["Pack"].used)
sum += g_unitPanelButtons["Pack"];
if (g_SelectionPanels["Gate"].used)
sum += g_unitPanelButtons["Gate"];
return sum;
}

View File

@ -288,6 +288,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"hasWorkOrders": cmpUnitAI.HasWorkOrders(),
"canGuard": cmpUnitAI.CanGuard(),
"isGuarding": cmpUnitAI.IsGuardOf(),
"possibleStances": cmpUnitAI.GetPossibleStances(),
};
// Add some information needed for ungarrisoning
if (cmpUnitAI.IsGarrisoned() && ret.player !== undefined)

View File

@ -5439,6 +5439,11 @@ UnitAI.prototype.GetStance = function()
return g_Stances[this.stance];
};
UnitAI.prototype.GetPossibleStances = function()
{
return Object.keys(g_Stances);
};
UnitAI.prototype.GetStanceName = function()
{
return this.stance;