mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 14:23:56 -07:00
Right-click on icons of units, structures, technologies, flora, fauna, etc. and view additional details. Reviewed By: elexis Fixes: #3212 Differential Revision: https://code.wildfiregames.com/D297 This was SVN commit r21310.
432 lines
12 KiB
JavaScript
432 lines
12 KiB
JavaScript
/**
|
|
* Functions used to collate the contents of a tooltip.
|
|
*/
|
|
var g_StructreeTooltipFunctions = [
|
|
getEntityNamesFormatted,
|
|
getEntityCostTooltip,
|
|
getEntityTooltip,
|
|
getAurasTooltip
|
|
].concat(g_StatsFunctions);
|
|
|
|
/**
|
|
* Draw the structree
|
|
*
|
|
* (Actually resizes and changes visibility of elements, and populates text)
|
|
*/
|
|
function draw()
|
|
{
|
|
// Set basic state (positioning of elements mainly), but only once
|
|
if (!Object.keys(g_DrawLimits).length)
|
|
predraw();
|
|
|
|
let leftMargin = Engine.GetGUIObjectByName("tree_display").size.left;
|
|
let defWidth = 96;
|
|
let defMargin = 4;
|
|
|
|
let phaseList = g_ParsedData.phaseList;
|
|
|
|
Engine.GetGUIObjectByName("civEmblem").sprite = "stretched:" + g_CivData[g_SelectedCiv].Emblem;
|
|
Engine.GetGUIObjectByName("civName").caption = g_CivData[g_SelectedCiv].Name;
|
|
Engine.GetGUIObjectByName("civHistory").caption = g_CivData[g_SelectedCiv].History;
|
|
|
|
let i = 0;
|
|
for (let pha of phaseList)
|
|
{
|
|
let prodBarWidth = 0;
|
|
let s = 0;
|
|
let y = 0;
|
|
|
|
for (let stru of g_BuildList[g_SelectedCiv][pha])
|
|
{
|
|
let thisEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
|
|
if (thisEle === undefined)
|
|
{
|
|
error("\""+g_SelectedCiv+"\" has more structures in phase " +
|
|
pha + " than can be supported by the current GUI layout");
|
|
break;
|
|
}
|
|
|
|
let c = 0;
|
|
let rowCounts = [];
|
|
stru = g_ParsedData.structures[stru];
|
|
|
|
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_icon").sprite =
|
|
"stretched:session/portraits/"+stru.icon;
|
|
|
|
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_icon").tooltip =
|
|
compileTooltip(stru);
|
|
|
|
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_name").caption =
|
|
translate(stru.name.specific);
|
|
|
|
setViewerOnPress("phase["+i+"]_struct["+s+"]_icon", stru.name.internal);
|
|
thisEle.hidden = false;
|
|
|
|
for (let r in g_DrawLimits[pha].prodQuant)
|
|
{
|
|
let p = 0;
|
|
r = +r; // force int
|
|
let prod_pha = phaseList[phaseList.indexOf(pha) + r];
|
|
|
|
if (stru.production.units[prod_pha])
|
|
for (let prod of stru.production.units[prod_pha])
|
|
{
|
|
prod = g_ParsedData.units[prod];
|
|
if (!drawProdIcon(i, s, r, p, prod))
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
if (stru.upgrades[prod_pha])
|
|
for (let upgrade of stru.upgrades[prod_pha])
|
|
{
|
|
if (!drawProdIcon(i, s, r, p, upgrade))
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
if (stru.wallset && prod_pha == pha)
|
|
{
|
|
if (!drawProdIcon(i, s, r, p, stru.wallset.tower))
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
if (stru.production.techs[prod_pha])
|
|
for (let prod of stru.production.techs[prod_pha])
|
|
{
|
|
prod = clone(basename(prod).startsWith("phase") ?
|
|
g_ParsedData.phases[prod] :
|
|
g_ParsedData.techs[g_SelectedCiv][prod]);
|
|
|
|
for (let res in stru.techCostMultiplier)
|
|
if (prod.cost[res])
|
|
prod.cost[res] *= stru.techCostMultiplier[res];
|
|
|
|
if (!drawProdIcon(i, s, r, p, prod))
|
|
break;
|
|
|
|
++p;
|
|
}
|
|
|
|
rowCounts[r] = p;
|
|
|
|
if (p>c)
|
|
c = p;
|
|
|
|
hideRemaining("phase["+i+"]_struct["+s+"]_row["+r+"]", p);
|
|
}
|
|
|
|
let size = thisEle.size;
|
|
size.left = y;
|
|
size.right = size.left + ((c*24 < defWidth) ? defWidth : c*24) + 4;
|
|
y = size.right + defMargin;
|
|
thisEle.size = size;
|
|
|
|
let eleWidth = size.right - size.left;
|
|
let r;
|
|
for (r in rowCounts)
|
|
{
|
|
let wid = rowCounts[r] * 24 - 4;
|
|
let phaEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]");
|
|
size = phaEle.size;
|
|
size.left = (eleWidth - wid)/2;
|
|
phaEle.size = size;
|
|
}
|
|
++r;
|
|
hideRemaining("phase["+i+"]_struct["+s+"]_rows", r);
|
|
++s;
|
|
prodBarWidth += eleWidth + defMargin;
|
|
}
|
|
|
|
hideRemaining("phase["+i+"]", s);
|
|
|
|
// Resize phase bars
|
|
for (let j = 1; j < phaseList.length - i; ++j)
|
|
{
|
|
let prodBar = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]");
|
|
let prodBarSize = prodBar.size;
|
|
prodBarSize.right = leftMargin + prodBarWidth;
|
|
prodBar.size = prodBarSize;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
let t = 0;
|
|
for (let trainer of g_TrainList[g_SelectedCiv])
|
|
{
|
|
let thisEle = Engine.GetGUIObjectByName("trainer["+t+"]");
|
|
if (thisEle === undefined)
|
|
{
|
|
error("\""+g_SelectedCiv+"\" has more unit trainers than can be supported by the current GUI layout");
|
|
break;
|
|
}
|
|
|
|
trainer = g_ParsedData.units[trainer];
|
|
Engine.GetGUIObjectByName("trainer["+t+"]_icon").sprite = "stretched:session/portraits/"+trainer.icon;
|
|
Engine.GetGUIObjectByName("trainer["+t+"]_icon").tooltip = compileTooltip(trainer);
|
|
Engine.GetGUIObjectByName("trainer["+t+"]_name").caption = translate(trainer.name.specific);
|
|
setViewerOnPress("trainer["+t+"]_icon", trainer.name.internal);
|
|
thisEle.hidden = false;
|
|
|
|
let p = 0;
|
|
if (trainer.production)
|
|
for (let prodType in trainer.production)
|
|
for (let prod of trainer.production[prodType])
|
|
{
|
|
switch (prodType)
|
|
{
|
|
case "units":
|
|
prod = g_ParsedData.units[prod];
|
|
break;
|
|
case "techs":
|
|
prod = clone(g_ParsedData.techs[g_SelectedCiv][prod]);
|
|
for (let res in trainer.techCostMultiplier)
|
|
if (prod.cost[res])
|
|
prod.cost[res] *= trainer.techCostMultiplier[res];
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (!drawProdIcon(null, t, null, p, prod))
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
if (trainer.upgrades)
|
|
for (let upgrade of trainer.upgrades)
|
|
{
|
|
if (!drawProdIcon(null, t, null, p, upgrade))
|
|
break;
|
|
++p;
|
|
}
|
|
|
|
hideRemaining("trainer["+t+"]_row", p);
|
|
|
|
let size = thisEle.size;
|
|
size.right = size.left + Math.max(p*24, defWidth) + 4;
|
|
thisEle.size = size;
|
|
|
|
let eleWidth = size.right - size.left;
|
|
let wid = p * 24 - 4;
|
|
let phaEle = Engine.GetGUIObjectByName("trainer["+t+"]_row");
|
|
size = phaEle.size;
|
|
size.left = (eleWidth - wid)/2;
|
|
phaEle.size = size;
|
|
++t;
|
|
}
|
|
hideRemaining("trainers", t);
|
|
|
|
let size = Engine.GetGUIObjectByName("display_tree").size;
|
|
size.right = t > 0 ? -124 : -4;
|
|
Engine.GetGUIObjectByName("display_tree").size = size;
|
|
|
|
Engine.GetGUIObjectByName("display_trainers").hidden = t === 0;
|
|
}
|
|
|
|
/**
|
|
* Draws production icons.
|
|
*
|
|
* These are the small icons on the gray bars.
|
|
*
|
|
* @param {string} phase - The phase that the parent entity (the entity that
|
|
* builds/trains/researches this entity) belongs to.
|
|
* @param {number} parentID - Which parent entity it belongs to on the main phase rows.
|
|
* @param {number} rowID - Which production row of the parent entity the production
|
|
* icon sits on, if applicable.
|
|
* @param {number} iconID - Which production icon to affect.
|
|
* @param {object} template - The entity being produced.
|
|
* @return {boolean} True is successfully drawn, False if no space left to draw.
|
|
*/
|
|
function drawProdIcon(phase, parentID, rowID, iconID, template)
|
|
{
|
|
let prodEle = Engine.GetGUIObjectByName("phase["+phase+"]_struct["+parentID+"]_row["+rowID+"]_prod["+iconID+"]");
|
|
|
|
if (phase === null)
|
|
prodEle = Engine.GetGUIObjectByName("trainer["+parentID+"]_prod["+iconID+"]");
|
|
|
|
if (prodEle === undefined)
|
|
{
|
|
error("The " + (phase === null ? "trainer units" : "structures") + " of \"" + g_SelectedCiv +
|
|
"\" have more production icons than can be supported by the current GUI layout");
|
|
return false;
|
|
}
|
|
|
|
prodEle.sprite = "stretched:session/portraits/"+template.icon;
|
|
prodEle.tooltip = compileTooltip(template);
|
|
prodEle.hidden = false;
|
|
setViewerOnPress(prodEle.name, template.name.internal);
|
|
return true;
|
|
}
|
|
|
|
function compileTooltip(template)
|
|
{
|
|
return buildText(template, g_StructreeTooltipFunctions) + "\n" + showTemplateViewerOnClickTooltip();
|
|
}
|
|
|
|
/**
|
|
* Calculate row position offset (accounting for different number of prod rows per phase).
|
|
*
|
|
* @param {number} idx
|
|
* @return {number}
|
|
*/
|
|
function getPositionOffset(idx)
|
|
{
|
|
let phases = g_ParsedData.phaseList.length;
|
|
|
|
let size = 92*idx; // text, image and offset
|
|
size += 24 * (phases*idx - (idx-1)*idx/2); // phase rows (phase-currphase+1 per row)
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Positions certain elements that only need to be positioned once
|
|
* (as <repeat> does not position automatically).
|
|
*
|
|
* Also detects limits on what the GUI can display by iterating through the set
|
|
* elements of the GUI. These limits are then used by draw().
|
|
*/
|
|
function predraw()
|
|
{
|
|
let phaseList = g_ParsedData.phaseList;
|
|
let initIconSize = Engine.GetGUIObjectByName("phase[0]_struct[0]_row[0]_prod[0]").size;
|
|
|
|
let phaseCount = phaseList.length;
|
|
let i = 0;
|
|
for (let pha of phaseList)
|
|
{
|
|
let offset = getPositionOffset(i);
|
|
// Align the phase row
|
|
Engine.GetGUIObjectByName("phase["+i+"]").size = "8 16+" + offset + " 100% 100%";
|
|
|
|
// Set phase icon
|
|
let phaseIcon = Engine.GetGUIObjectByName("phase["+i+"]_phase");
|
|
phaseIcon.size = "16 32+"+offset+" 48+16 48+32+"+offset;
|
|
|
|
// Set initial prod bar size
|
|
let j = 1;
|
|
for (; j < phaseList.length - i; ++j)
|
|
{
|
|
let prodBar = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]");
|
|
prodBar.size = "40 1+"+(24*j)+"+98+"+offset+" 0 1+"+(24*j)+"+98+"+offset+"+22";
|
|
}
|
|
// Hide remaining prod bars
|
|
hideRemaining("phase["+i+"]_bars", j-1);
|
|
|
|
let s = 0;
|
|
let ele = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
|
|
g_DrawLimits[pha] = {
|
|
"structQuant": 0,
|
|
"prodQuant": []
|
|
};
|
|
|
|
do
|
|
{
|
|
// Position production icons
|
|
for (let r in phaseList.slice(phaseList.indexOf(pha)))
|
|
{
|
|
let p=1;
|
|
let prodEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]_prod["+p+"]");
|
|
|
|
do
|
|
{
|
|
let prodsize = prodEle.size;
|
|
prodsize.left = (initIconSize.right+4) * p;
|
|
prodsize.right = (initIconSize.right+4) * (p+1) - 4;
|
|
prodEle.size = prodsize;
|
|
|
|
p++;
|
|
prodEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]_prod["+p+"]");
|
|
} while (prodEle !== undefined);
|
|
|
|
// Set quantity of productions in this row
|
|
g_DrawLimits[pha].prodQuant[r] = p;
|
|
|
|
// Position the prod row
|
|
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]").size = "4 100%-"+24*(phaseCount - i - r)+" 100%-4 100%";
|
|
}
|
|
|
|
// Hide unused struct rows
|
|
for (let r = phaseCount - i; r < phaseCount; ++r)
|
|
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]").hidden = true;
|
|
|
|
let size = ele.size;
|
|
size.bottom += Object.keys(g_DrawLimits[pha].prodQuant).length*24;
|
|
ele.size = size;
|
|
|
|
s++;
|
|
ele = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
|
|
} while (ele !== undefined);
|
|
|
|
// Set quantity of structures in each phase
|
|
g_DrawLimits[pha].structQuant = s;
|
|
++i;
|
|
}
|
|
hideRemaining("phase_rows", i);
|
|
hideRemaining("phase_ident", i);
|
|
|
|
let t = 0;
|
|
let ele = Engine.GetGUIObjectByName("trainer["+t+"]");
|
|
g_DrawLimits.trainer = {
|
|
"trainerQuant": 0,
|
|
"prodQuant": 0
|
|
};
|
|
|
|
let x = 4;
|
|
do
|
|
{
|
|
let p = 0;
|
|
let prodEle = Engine.GetGUIObjectByName("trainer["+t+"]_prod["+p+"]");
|
|
do
|
|
{
|
|
let prodsize = prodEle.size;
|
|
prodsize.left = (initIconSize.right+4) * p;
|
|
prodsize.right = (initIconSize.right+4) * (p+1) - 4;
|
|
prodEle.size = prodsize;
|
|
|
|
p++;
|
|
prodEle = Engine.GetGUIObjectByName("trainer["+t+"]_prod["+p+"]");
|
|
} while (prodEle !== undefined);
|
|
Engine.GetGUIObjectByName("trainer["+t+"]_row").size = "4 100%-24 100%-4 100%";
|
|
g_DrawLimits.trainer.prodQuant = p;
|
|
|
|
let size = ele.size;
|
|
size.top += x;
|
|
size.bottom += x + 24;
|
|
x += size.bottom - size.top + 8;
|
|
ele.size = size;
|
|
|
|
t++;
|
|
ele = Engine.GetGUIObjectByName("trainer["+t+"]");
|
|
|
|
} while (ele !== undefined);
|
|
|
|
g_DrawLimits.trainer.trainerQuant = t;
|
|
}
|
|
|
|
function drawPhaseIcons()
|
|
{
|
|
for (let i = 0; i < g_ParsedData.phaseList.length; ++i)
|
|
{
|
|
drawPhaseIcon("phase["+i+"]_phase", i);
|
|
|
|
for (let j = 1; j < g_ParsedData.phaseList.length - i; ++j)
|
|
drawPhaseIcon("phase["+i+"]_bar["+(j-1)+"]_icon", j+i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} guiObjectName
|
|
* @param {number} phaseIndex
|
|
*/
|
|
function drawPhaseIcon(guiObjectName, phaseIndex)
|
|
{
|
|
let phaseName = g_ParsedData.phaseList[phaseIndex];
|
|
let prodPhaseTemplate = g_ParsedData.phases[phaseName + "_" + g_SelectedCiv] || g_ParsedData.phases[phaseName];
|
|
|
|
let phaseIcon = Engine.GetGUIObjectByName(guiObjectName);
|
|
phaseIcon.sprite = "stretched:session/portraits/" + prodPhaseTemplate.icon;
|
|
phaseIcon.tooltip = getEntityNamesFormatted(prodPhaseTemplate);
|
|
}
|