var g_DrawLimits = {}; // GUI limits. Populated by predraw()
/**
* 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 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 s = 0;
let y = 0;
for (let stru of g_CivData[g_SelectedCiv].buildList[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 = assembleTooltip(stru);
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_name").caption = translate(stru.name.specific);
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.wallset && prod_pha == pha)
{
for (let prod of [stru.wallset.gate, stru.wallset.tower])
{
if (!drawProdIcon(i, s, r, p, prod))
break;
p++;
}
}
if (stru.production.technology[prod_pha])
{
for (let prod of stru.production.technology[prod_pha])
{
prod = (depath(prod).slice(0,5) == "phase") ? g_ParsedData.phases[prod] : g_ParsedData.techs[prod];
if (!drawProdIcon(i, s, r, p, prod))
break;
p++;
}
}
rowCounts[r] = p;
if (p>c)
c = p;
hideRemaining("phase["+i+"]_struct["+s+"]_row["+r+"]_prod[", 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+"]_row[", r, "]");
++s;
}
hideRemaining("phase["+i+"]_struct[", s, "]");
++i;
}
let t = 0;
for (let trainer of g_CivData[g_SelectedCiv].trainList)
{
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 = assembleTooltip(trainer);
Engine.GetGUIObjectByName("trainer["+t+"]_name").caption = translate(trainer.name.specific);
thisEle.hidden = false;
let p = 0;
for (let prod of trainer.trainer)
{
prod = g_ParsedData.units[prod];
if (!drawProdIcon(null, t, null, p, prod))
break;
p++;
}
hideRemaining("trainer["+t+"]_prod[", p, "]");
let size = thisEle.size;
size.right = size.left + ((p*24 < defWidth)?defWidth:p*24)+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("trainer[", 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;
}
function drawProdIcon(pha, s, r, p, prod)
{
let prodEle = Engine.GetGUIObjectByName("phase["+pha+"]_struct["+s+"]_row["+r+"]_prod["+p+"]");
if (pha === null)
prodEle = Engine.GetGUIObjectByName("trainer["+s+"]_prod["+p+"]");
if (prodEle === undefined)
{
error("The "+(pha === 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/"+prod.icon;
prodEle.tooltip = assembleTooltip(prod);
prodEle.hidden = false;
return true;
}
/**
* Calculate row position offset (accounting for different number of prod rows per phase).
*/
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;
}
function hideRemaining(prefix, idx, suffix)
{
let obj = Engine.GetGUIObjectByName(prefix+idx+suffix);
while (obj)
{
obj.hidden = true;
++idx;
obj = Engine.GetGUIObjectByName(prefix+idx+suffix);
}
}
/**
* Positions certain elements that only need to be positioned once
* (as <repeat> does not reposition 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.sprite = "stretched:session/portraits/"+g_ParsedData.phases[pha].icon;
phaseIcon.size = "16 32+"+offset+" 48+16 48+32+"+offset;
// Position prod bars
let j = 1;
for (; j < phaseCount - i; ++j)
{
let prodBar = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]");
prodBar.size = "40 1+"+(24*j)+"+98+"+offset+" 100%-8 1+"+(24*j)+"+98+"+offset+"+22";
// Set phase icon
let prodBarIcon = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]_icon");
prodBarIcon.sprite = "stretched:session/portraits/"+g_ParsedData.phases[phaseList[i+j]].icon;
}
// Hide remaining prod bars
hideRemaining("phase["+i+"]_bar[", 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 j = phaseCount - i; j < phaseCount; ++j)
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+j+"]").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[", i, "]");
hideRemaining("phase[", i, "]_bar");
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;
}
/**
* Assemble a tooltip text
*
* @param template Information about a Unit, a Structure or a Technology
*
* @return The tooltip text, formatted.
*/
function assembleTooltip(template)
{
let txt = getEntityNamesFormatted(template);
txt += '\n' + getEntityCostTooltip(template, 1);
if (template.tooltip)
txt += '\n' + g_TooltipTextFormats.body[0] + translate(template.tooltip) + g_TooltipTextFormats.body[1];
if (template.auras)
txt += getAurasTooltip(template);
if (template.health)
txt += '\n' + sprintf(translate("%(label)s %(details)s"), {
"label": g_TooltipTextFormats.header[0] + translate("Health:") + g_TooltipTextFormats.header[1],
"details": template.health
});
if (template.healer)
txt += '\n' + getHealerTooltip(template);
if (template.attack)
txt += '\n' + getAttackTooltip(template);
if (template.armour)
txt += '\n' + getArmorTooltip(template.armour);
if (template.speed)
txt += '\n' + getSpeedTooltip(template);
if (template.gather)
{
let rates = [];
for (let type in template.gather)
rates.push(sprintf(translate("%(resourceIcon)s %(rate)s"), {
"resourceIcon": getCostComponentDisplayName(type),
"rate": template.gather[type]
}));
txt += '\n' + sprintf(translate("%(label)s %(details)s"), {
"label": g_TooltipTextFormats.header[0] + translate("Gather Rates:") + g_TooltipTextFormats.header[1],
"details": rates.join(" ")
});
}
txt += getPopulationBonusTooltip(template);
return txt;
}