Ticket #3904: tooltip.5.diff
File tooltip.5.diff, 64.0 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
function MatchesClassList(classes, match 74 74 */ 75 75 function GetTemplateDataHelper(template, player, auraTemplates) 76 76 { 77 77 var ret = {}; 78 78 79 // TODO: the second argument of func can be derived from the first 79 80 var func; 80 81 if (player) 81 82 func = ApplyValueModificationsToTemplate; 82 83 else 83 84 func = function(a, val, c, d) { return val; } 84 85 85 86 if (template.Armour) 86 {87 87 ret.armour = { 88 88 "hack": func("Armour/Hack", +template.Armour.Hack, player, template), 89 89 "pierce": func("Armour/Pierce", +template.Armour.Pierce, player, template), 90 90 "crush": func("Armour/Crush", +template.Armour.Crush, player, template), 91 91 }; 92 }93 92 94 93 if (template.Attack) 95 94 { 96 95 let getAttackStat = function(type, stat) 97 96 { … … function GetTemplateDataHelper(template, 130 129 "description": aura.auraDescription || null 131 130 }; 132 131 } 133 132 } 134 133 134 if (template.BuildingAI) 135 { 136 ret.buildingAI = {}; 137 if (template.BuildingAI.DefaultArrowCount) 138 ret.buildingAI.defaultArrowCount = func("BuildingAI/DefaultArrowCount", +template.BuildingAI.DefaultArrowCount, player, template); 139 if (template.BuildingAI.GarrisonArrowMultiplier) 140 ret.buildingAI.garrisonArrowMultiplier = func("BuildingAI/GarrisonArrowMultiplier", +template.BuildingAI.GarrisonArrowMultiplier, player, template); 141 if (template.BuildingAI.MaxArrowCount) 142 ret.buildingAI.maxArrowCount = func("BuildingAI/MaxArrowCount", +template.BuildingAI.MaxArrowCount, player, template); 143 } 144 135 145 if (template.BuildRestrictions) 136 146 { 137 147 // required properties 138 148 ret.buildRestrictions = { 139 149 "placementType": template.BuildRestrictions.PlacementType, … … function GetTemplateDataHelper(template, 181 191 ret.footprint.circle = {"radius": +template.Footprint.Circle["@radius"]}; 182 192 else 183 193 warn("GetTemplateDataHelper(): Unrecognized Footprint type"); 184 194 } 185 195 196 if (template.GarrisonHolder) 197 { 198 ret.garrisonHolder = {}; 199 if (template.GarrisonHolder.Max) 200 ret.garrisonHolder.max = func("GarrisonHolder/Max", +template.GarrisonHolder.Max, player, template); 201 } 202 186 203 if (template.Obstruction) 187 204 { 188 205 ret.obstruction = { 189 206 "active": ("" + template.Obstruction.Active == "true"), 190 207 "blockMovement": ("" + template.Obstruction.BlockMovement == "true"), -
binaries/data/mods/public/gui/common/tooltips.js
const g_CostDisplayIcons = { 8 8 }; 9 9 10 10 const g_TooltipTextFormats = { 11 11 "unit": ['[font="sans-10"][color="orange"]', '[/color][/font]'], 12 12 "header": ['[font="sans-bold-13"]', '[/font]'], 13 "body": ['[font="sans-13"]', '[/font]'] 13 "body": ['[font="sans-13"]', '[/font]'], 14 "comma": ['[font="sans-12"]', '[/font]'] 14 15 }; 15 16 17 const g_AttackTypes = { 18 "Charge": translate("Charge Attack:"), 19 "Melee": translate("Melee Attack:"), 20 "Ranged": translate("Ranged Attack:"), 21 "Capture": translate("Capture Attack:") 22 }; 23 24 const g_DamageTypes = { 25 "hack": translate("Hack"), 26 "pierce": translate("Pierce"), 27 "crush": translate("Crush"), 28 }; 29 30 function bodyFont(text) 31 { 32 return g_TooltipTextFormats.body[0] + text + g_TooltipTextFormats.body[1]; 33 } 34 35 function headerFont(text) 36 { 37 return g_TooltipTextFormats.header[0] + text + g_TooltipTextFormats.header[1]; 38 } 39 40 function unitFont(text) 41 { 42 return g_TooltipTextFormats.unit[0] + text + g_TooltipTextFormats.unit[1]; 43 } 44 45 function commaFont(text) 46 { 47 return g_TooltipTextFormats.comma[0] + text + g_TooltipTextFormats.comma[1]; 48 } 49 16 50 function damageValues(dmg) 17 51 { 18 52 if (!dmg) 19 53 return [0, 0, 0]; 20 54 21 55 return [dmg.hack || 0, dmg.pierce || 0, dmg.crush || 0]; 22 56 } 23 57 24 function damageTypeDetails(dmg)58 function getEntityTooltip(template) 25 59 { 26 if (!dmg) 27 return '[font="sans-12"]' + translate("(None)") + '[/font]'; 28 29 let dmgArray = []; 30 31 if (dmg.hack) 32 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 33 "damage": dmg.hack.toFixed(1), 34 "damageType": g_TooltipTextFormats.unit[0] + translate("Hack") + g_TooltipTextFormats.unit[1] 35 })); 60 if (!template.tooltip) 61 return ""; 36 62 37 if (dmg.pierce) 38 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 39 "damage": dmg.pierce.toFixed(1), 40 "damageType": g_TooltipTextFormats.unit[0] + translate("Pierce") + g_TooltipTextFormats.unit[1] 41 })); 63 return bodyFont(template.tooltip); 64 } 42 65 43 if (dmg.crush) 44 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 45 "damage": dmg.crush.toFixed(1), 46 "damageType": g_TooltipTextFormats.unit[0] + translate("Crush") + g_TooltipTextFormats.unit[1] 47 })); 66 function getHealthTooltip(template) 67 { 68 if (!template.health) 69 return ""; 48 70 49 return dmgArray.join(translate(", ")); 71 return sprintf(translate("%(label)s %(details)s"), { 72 "label": headerFont(translate("Health:")), 73 "details": template.health 74 }) 50 75 } 51 76 52 function attackRateDetails( entState, type)77 function attackRateDetails(template, type) 53 78 { 54 79 // Either one arrow shot by UnitAI, 55 let time = entState.attack[type].repeatTime / 1000;80 let time = template.attack[type].repeatTime / 1000; 56 81 let timeString = sprintf(translatePlural("%(time)s %(second)s", "%(time)s %(second)s", time), { 57 82 "time": time, 58 "second": g_TooltipTextFormats.unit[0] + translatePlural("second", "seconds", time) + g_TooltipTextFormats.unit[1]83 "second": unitFont(translatePlural("second", "seconds", time)) 59 84 }); 60 85 61 86 // or multiple arrows shot by BuildingAI 62 if (! entState.buildingAI)87 if (!template.buildingAI || type != "Ranged") 63 88 return timeString; 64 89 65 let arrows = entState.buildingAI.arrowCount; 66 let arrowString = sprintf(translatePlural("%(arrowcount)s %(arrow)s", "%(arrowcount)s %(arrow)s", arrows), { 90 // Show either current rate from simulation or default count if the sim is not running 91 let arrows = template.buildingAI.arrowCount || template.buildingAI.defaultArrowCount; 92 let arrowString = sprintf(translatePlural("%(arrowcount)s %(arrows)s", "%(arrowcount)s %(arrows)s", arrows), { 67 93 "arrowcount": arrows, 68 "arrow ": g_TooltipTextFormats.unit[0] + translatePlural("arrow", "arrows", arrows) + g_TooltipTextFormats.unit[1]94 "arrows": unitFont(translatePlural("arrow", "arrows", arrows)) 69 95 }); 70 96 71 97 return sprintf(translate("%(arrowString)s / %(timeString)s"), { 72 98 "arrowString": arrowString, 73 99 "timeString": timeString 74 100 }); 75 101 } 76 102 77 // Converts an armor level into the actual reduction percentage 103 /** 104 * Converts an armor level into the actual reduction percentage 105 */ 78 106 function armorLevelToPercentageString(level) 79 107 { 80 return (100 - Math.round(Math.pow(0.9, level) * 100)) + "%"; 81 // return sprintf(translate("%(armorPercentage)s%"), { armorPercentage: (100 - Math.round(Math.pow(0.9, level) * 100)) }); // Not supported by our sprintf implementation. 108 return sprintf(translate("%(percentage)s%%"), { 109 "percentage": (100 - Math.round(Math.pow(0.9, level) * 100)) 110 }); 82 111 } 83 112 84 function getArmorTooltip( dmg)113 function getArmorTooltip(template) 85 114 { 86 let label = g_TooltipTextFormats.header[0] + translate("Armor:") + g_TooltipTextFormats.header[1]; 87 if (!dmg) 115 // TODO showns none currently for techs 116 let label = headerFont(translate("Armor:")); 117 118 if (!template.armour) 88 119 return sprintf(translate("%(label)s %(details)s"), { 89 120 "label": label, 90 121 "details": '[font="sans-12"]' + translate("(None)") + '[/font]' 91 122 }); 92 123 93 let dmgArray = [];94 if (dmg.hack)95 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {96 "damage": dmg.hack.toFixed(1),97 "damageType": g_TooltipTextFormats.unit[0] + translate("Hack") + g_TooltipTextFormats.unit[1],98 "armorPercentage": '[font="sans-10"]' + sprintf(translate("(%(armorPercentage)s)"), { "armorPercentage": armorLevelToPercentageString(dmg.hack) }) + '[/font]'99 }));100 101 if (dmg.pierce)102 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {103 "damage": dmg.pierce.toFixed(1),104 "damageType": g_TooltipTextFormats.unit[0] + translate("Pierce") + g_TooltipTextFormats.unit[1],105 "armorPercentage": '[font="sans-10"]' + sprintf(translate("(%(armorPercentage)s)"), { "armorPercentage": armorLevelToPercentageString(dmg.pierce) }) + '[/font]'106 }));107 108 if (dmg.crush)109 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {110 "damage": dmg.crush.toFixed(1),111 "damageType": g_TooltipTextFormats.unit[0] + translate("Crush") + g_TooltipTextFormats.unit[1],112 "armorPercentage": '[font="sans-10"]' + sprintf(translate("(%(armorPercentage)s)"), { "armorPercentage": armorLevelToPercentageString(dmg.crush) }) + '[/font]'113 }));114 115 124 return sprintf(translate("%(label)s %(details)s"), { 116 125 "label": label, 117 "details": dmgArray.join('[font="sans-12"]' + translate(", ") + '[/font]') 126 "details": 127 Object.keys(g_DamageTypes).filter( 128 dmgType => template.armour[dmgType]).map( 129 dmgType => sprintf( 130 translate("%(damage)s %(damageType)s %(armorPercentage)s"), { 131 "damage": template.armour[dmgType].toFixed(1), 132 "damageType": unitFont(g_DamageTypes[dmgType]), 133 "armorPercentage": 134 '[font="sans-10"]' + 135 sprintf(translate("(%(armorPercentage)s)"), { 136 "armorPercentage": armorLevelToPercentageString(template.armour[dmgType]) 137 }) + '[/font]' 138 }) 139 ).join(commaFont(translate(", "))) 118 140 }); 119 141 } 120 142 121 143 function damageTypesToText(dmg) 122 144 { 123 145 if (!dmg) 124 146 return '[font="sans-12"]' + translate("(None)") + '[/font]'; 125 147 126 let dmgArray = []; 127 if (dmg.hack) 128 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 129 "damage": dmg.hack.toFixed(1), 130 "damageType": g_TooltipTextFormats.unit[0] + translate("Hack") + g_TooltipTextFormats.unit[1] 131 })); 132 133 if (dmg.pierce) 134 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 135 "damage": dmg.pierce.toFixed(1), 136 "damageType": g_TooltipTextFormats.unit[0] + translate("Pierce") + g_TooltipTextFormats.unit[1] 137 })); 138 139 if (dmg.crush) 140 dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), { 141 "damage": dmg.crush.toFixed(1), 142 "damageType": g_TooltipTextFormats.unit[0] + translate("Crush") + g_TooltipTextFormats.unit[1] 143 })); 144 145 return dmgArray.join('[font="sans-12"]' + translate(", ") + '[/font]'); 148 return Object.keys(g_DamageTypes).filter( 149 dmgType => dmg[dmgType]).map( 150 dmgType => sprintf(translate("%(damage)s %(damageType)s"), { 151 "damage": dmg[dmgType].toFixed(1), 152 "damageType": unitFont(g_DamageTypes[dmgType]) 153 })).join(commaFont(translate(", "))) 146 154 } 147 155 148 156 function getAttackTypeLabel(type) 149 157 { 150 if (type === "Charge") return translate("Charge Attack:"); 151 if (type === "Melee") return translate("Melee Attack:"); 152 if (type === "Ranged") return translate("Ranged Attack:"); 153 if (type === "Capture") return translate("Capture Attack:"); 154 155 warn(sprintf("Internationalization: Unexpected attack type found with code ‘%(attackType)s’. This attack type must be internationalized.", { "attackType": type })); 156 return translate("Attack:"); 158 if (!g_AttackTypes[type]) 159 { 160 warn("Unexpected attack type:" + type); 161 return translate("Attack:"); 162 } 163 return g_AttackTypes[type]; 157 164 } 158 165 166 // TODO: should also show minRange and splash damage 159 167 function getAttackTooltip(template) 160 168 { 161 let attacks = [];162 169 if (!template.attack) 163 170 return ""; 164 171 165 let rateLabel = g_TooltipTextFormats.header[0] + (template.buildingAI ? translate("Interval:") : translate("Rate:")) + g_TooltipTextFormats.header[1]; 166 172 let attacks = []; 167 173 for (let type in template.attack) 168 174 { 169 175 if (type == "Slaughter") 170 continue; // Slaughter is not a real attack, so do not show it.176 continue; // Slaughter is used to kill animals, so do not show it. 171 177 if (type == "Charge") 172 178 continue; // Charging isn't implemented yet and shouldn't be displayed. 173 179 174 180 let rate = sprintf(translate("%(label)s %(details)s"), { 175 "label": rateLabel, 181 "label": headerFont(template.buildingAI && type == "Ranged" ? 182 translate("Interval:") : 183 translate("Rate:")), 176 184 "details": attackRateDetails(template, type) 177 185 }); 178 186 179 let attackLabel = g_TooltipTextFormats.header[0] + getAttackTypeLabel(type) + g_TooltipTextFormats.header[1];180 if (type == "Capture" )187 let attackLabel = headerFont(getAttackTypeLabel(type)); 188 if (type == "Capture" || type != "Ranged") 181 189 { 182 190 attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), { 183 191 "attackLabel": attackLabel, 184 "details": template.attack[type].value, 185 "rate": rate 186 })); 187 continue; 188 } 189 if (type != "Ranged") 190 { 191 attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), { 192 "attackLabel": attackLabel, 193 "details": damageTypesToText(template.attack[type]), 192 "details": 193 type == "Capture" ? 194 template.attack.Capture.value : 195 damageTypesToText(template.attack[type]), 194 196 "rate": rate 195 197 })); 196 198 continue; 197 199 } 198 200 199 201 let realRange = template.attack[type].elevationAdaptedRange; 200 202 let range = Math.round(template.attack[type].maxRange); 201 let rangeLabel = g_TooltipTextFormats.header[0] + translate("Range:") + g_TooltipTextFormats.header[1]; 202 let relativeRange = Math.round((realRange - range)); 203 let meters = g_TooltipTextFormats.unit[0] + translatePlural("meter", "meters", range) + g_TooltipTextFormats.unit[1]; 203 let relativeRange = realRange ? Math.round(realRange - range) : 0; 204 204 205 if (relativeRange) // show if it is non-zero 206 attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rangeLabel)s %(rangeString)s (%(relative)s), %(rate)s"), { 207 "attackLabel": attackLabel, 208 "details": damageTypesToText(template.attack[type]), 209 "rangeLabel": rangeLabel, 210 "rangeString": sprintf( 211 translatePlural("%(range)s %(meters)s", "%(range)s %(meters)s", range), { 212 "range": range, 213 "meters": meters 214 }), 215 "relative": relativeRange > 0 ? "+" + relativeRange : relativeRange, 216 "rate": rate 217 })); 218 else 219 attacks.push(sprintf(translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(rangeString)s, %(rate)s"), { 220 "attackLabel": attackLabel, 221 "damageTypes": damageTypesToText(template.attack[type]), 222 "rangeLabel": rangeLabel, 223 "rangeString": sprintf( 224 translatePlural("%(range)s %(meters)s", "%(range)s %(meters)s", range), { 225 "range": range, 226 "meters": meters 227 }), 228 rate: rate 229 })); 205 let rangeString = relativeRange ? 206 translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(rangeString)s (%(relative)s), %(rate)s") : 207 translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(rangeString)s, %(rate)s"); 208 209 attacks.push(sprintf(rangeString, { 210 "attackLabel": attackLabel, 211 "damageTypes": damageTypesToText(template.attack[type]), 212 "rangeLabel": translate("Range:"), 213 "rangeString": sprintf( 214 translatePlural("%(range)s %(meters)s", "%(range)s %(meters)s", range), { 215 "range": range, 216 "meters": unitFont(translatePlural("meter", "meters", range)) 217 }), 218 "rate": rate, 219 "relative": relativeRange ? "+" + relativeRange : "", 220 })); 230 221 } 231 232 222 return attacks.join("\n"); 233 223 } 234 224 235 function getRepairRateTooltip(rate) 225 function getGarrisonTooltip(template) 226 { 227 if (!template.garrisonHolder) 228 return ""; 229 230 return sprintf(translate("%(label)s: %(garrisonLimit)s"), { 231 "label": headerFont(translate("Garrison Limit")), 232 "garrisonLimit": template.garrisonHolder.capacity || template.garrisonHolder.max 233 }); 234 } 235 236 function getProjectilesTooltip(template) 237 { 238 if (!template.garrisonHolder || !template.buildingAI) 239 return ""; 240 241 let limit = Math.min( 242 template.buildingAI.maxArrowCount || Infinity, 243 template.buildingAI.defaultArrowCount + 244 template.buildingAI.garrisonArrowMultiplier * 245 (template.garrisonHolder.capacity || template.garrisonHolder.max) 246 ); 247 248 if (!limit) 249 return ""; 250 251 return [ 252 sprintf(translate("%(label)s: %(value)s"), { 253 "label": headerFont(translate("Projectile Limit")), 254 "value": limit 255 }), 256 257 sprintf(translate("%(label)s: %(value)s"), { 258 "label": headerFont(translateWithContext("projectiles", "Default")), 259 "value": template.buildingAI.defaultArrowCount 260 }), 261 262 sprintf(translate("%(label)s: %(value)s"), { 263 "label": headerFont(translateWithContext("projectiles", "Per Unit")), 264 "value": template.buildingAI.garrisonArrowMultiplier 265 }) 266 ].join(commaFont(translate(", "))); 267 } 268 269 function getRepairRateTooltip(template) 236 270 { 237 return "\n" + sprintf(translate("%(repairRateLabel)s %(value)s %(health)s / %(second)s / %(worker)s"), { 238 "repairRateLabel": g_TooltipTextFormats.header[0] + translate("Repair Rate:") + g_TooltipTextFormats.header[1], 239 "value": Math.round(rate * 10) / 10, 240 "health": g_TooltipTextFormats.unit[0] + translate("health") + g_TooltipTextFormats.unit[1], 241 "second": g_TooltipTextFormats.unit[0] + translate("second") + g_TooltipTextFormats.unit[1], 242 "worker": g_TooltipTextFormats.unit[0] + translate("worker") + g_TooltipTextFormats.unit[1] 271 if (!template.repairRate) 272 return ""; 273 274 return sprintf(translate("%(repairRateLabel)s %(value)s %(health)s / %(second)s / %(worker)s"), { 275 "repairRateLabel": headerFont(translate("Repair Rate:")), 276 "value": Math.round(template.repairRate * 10) / 10, 277 "health": unitFont(translate("health")), 278 "second": unitFont(translate("second")), 279 "worker": unitFont(translate("worker")) 243 280 }); 244 281 } 245 282 246 function getBuildRateTooltip( rate)283 function getBuildRateTooltip(template) 247 284 { 248 return "\n" + sprintf(translate("%(buildRateLabel)s %(value)s %(health)s / %(second)s / %(worker)s"), { 249 "buildRateLabel": g_TooltipTextFormats.header[0] + translate("Build Rate:") + g_TooltipTextFormats.header[1], 250 "value": Math.round(rate * 10) / 10, 251 "health": g_TooltipTextFormats.unit[0] + translate("health") + g_TooltipTextFormats.unit[1], 252 "second": g_TooltipTextFormats.unit[0] + translate("second") + g_TooltipTextFormats.unit[1], 253 "worker": g_TooltipTextFormats.unit[0] + translate("worker") + g_TooltipTextFormats.unit[1] 285 if (!template.buildRate) 286 return ""; 287 288 return sprintf(translate("%(buildRateLabel)s %(value)s %(health)s / %(second)s / %(worker)s"), { 289 "buildRateLabel": headerFont(translate("Build Rate:")), 290 "value": Math.round(template.buildRate * 10) / 10, 291 "health": unitFont(translate("health")), 292 "second": unitFont(translate("second")), 293 "worker": unitFont(translate("worker")) 254 294 }); 255 295 } 256 296 257 297 /** 258 298 * Translates a cost component identifier as they are used internally … … function getBuildRateTooltip(rate) 261 301 function getCostComponentDisplayIcon(costComponentName) 262 302 { 263 303 if (costComponentName in g_CostDisplayIcons) 264 304 return g_CostDisplayIcons[costComponentName]; 265 305 266 warn( sprintf("The specified cost component, ‘%(component)s’, is not currently supported.", { "component": costComponentName }));306 warn("The specified cost component, ‘" + costComponentName + "’, is not currently supported."); 267 307 return ""; 268 308 } 269 309 270 310 /** 271 311 * Multiplies the costs for a template by a given batch size. … … function getEntityCostComponentsTooltipS 299 339 "cost": totalCosts[type] 300 340 })); 301 341 302 342 return costs; 303 343 } 344 function getGatherTooltip(template) 345 { 346 if (!template.gather) 347 return ""; 348 349 return sprintf(translate("%(label)s %(details)s"), { 350 "label": headerFont(translate("Gather Rates:")), 351 "details": 352 Object.keys(template.gather).map( 353 type => sprintf(translate("%(resourceIcon)s %(rate)s"), { 354 "resourceIcon": getCostComponentDisplayIcon(type), 355 "rate": template.gather[type] 356 }) 357 ).join(" ") 358 }); 359 } 304 360 305 361 /** 306 362 * Returns an array of strings for a set of wall pieces. If the pieces share 307 363 * resource type requirements, output will be of the form '10 to 30 Stone', 308 364 * otherwise output will be, e.g. '10 Stone, 20 Stone, 30 Stone'. … … function getEntityCostTooltip(template, 398 454 */ 399 455 function getPopulationBonusTooltip(template) 400 456 { 401 457 let popBonus = ""; 402 458 if (template.cost && template.cost.populationBonus) 403 popBonus = "\n" +sprintf(translate("%(label)s %(populationBonus)s"), {404 "label": g_TooltipTextFormats.header[0] + translate("Population Bonus:") + g_TooltipTextFormats.header[1],459 popBonus = sprintf(translate("%(label)s %(populationBonus)s"), { 460 "label": headerFont(translate("Population Bonus:")), 405 461 "populationBonus": template.cost.populationBonus 406 462 }); 407 463 return popBonus; 408 464 } 409 465 410 466 /** 411 467 * Returns a message with the amount of each resource needed to create an entity. 412 468 */ 413 469 function getNeededResourcesTooltip(resources) 414 470 { 471 if (!resources) 472 return ""; 473 415 474 let formatted = []; 416 475 for (let resource in resources) 417 476 formatted.push(sprintf(translate("%(component)s %(cost)s"), { 418 477 "component": '[font="sans-12"]' + getCostComponentDisplayIcon(resource) + '[/font]', 419 478 "cost": resources[resource] 420 479 })); 421 480 422 return '\n \n[font="sans-bold-13"][color="red"]' + translate("Insufficient resources:") + '[/color][/font]\n' + formatted.join(" ");481 return '\n[font="sans-bold-13"][color="red"]' + translate("Insufficient resources:") + '[/color][/font]\n' + formatted.join(" "); 423 482 } 424 483 425 484 function getSpeedTooltip(template) 426 485 { 427 486 if (!template.speed) 428 487 return ""; 429 488 430 let label = g_TooltipTextFormats.header[0] + translate("Speed:") + g_TooltipTextFormats.header[1];489 let label = headerFont(translate("Speed:")); 431 490 let speeds = []; 432 491 433 492 if (template.speed.walk) 434 493 speeds.push(sprintf(translate("%(speed)s %(movementType)s"), { 435 494 "speed": Math.round(template.speed.walk), 436 "movementType": g_TooltipTextFormats.unit[0] + translate("Walk") + g_TooltipTextFormats.unit[1]495 "movementType": unitFont(translate("Walk")) 437 496 })); 438 497 439 498 if (template.speed.run) 440 499 speeds.push(sprintf(translate("%(speed)s %(movementType)s"), { 441 500 "speed": Math.round(template.speed.run), 442 "movementType": g_TooltipTextFormats.unit[0] + translate("Run") + g_TooltipTextFormats.unit[1]501 "movementType": unitFont(translate("Run")) 443 502 })); 444 503 445 504 return sprintf(translate("%(label)s %(speeds)s"), { 446 505 "label": label, 447 506 "speeds": speeds.join(translate(", ")) … … function getSpeedTooltip(template) 451 510 function getHealerTooltip(template) 452 511 { 453 512 if (!template.healer) 454 513 return ""; 455 514 456 let healer =[515 return [ 457 516 sprintf(translatePlural("%(label)s %(val)s %(unit)s", "%(label)s %(val)s %(unit)s", template.healer.HP), { 458 "label": g_TooltipTextFormats.header[0] + translate("Heal:") + g_TooltipTextFormats.header[1],517 "label": headerFont(translate("Heal:")), 459 518 "val": template.healer.HP, 460 519 // Translation: Short for hit points (or health points) that are healed in one healing action 461 "unit": g_TooltipTextFormats.unit[0] + translatePlural("HP", "HP", template.healer.HP) + g_TooltipTextFormats.unit[1]520 "unit": unitFont(translatePlural("HP", "HP", template.healer.HP)) 462 521 }), 463 522 sprintf(translatePlural("%(label)s %(val)s %(unit)s", "%(label)s %(val)s %(unit)s", template.healer.Range), { 464 "label": g_TooltipTextFormats.header[0] + translate("Range:") + g_TooltipTextFormats.header[1],523 "label": headerFont(translate("Range:")), 465 524 "val": template.healer.Range, 466 "unit": g_TooltipTextFormats.unit[0] + translatePlural("meter", "meters", template.healer.Range) + g_TooltipTextFormats.unit[1]525 "unit": unitFont(translatePlural("meter", "meters", template.healer.Range)) 467 526 }), 468 527 sprintf(translatePlural("%(label)s %(val)s %(unit)s", "%(label)s %(val)s %(unit)s", template.healer.Rate/1000), { 469 "label": g_TooltipTextFormats.header[0] + translate("Rate:") + g_TooltipTextFormats.header[1],528 "label": headerFont(translate("Rate:")), 470 529 "val": template.healer.Rate/1000, 471 "unit": g_TooltipTextFormats.unit[0] + translatePlural("second", "seconds", template.healer.Rate/1000) + g_TooltipTextFormats.unit[1]530 "unit": unitFont(translatePlural("second", "seconds", template.healer.Rate / 1000)) 472 531 }) 473 ]; 474 return healer.join(translate(", ")); 532 ].join(translate(", ")); 475 533 } 476 534 477 535 function getAurasTooltip(template) 478 536 { 479 537 if (!template.auras) 480 538 return ""; 481 539 482 let txt = ""; 483 for (let aura in template.auras) 484 txt += '\n' + sprintf(translate("%(auralabel)s %(aurainfo)s"), { 485 "auralabel": g_TooltipTextFormats.header[0] + sprintf(translate("%(auraname)s:"), { 540 let tooltips = Object.keys(template.auras).map( 541 aura => sprintf(translate("%(auralabel)s %(aurainfo)s"), { 542 "auralabel": headerFont(sprintf(translate("%(auraname)s:"), { 486 543 "auraname": translate(template.auras[aura].name) 487 }) + g_TooltipTextFormats.header[1],488 "aurainfo": g_TooltipTextFormats.body[0] + translate(template.auras[aura].description) + g_TooltipTextFormats.body[1]489 }) ;490 return t xt;544 })), 545 "aurainfo": bodyFont(translate(template.auras[aura].description)) 546 })); 547 return tooltips.join("\n"); 491 548 } 492 549 493 550 function getEntityNames(template) 494 551 { 495 552 if (template.name.specific) … … function getEntityNamesFormatted(templat 523 580 names += '[font="sans-bold-16"] (' + generic + ')[/font]'; 524 581 } 525 582 else if (generic) 526 583 names = '[font="sans-bold-16"]' + generic + "[/font]"; 527 584 else 585 // TODO: translate and proper string 528 586 names = "???"; 529 587 530 588 return names; 531 589 } 532 590 533 591 function getVisibleEntityClassesFormatted(template) 534 592 { 535 let r = ""; 536 if (template.visibleIdentityClasses && template.visibleIdentityClasses.length) 537 { 538 r += '\n' + g_TooltipTextFormats.header[0] + translate("Classes:") + g_TooltipTextFormats.header[1]; 539 let classes = []; 540 for (let c of template.visibleIdentityClasses) 541 classes.push(translate(c)); 542 r += ' ' + g_TooltipTextFormats.body[0] + classes.join(translate(", ")) + g_TooltipTextFormats.body[1]; 543 } 593 if (!template.visibleIdentityClasses || !template.visibleIdentityClasses.length) 594 return ""; 595 596 597 let r = headerFont(translate("Classes:")); 598 599 let classes = []; 600 for (let c of template.visibleIdentityClasses) 601 classes.push(translate(c)); 602 603 r += ' ' + bodyFont(classes.join(translate(", "))); 544 604 return r; 545 605 } -
binaries/data/mods/public/gui/session/selection_details.js
function displaySingle(entState) 286 286 { 287 287 Engine.GetGUIObjectByName("playerCivIcon").sprite = ""; 288 288 Engine.GetGUIObjectByName("player").tooltip = ""; 289 289 } 290 290 291 // Icon image292 291 // TODO: we should require all entities to have icons 293 292 Engine.GetGUIObjectByName("icon").sprite = template.icon ? ("stretched:session/portraits/" + template.icon) : "bkFillBlack"; 294 293 295 let armorString = getArmorTooltip(entState.armour); 294 Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = [ 295 getAttackTooltip, 296 getArmorTooltip, 297 getRepairRateTooltip, 298 getBuildRateTooltip, 299 getGarrisonTooltip, 300 getProjectilesTooltip 301 ].map(func => func(entState)).filter(tip => tip).join("\n"); 296 302 297 // Attack and Armor298 Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = entState.attack ? (getAttackTooltip(entState) + "\n" + armorString) : armorString;299 300 // Repair Rate301 if (entState.repairRate)302 Engine.GetGUIObjectByName("attackAndArmorStats").tooltip += getRepairRateTooltip(entState.repairRate);303 304 // Build Rate305 if (entState.buildRate)306 Engine.GetGUIObjectByName("attackAndArmorStats").tooltip += getBuildRateTooltip(entState.buildRate);307 308 // Icon Tooltip309 303 let iconTooltip = ""; 310 304 311 305 if (genericName) 312 306 iconTooltip = "[font=\"sans-bold-16\"]" + genericName + "[/font]"; 313 307 … … function displaySingle(entState) 319 313 template.visibleIdentityClasses.map(c => translate(c)).join(translate(", ")) + 320 314 "[/font]"; 321 315 } 322 316 323 317 if (template.auras) 324 iconTooltip += getAurasTooltip(template);318 iconTooltip += "\n" + getAurasTooltip(template); 325 319 326 320 if (template.tooltip) 327 iconTooltip += "\n [font=\"sans-13\"]" + template.tooltip + "[/font]";321 iconTooltip += "\n" + getEntityTooltip(template); 328 322 329 323 Engine.GetGUIObjectByName("iconBorder").tooltip = iconTooltip; 330 324 331 325 // Unhide Details Area 332 326 Engine.GetGUIObjectByName("detailsAreaSingle").hidden = false; -
binaries/data/mods/public/gui/session/selection_panels.js
g_SelectionPanels.Construction = { 299 299 neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 300 300 "cost": multiplyEntityCosts(template, 1), 301 301 "player": data.unitEntState.player 302 302 }); 303 303 304 let limits = getEntityLimitAndCount(data.playerState, data.item);305 306 304 if (template.wallSet) 307 305 template.auras = GetTemplateData(template.wallSet.templates.long).auras; 308 306 309 307 data.button.onPress = function () { startBuildingPlacement(data.item, data.playerState); }; 310 308 311 let tooltip = getEntityNamesFormatted(template); 312 tooltip += getVisibleEntityClassesFormatted(template); 313 tooltip += getAurasTooltip(template); 314 315 if (template.tooltip) 316 tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]"; 309 let tooltips = [ 310 getEntityNamesFormatted, 311 getVisibleEntityClassesFormatted, 312 getAurasTooltip, 313 getEntityTooltip, 314 getEntityCostTooltip, 315 getGarrisonTooltip, 316 getProjectilesTooltip, 317 getPopulationBonusTooltip 318 ].map(func => func(template)); 317 319 318 tooltip += "\n" + getEntityCostTooltip(template); 319 tooltip += getPopulationBonusTooltip(template); 320 321 tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers); 320 let limits = getEntityLimitAndCount(data.playerState, data.item); 321 tooltips.push(formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers)); 322 322 323 323 if (!technologyEnabled) 324 tooltip += "\n" +sprintf(translate("Requires %(technology)s"), {324 tooltips.push(sprintf(translate("Requires %(technology)s"), { 325 325 "technology": getEntityNames(GetTechnologyData(template.requiredTechnology)) 326 }) ;326 })); 327 327 328 if (neededResources) 329 tooltip += getNeededResourcesTooltip(neededResources); 328 tooltips.push(getNeededResourcesTooltip(neededResources)); 330 329 331 data.button.tooltip = tooltip ;330 data.button.tooltip = tooltips.filter(tip => tip).join("\n"); 332 331 333 332 let modifier = ""; 334 333 if (!technologyEnabled || limits.canBeAddedCount == 0) 335 334 { 336 335 data.button.enabled = false; … … g_SelectionPanels.Gate = { 527 526 }, 528 527 "setupButton": function(data) 529 528 { 530 529 data.button.onPress = function() {data.item.callback(data.item); }; 531 530 532 let tooltip = data.item.tooltip;531 let tooltips = [data.item.tooltip]; 533 532 if (data.item.template) 534 533 { 535 534 data.template = GetTemplateData(data.item.template); 536 data.wallCount = data.selection.reduce( function (count, ent){535 data.wallCount = data.selection.reduce(count, ent => { 537 536 let state = GetEntityState(ent); 538 537 if (hasClass(state, "LongWall") && !state.gate) 539 538 ++count; 540 539 return count; 541 540 }, 0); 542 541 543 tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);542 tooltips.push(getEntityCostTooltip(data.template, data.wallCount)); 544 543 545 544 data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 546 545 "cost": multiplyEntityCosts(data.template, data.wallCount) 547 546 }); 548 547 549 if (data.neededResources) 550 tooltip += getNeededResourcesTooltip(data.neededResources); 548 tooltips.push(getNeededResourcesTooltip(data.neededResources)); 551 549 } 552 data.button.tooltip = tooltip ;550 data.button.tooltip = tooltips.filter(tip => tip).join("\n"); 553 551 554 552 data.button.enabled = controlsPlayer(data.unitEntState.player); 555 553 let gateIcon; 556 554 if (data.item.gate) 557 555 { … … g_SelectionPanels.Research = { 781 779 }); 782 780 783 781 let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]"); 784 782 let icon = Engine.GetGUIObjectByName("unitResearchIcon[" + position + "]"); 785 783 786 let tooltip = getEntityNamesFormatted(template); 787 if (template.tooltip) 788 tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]"; 784 let tooltips = [ 785 getEntityNamesFormatted(template), 786 getEntityTooltip(template), 787 getEntityCostTooltip(template) 788 ]; 789 789 790 tooltip += "\n" + getEntityCostTooltip(template);791 790 if (!requirementsPassed) 792 791 { 793 tooltip += "\n" +template.requirementsTooltip;792 let tip = template.requirementsTooltip; 794 793 if (template.classRequirements) 795 794 { 796 795 let player = data.unitEntState.player; 797 796 let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0; 798 797 let remaining = template.classRequirements.number - current; 799 tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { "number": remaining }); 798 tip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { 799 "number": remaining 800 }); 800 801 } 802 tooltips.push(tip); 801 803 } 802 if (neededResources) 803 tooltip += getNeededResourcesTooltip(neededResources); 804 button.tooltip = tooltip; 804 tooltips.push(getNeededResourcesTooltip(neededResources)); 805 button.tooltip = tooltips.filter(tip => tip).join("\n"); 805 806 806 807 button.onPress = function () { 807 808 addResearchToQueue(data.unitEntState.id, tech); 808 809 }; 809 810 … … g_SelectionPanels.Training = { 986 987 987 988 data.button.onPress = function() { addTrainingToQueue(data.selection, data.item, data.playerState); }; 988 989 989 990 data.countDisplay.caption = trainNum > 1 ? trainNum : ""; 990 991 991 let tooltip = "[font=\"sans-bold-16\"]" + 992 colorizeHotkey("%(hotkey)s", "session.queueunit." + (data.i + 1)) + 993 "[/font]"; 994 995 tooltip += getEntityNamesFormatted(template); 996 tooltip += getVisibleEntityClassesFormatted(template); 997 tooltip += getAurasTooltip(template); 998 999 if (template.tooltip) 1000 tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]"; 1001 1002 tooltip += "\n" + getEntityCostTooltip(template, trainNum, data.unitEntState.id); 992 let tooltips = [ 993 "[font=\"sans-bold-16\"]" + 994 colorizeHotkey("%(hotkey)s", "session.queueunit." + (data.i + 1)) + 995 "[/font]" + getEntityNamesFormatted(template), 996 getVisibleEntityClassesFormatted(template), 997 getAurasTooltip(template), 998 getEntityTooltip(template), 999 getEntityCostTooltip(template, trainNum, data.unitEntState.id) 1000 ]; 1003 1001 1004 1002 let limits = getEntityLimitAndCount(data.playerState, data.item); 1003 tooltips.push(formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers)); 1005 1004 1006 tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);1007 1005 if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true") 1008 { 1009 if (template.health) 1010 tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + template.health; 1011 if (template.attack) 1012 tooltip += "\n" + getAttackTooltip(template); 1013 if (template.armour) 1014 tooltip += "\n" + getArmorTooltip(template.armour); 1015 if (template.speed) 1016 tooltip += "\n" + getSpeedTooltip(template); 1017 } 1006 tooltips.push( 1007 getHealthTooltip(template), 1008 getAttackTooltip(template), 1009 getArmorTooltip(template), 1010 getGarrisonTooltip(template), 1011 getProjectilesTooltip(template), 1012 getSpeedTooltip(template) 1013 ); 1018 1014 1019 tooltip += "[color=\"" + g_HotkeyColor + "\"]" + 1015 tooltips.push( 1016 "[color=\"" + g_HotkeyColor + "\"]" + 1020 1017 formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) + 1021 "[/color]" ;1018 "[/color]"); 1022 1019 1023 1020 if (!technologyEnabled) 1024 {1025 let techName = getEntityNames(GetTechnologyData(template.requiredTechnology));1026 tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName });1027 } 1021 tooltips.push(sprintf(translate("Requires %(technology)s"), { 1022 "technology": getEntityNames(GetTechnologyData(template.requiredTechnology)) 1023 })); 1024 1028 1025 if (neededResources) 1029 tooltip += getNeededResourcesTooltip(neededResources);1026 tooltips.push(getNeededResourcesTooltip(neededResources)); 1030 1027 1031 data.button.tooltip = tooltip ;1028 data.button.tooltip = tooltips.filter(tip => tip).join("\n"); 1032 1029 1033 1030 let modifier = ""; 1034 1031 if (!technologyEnabled || limits.canBeAddedCount == 0) 1035 1032 { 1036 1033 data.button.enabled = false; -
binaries/data/mods/public/gui/session/selection_panels_helpers.js
function getStanceTooltip(name) 77 77 function formatLimitString(trainEntLimit, trainEntCount, trainEntLimitChangers) 78 78 { 79 79 if (trainEntLimit == undefined) 80 80 return ""; 81 81 82 var text = "\n\n" + sprintf(translate("Current Count: %(count)s, Limit: %(limit)s."), { "count": trainEntCount, "limit": trainEntLimit }); 82 var text = "\n" + sprintf(translate("Current Count: %(count)s, Limit: %(limit)s."), { 83 "count": trainEntCount, 84 "limit": trainEntLimit 85 }); 83 86 84 87 if (trainEntCount >= trainEntLimit) 85 88 text = "[color=\"red\"]" + text + "[/color]"; 86 89 87 90 for (var c in trainEntLimitChangers) 88 91 { 89 if (trainEntLimitChangers[c] > 0) 90 text += "\n" + sprintf(translate("%(changer)s enlarges the limit with %(change)s."), { "changer": translate(c), "change": trainEntLimitChangers[c] }); 91 else if (trainEntLimitChangers[c] < 0) 92 text += "\n" + sprintf(translate("%(changer)s lessens the limit with %(change)s."), { "changer": translate(c), "change": (-trainEntLimitChangers[c]) }); 92 if (!trainEntLimitChangers[c]) 93 continue; 94 95 let string = trainEntLimitChangers[c] > 0 ? 96 translate("%(changer)s enlarges the limit with %(change)s.") : 97 translate("%(changer)s lessens the limit with %(change)s."); 98 99 text += "\n" + sprintf(string, { 100 "changer": translate(c), 101 "change": trainEntLimitChangers[c] 102 }); 93 103 } 94 104 return text; 95 105 } 96 106 97 107 /** … … function formatBatchTrainingString(build 131 141 var action = "[font=\"sans-bold-13\"]" + translate("Shift-click") + "[/font]"; 132 142 133 143 // We need to display the batch details part if there is either more than 134 144 // one building with full batch or one building with the full batch and 135 145 // another with a partial batch 136 if (buildingsCountToTrainFullBatch > 1 || 137 (buildingsCountToTrainFullBatch == 1 && remainderBatch > 0)) 138 { 139 if (remainderBatch > 0) 140 return "\n[font=\"sans-13\"]" + sprintf(translate("%(action)s to train %(number)s (%(fullBatch)s + %(remainderBatch)s)."), { 141 "action": action, 142 "number": totalBatchTrainingCount, 143 "fullBatch": fullBatchesString, 144 "remainderBatch": remainderBatch 145 }) + "[/font]"; 146 147 return "\n[font=\"sans-13\"]" + sprintf(translate("%(action)s to train %(number)s (%(fullBatch)s)."), { 148 "action": action, 149 "number": totalBatchTrainingCount, 150 "fullBatch": fullBatchesString 151 }) + "[/font]"; 152 } 146 var batchString = 147 buildingsCountToTrainFullBatch > 1 || 148 buildingsCountToTrainFullBatch == 1 && remainderBatch > 0 ? 149 translate("%(action)s to train %(number)s.") : 150 remainderBatch ? 151 translate("%(action)s to train %(number)s (%(fullBatch)s + %(remainderBatch)s).") : 152 translate("%(action)s to train %(number)s (%(fullBatch)s)."); 153 153 154 return " \n[font=\"sans-13\"]" + sprintf(translate("%(action)s to train %(number)s."), {154 return "[font=\"sans-13\"]" + sprintf(batchString, { 155 155 "action": action, 156 "number": totalBatchTrainingCount 156 "number": totalBatchTrainingCount, 157 "fullBatch": fullBatchesString, 158 "remainderBatch": remainderBatch 157 159 }) + "[/font]"; 158 160 } 159 161 160 162 -
binaries/data/mods/public/gui/session/session.js
function updateHeroes() 822 822 } 823 823 } 824 824 825 825 function createHeroTooltip(heroState, template) 826 826 { 827 let tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]" + "\n" + 828 sprintf(translate("%(label)s %(current)s / %(max)s"), { 829 "label": "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]", 830 "current": Math.ceil(heroState.hitpoints), 831 "max": Math.ceil(heroState.maxHitpoints) 832 }); 833 834 if (heroState.attack) 835 tooltip += "\n" + getAttackTooltip(heroState); 836 837 tooltip += "\n" + getArmorTooltip(heroState.armour); 838 839 if (template.tooltip) 840 tooltip += "\n" + template.tooltip; 841 842 return tooltip; 827 return [ 828 "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]" + "\n" + 829 sprintf(translate("%(label)s %(current)s / %(max)s"), { 830 "label": "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]", 831 "current": Math.ceil(heroState.hitpoints), 832 "max": Math.ceil(heroState.maxHitpoints) 833 }), 834 getAttackTooltip(heroState), 835 getArmorTooltip(heroState), 836 getEntityTooltip(heroState) 837 ].filter(tip => tip).join("\n"); 843 838 } 844 839 845 840 function displayHeroes() 846 841 { 847 842 let buttons = Engine.GetGUIObjectByName("unitHeroPanel").children; -
binaries/data/mods/public/gui/structree/draw.js
1 1 var g_DrawLimits = {}; // GUI limits. Populated by predraw() 2 2 3 var g_TooltipFunctions = [ 4 getEntityNamesFormatted, 5 getEntityCostTooltip, 6 getEntityTooltip, 7 getAurasTooltip, 8 getHealthTooltip, 9 getHealerTooltip, 10 getAttackTooltip, 11 getArmorTooltip, 12 getGarrisonTooltip, 13 getProjectilesTooltip, 14 getSpeedTooltip, 15 getGatherTooltip, 16 getPopulationBonusTooltip 17 ]; 18 3 19 /** 4 20 * Draw the structree 5 21 * 6 22 * (Actually resizes and changes visibility of elements, and populates text) 7 23 */ … … function predraw() 361 377 362 378 /** 363 379 * Assemble a tooltip text 364 380 * 365 381 * @param template Information about a Unit, a Structure or a Technology 366 *367 382 * @return The tooltip text, formatted. 368 383 */ 369 384 function assembleTooltip(template) 370 385 { 371 let txt = getEntityNamesFormatted(template); 372 txt += '\n' + getEntityCostTooltip(template, 1); 373 374 if (template.tooltip) 375 txt += '\n' + g_TooltipTextFormats.body[0] + translate(template.tooltip) + g_TooltipTextFormats.body[1]; 376 377 if (template.auras) 378 txt += getAurasTooltip(template); 379 380 if (template.health) 381 txt += '\n' + sprintf(translate("%(label)s %(details)s"), { 382 "label": g_TooltipTextFormats.header[0] + translate("Health:") + g_TooltipTextFormats.header[1], 383 "details": template.health 384 }); 385 386 if (template.healer) 387 txt += '\n' + getHealerTooltip(template); 388 389 if (template.attack) 390 txt += '\n' + getAttackTooltip(template); 391 392 if (template.armour) 393 txt += '\n' + getArmorTooltip(template.armour); 394 395 if (template.speed) 396 txt += '\n' + getSpeedTooltip(template); 397 398 if (template.gather) 399 { 400 let rates = []; 401 for (let type in template.gather) 402 rates.push(sprintf(translate("%(resourceIcon)s %(rate)s"), { 403 "resourceIcon": getCostComponentDisplayIcon(type), 404 "rate": template.gather[type] 405 })); 406 407 txt += '\n' + sprintf(translate("%(label)s %(details)s"), { 408 "label": g_TooltipTextFormats.header[0] + translate("Gather Rates:") + g_TooltipTextFormats.header[1], 409 "details": rates.join(" ") 410 }); 411 } 412 413 txt += getPopulationBonusTooltip(template); 414 415 return txt; 386 return g_TooltipFunctions.map(func => func(template)).filter(tip => tip).join("\n"); 416 387 } -
binaries/data/mods/public/simulation/templates/other/hellenic_epic_temple.xml
27 27 <Identity> 28 28 <Civ>gaia</Civ> 29 29 <GenericName>Epic Temple</GenericName> 30 30 <SpecificName>Naos Parthenos</SpecificName> 31 31 <History>The Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.</History> 32 <Tooltip>Garrison u p to 30 units to heal them at a quick rate.</Tooltip>32 <Tooltip>Garrison units to heal them at a quick rate.</Tooltip> 33 33 </Identity> 34 34 <Loot> 35 35 <xp>50</xp> 36 36 <food>0</food> 37 37 <wood>0</wood> -
binaries/data/mods/public/simulation/templates/structures/athen_fortress.xml
5 5 <Height>8.0</Height> 6 6 </Footprint> 7 7 <Identity> 8 8 <Civ>athen</Civ> 9 9 <SpecificName>Epiteíkhisma</SpecificName> 10 <Tooltip>Build siege engines. Garrison up to 20soldiers inside for stout defense.</Tooltip>10 <Tooltip>Build siege engines. Garrison soldiers inside for stout defense.</Tooltip> 11 11 <History>Fortresses (also called "Phroúria") were built to guard passes and atop hills in order to command plains and valleys below. One such Athenian fortress, Gyphtokastro, guarded the pass from Attica into Boeotia.</History> 12 12 </Identity> 13 13 <Obstruction> 14 14 <Static width="24.0" depth="26.0"/> 15 15 </Obstruction> -
binaries/data/mods/public/simulation/templates/structures/athen_wonder.xml
12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>athen</Civ> 15 15 <SpecificName>Naós Parthenṓn</SpecificName> 16 16 <History>The Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.</History> 17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at an extremely quick rate.</Tooltip>17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at an extremely quick rate.</Tooltip> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="27.0" depth="57.0"/> 21 21 </Obstruction> 22 22 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/cart_temple.xml
11 11 </Footprint> 12 12 <Identity> 13 13 <Civ>cart</Civ> 14 14 <SpecificName>Maqdaš</SpecificName> 15 15 <History>What little we know of the Carthaginian religion has be pieced together from scattered sources. Tanit, a fertility goddess, was one of two principle gods in the Carthaginian pantheon, the other being her consort Ba'al, a deity of Phoenician origin.</History> 16 <Tooltip>Train priestesses to heal your troops. Train Sacred Band pikemen. Garrison u p to 20 units to heal them at a quick rate.</Tooltip>16 <Tooltip>Train priestesses to heal your troops. Train Sacred Band pikemen. Garrison units to heal them at a quick rate.</Tooltip> 17 17 </Identity> 18 18 <Obstruction> 19 19 <Static width="17.0" depth="30.0"/> 20 20 </Obstruction> 21 21 <ProductionQueue> -
binaries/data/mods/public/simulation/templates/structures/cart_wonder.xml
12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>cart</Civ> 15 15 <SpecificName>Temple of Ba'al Hammon</SpecificName> 16 16 <History>Dating from the 2nd Century BC, the Mausoleum of Atban in northern Tunisia is over twenty metres high and was built by the inhabitants of Dougga for a Numidian prince.</History> 17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at an extremely quick rate.</Tooltip>17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at an extremely quick rate.</Tooltip> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="27.0" depth="57.0"/> 21 21 </Obstruction> 22 22 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/mace_fortress.xml
11 11 <Height>8.0</Height> 12 12 </Footprint> 13 13 <Identity> 14 14 <Civ>mace</Civ> 15 15 <SpecificName>Teíchisma</SpecificName> 16 <Tooltip>Train Champions and Heroes. Garrison up to 20soldiers inside for stout defense.</Tooltip>16 <Tooltip>Train Champions and Heroes. Garrison soldiers inside for stout defense.</Tooltip> 17 17 <History>The Akropolis was usually a fortified citadel in the upper part of the city. The Athenian Akropolis was renowned for its marvelous temples, among which was the Parthenon, while the Acro-Corinthus was highly prized by the Macedonians for its strategic location and good defenses. Fortresses (also called a "phrourion") were also built to guard passes and atop hills in order to command plains and valleys below.</History> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="24.0" depth="26.0"/> 21 21 </Obstruction> -
binaries/data/mods/public/simulation/templates/structures/mace_wonder.xml
12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>mace</Civ> 15 15 <SpecificName>Naós Parthenṓn</SpecificName> 16 16 <History>The Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.</History> 17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at an extremely quick rate.</Tooltip>17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at an extremely quick rate.</Tooltip> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="27.0" depth="57.0"/> 21 21 </Obstruction> 22 22 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/ptol_barracks.xml
13 13 <SpawnEntityOnDeath>rubble/rubble_stone_5x5</SpawnEntityOnDeath> 14 14 </Health> 15 15 <Identity> 16 16 <Civ>ptol</Civ> 17 17 <SpecificName>ḥwt-n-mš'</SpecificName> 18 <Tooltip>Train Egyptian and Middle-Eastern citizen-soldiers. Research training improvements. Garrison: 10.</Tooltip>18 <Tooltip>Train Egyptian and Middle-Eastern citizen-soldiers. Research training improvements.</Tooltip> 19 19 </Identity> 20 20 <Obstruction> 21 21 <Static width="20.0" depth="20.0"/> 22 22 </Obstruction> 23 23 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/ptol_wonder.xml
12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>ptol</Civ> 15 15 <SpecificName>Temple of Edfu</SpecificName> 16 16 <History>The Temple of Edfu is an ancient Egyptian temple located on the west bank of the Nile in the city of Edfu which was known in Greco-Roman times as Apollonopolis Magna, after the chief god Horus-Apollo.The temple, dedicated to the falcon god Horus, was built in the Ptolemaic period between 237 and 57 BCE. In modern times, it is one of the best preserved temples of Egypt.</History> 17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at an extremely quick rate.</Tooltip>17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at an extremely quick rate.</Tooltip> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="44.0" depth="60.0"/> 21 21 </Obstruction> 22 22 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/rome_temple_mars.xml
10 10 <Identity> 11 11 <Civ>rome</Civ> 12 12 <GenericName>Temple of Mars</GenericName> 13 13 <SpecificName>Aedes Martis</SpecificName> 14 14 <History>Roman temples in general were not meant for congregational worship. Instead the temple housed a statue of whatever deity the temple was dedicated to and what was needed to carry out the ceremonial and cultic practice necessary for worship. Any actual worship activity was performed outside.</History> 15 <Tooltip>Train healers. Garrison u p to 30 units to heal them at a quick rate.</Tooltip>15 <Tooltip>Train healers. Garrison units to heal them at a quick rate.</Tooltip> 16 16 </Identity> 17 17 <Obstruction> 18 18 <Static width="20.0" depth="40.0"/> 19 19 </Obstruction> 20 20 <TerritoryInfluence> -
binaries/data/mods/public/simulation/templates/structures/rome_wonder.xml
11 11 <BuffHeal>8</BuffHeal> 12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>rome</Civ> 15 15 <SpecificName>Aedes Iovis Optimi Maximi</SpecificName> 16 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at an extremely quick rate.</Tooltip>16 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at an extremely quick rate.</Tooltip> 17 17 </Identity> 18 18 <Obstruction> 19 19 <Static width="20.0" depth="40.0"/> 20 20 </Obstruction> 21 21 <VisualActor> -
binaries/data/mods/public/simulation/templates/structures/sele_fortress.xml
5 5 <Height>8.0</Height> 6 6 </Footprint> 7 7 <Identity> 8 8 <Civ>sele</Civ> 9 9 <SpecificName>Phrourion</SpecificName> 10 <Tooltip>Build siege engines and train Champions. Garrison up to 20soldiers inside for stout defense.</Tooltip>10 <Tooltip>Build siege engines and train Champions. Garrison soldiers inside for stout defense.</Tooltip> 11 11 <History>Fortresses were built to guard passes and atop hills in order to command plains and valleys below.</History> 12 12 </Identity> 13 13 <Obstruction> 14 14 <Static width="24.0" depth="26.0"/> 15 15 </Obstruction> -
binaries/data/mods/public/simulation/templates/structures/spart_wonder.xml
12 12 </GarrisonHolder> 13 13 <Identity> 14 14 <Civ>spart</Civ> 15 15 <SpecificName>Naós Parthenṓn</SpecificName> 16 16 <History>The Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.</History> 17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison u p to 30 units to heal them at a quick rate.</Tooltip>17 <Tooltip>Bring glory to your civilization and add large tracts of land to your empire. Garrison units to heal them at a quick rate.</Tooltip> 18 18 </Identity> 19 19 <Obstruction> 20 20 <Static width="27.0" depth="57.0"/> 21 21 </Obstruction> 22 22 <VisualActor> -
binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
64 64 <Max>3000</Max> 65 65 <SpawnEntityOnDeath>rubble/rubble_stone_6x6</SpawnEntityOnDeath> 66 66 </Health> 67 67 <Identity> 68 68 <GenericName>Civic Center</GenericName> 69 <Tooltip>Build to acquire large tracts of territory. Train citizens. Garrison: 20.</Tooltip>69 <Tooltip>Build to acquire large tracts of territory. Train citizens.</Tooltip> 70 70 <Classes datatype="tokens">Defensive CivCentre</Classes> 71 71 <VisibleClasses datatype="tokens">CivilCentre</VisibleClasses> 72 72 <Icon>structures/civic_centre.png</Icon> 73 73 </Identity> 74 74 <Loot> -
binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
27 27 <Max>2000</Max> 28 28 <SpawnEntityOnDeath>rubble/rubble_stone_4x6</SpawnEntityOnDeath> 29 29 </Health> 30 30 <Identity> 31 31 <GenericName>Temple</GenericName> 32 <Tooltip>Train healers. Garrison u p to 20 units to heal them at a quick rate (3 HP per second). Research healing and religious improvements.</Tooltip>32 <Tooltip>Train healers. Garrison units to heal them at a quick rate (3 HP per second). Research healing and religious improvements.</Tooltip> 33 33 <VisibleClasses datatype="tokens">Town Temple</VisibleClasses> 34 34 <Icon>structures/temple.png</Icon> 35 35 <RequiredTechnology>phase_town</RequiredTechnology> 36 36 </Identity> 37 37 <Loot> -
binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml
25 25 <Max>2000</Max> 26 26 <SpawnEntityOnDeath>rubble/rubble_stone_4x4</SpawnEntityOnDeath> 27 27 </Health> 28 28 <Identity> 29 29 <GenericName>Barracks</GenericName> 30 <Tooltip>Train citizen-soldiers. Research training improvements. Garrison: 10.</Tooltip>30 <Tooltip>Train citizen-soldiers. Research training improvements.</Tooltip> 31 31 <VisibleClasses datatype="tokens">Village Barracks</VisibleClasses> 32 32 <Icon>structures/barracks.png</Icon> 33 33 <RequiredTechnology>phase_village</RequiredTechnology> 34 34 </Identity> 35 35 <Loot> -
binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
59 59 <Max>4200</Max> 60 60 <SpawnEntityOnDeath>rubble/rubble_stone_6x6</SpawnEntityOnDeath> 61 61 </Health> 62 62 <Identity> 63 63 <GenericName>Fortress</GenericName> 64 <Tooltip>Train heroes, champions, and siege weapons. Research siege weapon improvements. Garrison: 20.</Tooltip>64 <Tooltip>Train heroes, champions, and siege weapons. Research siege weapon improvements.</Tooltip> 65 65 <Classes datatype="tokens">GarrisonFortress</Classes> 66 66 <VisibleClasses datatype="tokens">Defensive City Fortress</VisibleClasses> 67 67 <Icon>structures/fortress.png</Icon> 68 68 <RequiredTechnology>phase_city</RequiredTechnology> 69 69 </Identity> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
45 45 </Health> 46 46 <Identity> 47 47 <GenericName>Light Warship</GenericName> 48 48 <Classes datatype="tokens">Warship Light Bow Ranged</Classes> 49 49 <RequiredTechnology>phase_town</RequiredTechnology> 50 <Tooltip>Garrison u p to 20 units for transport. Garrison increases the firepower up to 10 arrows.</Tooltip>50 <Tooltip>Garrison units for transport and to increase firepower.</Tooltip> 51 51 </Identity> 52 52 <ResourceGatherer disable=""/> 53 53 <Sound> 54 54 <SoundGroups> 55 55 <attack>attack/weapon/arrowfly.xml</attack> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
21 21 </Splash> 22 22 </Ranged> 23 23 </Attack> 24 24 <BuildingAI> 25 25 <DefaultArrowCount>1</DefaultArrowCount> 26 <MaxArrowCount>1 1</MaxArrowCount>26 <MaxArrowCount>10</MaxArrowCount> 27 27 <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> 28 28 <GarrisonArrowClasses>Catapult</GarrisonArrowClasses> 29 29 </BuildingAI> 30 30 <Cost> 31 31 <Population>3</Population> … … 52 52 <Health> 53 53 <Max>2000</Max> 54 54 </Health> 55 55 <Identity> 56 56 <GenericName>Heavy Warship</GenericName> 57 <Tooltip>Garrison u p to 10 catapults to increase firepower.</Tooltip>57 <Tooltip>Garrison units for transport and to increase firepower.</Tooltip> 58 58 <Classes datatype="tokens">Warship Heavy Ranged</Classes> 59 59 <RequiredTechnology>phase_city</RequiredTechnology> 60 60 </Identity> 61 61 <Sound> 62 62 <SoundGroups> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
45 45 </Health> 46 46 <Identity> 47 47 <GenericName>Medium Warship</GenericName> 48 48 <VisibleClasses datatype="tokens">Warship Medium Ranged</VisibleClasses> 49 49 <RequiredTechnology>phase_town</RequiredTechnology> 50 <Tooltip>Garrison u p to 30 units for transport. Garrison increases the firepower up to 13 arrows.</Tooltip>50 <Tooltip>Garrison units for transport and to increase firepower.</Tooltip> 51 51 </Identity> 52 52 <ResourceGatherer disable=""/> 53 53 <Sound> 54 54 <SoundGroups> 55 55 <attack>attack/weapon/arrowfly.xml</attack> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml
53 53 <Max>500</Max> 54 54 </Health> 55 55 <Identity> 56 56 <GenericName>Siege Tower</GenericName> 57 57 <Classes datatype="tokens">Ranged SiegeTower</Classes> 58 <Tooltip>Garrison u p to 20 infantry inside to increase arrow count from 0 to 10.</Tooltip>58 <Tooltip>Garrison units for transport and to increase firepower.</Tooltip> 59 59 </Identity> 60 60 <Selectable> 61 61 <Overlay> 62 62 <Texture> 63 63 <MainTexture>circle/256x256.png</MainTexture>