Index: ps/trunk/binaries/data/mods/public/gui/common/tooltips.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/tooltips.js +++ ps/trunk/binaries/data/mods/public/gui/common/tooltips.js @@ -185,15 +185,16 @@ function attackRateDetails(interval, projectiles) { - // ToDo: Get the name of a projectile from the template. - - if (!interval || projectiles == 0) + if (!interval) return ""; + if (projectiles === 0) + return translate("Garrison to fire arrows"); + let attackRateString = getSecondsString(interval / 1000); let header = headerFont(translate("Interval:")); - if (+projectiles > 1) + if (projectiles && +projectiles > 1) { header = headerFont(translate("Rate:")); let projectileString = sprintf(translatePlural("%(projectileCount)s %(projectileName)s", "%(projectileCount)s %(projectileName)s", projectiles), { @@ -258,7 +259,7 @@ return Object.keys(damageTemplate).filter(dmgType => damageTemplate[dmgType]).map( dmgType => sprintf(translate("%(damage)s %(damageType)s"), { - "damage": damageTemplate[dmgType].toFixed(1), + "damage": (+damageTemplate[dmgType]).toFixed(1), "damageType": unitFont(translateWithContext("damage type", dmgType)) })).join(commaFont(translate(", "))); } @@ -269,11 +270,21 @@ return ""; return sprintf(translate("%(amount)s %(name)s"), { - "amount": captureTemplate.toFixed(1), + "amount": (+captureTemplate).toFixed(1), "name": unitFont(translateWithContext("damage type", "Capture")) }); } +function giveStatusDetails(giveStatusTemplate) +{ + if (!giveStatusTemplate) + return ""; + + return sprintf(translate("gives %(name)s"), { + "name": Object.keys(giveStatusTemplate).map(x => unitFont(translateWithContext("status effect", x))).join(', '), + }); +} + function attackEffectsDetails(attackTypeTemplate) { if (!attackTypeTemplate) @@ -281,7 +292,8 @@ let effects = [ captureDetails(attackTypeTemplate.Capture || undefined), - damageDetails(attackTypeTemplate.Damage || undefined) + damageDetails(attackTypeTemplate.Damage || undefined), + giveStatusDetails(attackTypeTemplate.GiveStatus || undefined) ]; return effects.filter(effect => effect).join(commaFont(translate(", "))); } @@ -309,11 +321,19 @@ if (template.buildingAI) projectiles = template.buildingAI.arrowCount || template.buildingAI.defaultArrowCount; - tooltips.push(sprintf(translate("%(attackLabel)s: %(effects)s, %(range)s, %(rate)s"), { + // Show the effects of status effects below + let statusEffectsDetails = []; + if (attackTypeTemplate.GiveStatus) + for (let status in attackTypeTemplate.GiveStatus) + statusEffectsDetails.push("\n " + getStatusEffectsTooltip(status, attackTypeTemplate.GiveStatus[status])); + statusEffectsDetails = statusEffectsDetails.join(""); + + tooltips.push(sprintf(translate("%(attackLabel)s: %(effects)s, %(range)s, %(rate)s%(statusEffects)s"), { "attackLabel": attackLabel, "effects": attackEffectsDetails(attackTypeTemplate), "range": rangeDetails(attackTypeTemplate), - "rate": attackRateDetails(attackTypeTemplate.repeatTime, projectiles) + "rate": attackRateDetails(attackTypeTemplate.repeatTime, projectiles), + "statusEffects": statusEffectsDetails })); } return tooltips.join("\n"); @@ -351,6 +371,23 @@ return tooltips.join("\n"); } +function getStatusEffectsTooltip(name, template) +{ + let durationString = ""; + if (template.Duration) + durationString = sprintf(translate(", %(durName)s: %(duration)s"), { + "durName": headerFont(translate("Duration")), + "duration": getSecondsString((template.TimeElapsed ? +template.Duration - template.TimeElapsed : +template.Duration) / 1000), + }); + + return sprintf(translate("%(statusName)s: %(effects)s, %(rate)s%(durationString)s"), { + "statusName": headerFont(translateWithContext("status effect", name)), + "effects": attackEffectsDetails(template), + "rate": attackRateDetails(+template.Interval), + "durationString": durationString + }); +} + function getGarrisonTooltip(template) { if (!template.garrisonHolder) Index: ps/trunk/binaries/data/mods/public/gui/session/selection_details.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/selection_details.js +++ ps/trunk/binaries/data/mods/public/gui/session/selection_details.js @@ -93,6 +93,26 @@ Engine.GetGUIObjectByName("rankIcon").tooltip = ""; } + if (entState.statusEffects) + { + let statusIcons = Engine.GetGUIObjectByName("statusEffectsIcons").children; + let i = 0; + for (let effectName in entState.statusEffects) + { + let effect = entState.statusEffects[effectName]; + statusIcons[i].hidden = false; + statusIcons[i].sprite = "stretched:session/icons/status_effects/" + (effect.Icon || "default") + ".png"; + statusIcons[i].tooltip = getStatusEffectsTooltip(effectName, effect); + let size = statusIcons[i].size; + size.top = i * 18; + size.bottom = i * 18 + 16; + statusIcons[i].size = size; + i++; + } + for (; i < statusIcons.length; ++i) + statusIcons[i].hidden = true; + } + let showHealth = entState.hitpoints; let showResource = entState.resourceSupply; Index: ps/trunk/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml +++ ps/trunk/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml @@ -82,9 +82,18 @@ Rank + + + + + + + + + Index: ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js +++ ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -291,6 +291,10 @@ "template": cmpUpgrade.GetUpgradingTo() }; + let cmpStatusEffects = Engine.QueryInterface(ent, IID_StatusEffectsReceiver); + if (cmpStatusEffects) + ret.statusEffects = cmpStatusEffects.GetActiveStatuses(); + let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue); if (cmpProductionQueue) ret.production = { Index: ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js +++ ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js @@ -5,6 +5,11 @@ this.activeStatusEffects = {}; }; +StatusEffectsReceiver.prototype.GetActiveStatuses = function() +{ + return this.activeStatusEffects; +}; + // Called by attacking effects. StatusEffectsReceiver.prototype.GiveStatus = function(effectData, attacker, attackerOwner, bonusMultiplier) { @@ -23,23 +28,23 @@ this.activeStatusEffects[statusName] = {}; let status = this.activeStatusEffects[statusName]; - status.duration = +data.Duration; - status.interval = +data.Interval; - status.damage = +data.Damage; - status.timeElapsed = 0; - status.firstTime = true; + Object.assign(status, data); + status.Interval = +data.Interval; + status.TimeElapsed = 0; + status.FirstTime = true; let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - status.timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +status.interval, statusName); + status.Timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +status.Interval, statusName); }; -StatusEffectsReceiver.prototype.RemoveStatus = function(statusName) { +StatusEffectsReceiver.prototype.RemoveStatus = function(statusName) +{ if (!this.activeStatusEffects[statusName]) return; let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - cmpTimer.CancelTimer(this.activeStatusEffects[statusName].timer); - this.activeStatusEffects[statusName] = undefined; + cmpTimer.CancelTimer(this.activeStatusEffects[statusName].Timer); + delete this.activeStatusEffects[statusName]; }; StatusEffectsReceiver.prototype.ExecuteEffect = function(statusName, lateness) @@ -48,17 +53,17 @@ if (!status) return; - if (status.firstTime) + if (status.FirstTime) { - status.firstTime = false; - status.timeElapsed += lateness; + status.FirstTime = false; + status.TimeElapsed += lateness; } else - status.timeElapsed += status.interval + lateness; + status.TimeElapsed += status.Interval + lateness; - Attacking.HandleAttackEffects(statusName, { "Damage": { [statusName]: status.damage } }, this.entity, -1, -1); + Attacking.HandleAttackEffects(statusName, status, this.entity, -1, -1); - if (status.timeElapsed >= status.duration) + if (status.Duration && status.TimeElapsed >= +status.Duration) this.RemoveStatus(statusName); }; Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -31,6 +31,7 @@ Engine.LoadComponentScript("interfaces/Trader.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); +Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("interfaces/Upgrade.js"); Engine.LoadComponentScript("interfaces/BuildingAI.js"); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js @@ -28,7 +28,9 @@ cmpStatusReceiver.AddStatus(statusName, { "Duration": 20000, "Interval": 10000, - "Damage": 1 + "Damage": { + [statusName]: 1 + } }); cmpTimer.OnUpdate({ "turnLength": 1 }); @@ -65,12 +67,16 @@ "Burn": { "Duration": 20000, "Interval": 10000, - "Damage": 10 + "Damage": { + "Burn": 10 + } }, "Poison": { "Duration": 3000, "Interval": 1000, - "Damage": 1 + "Damage": { + "Poison": 1 + } } }); @@ -102,7 +108,9 @@ cmpStatusReceiver.AddStatus(statusName, { "Duration": 20000, "Interval": 10000, - "Damage": 1 + "Damage": { + [statusName]: 1 + } }); cmpTimer.OnUpdate({ "turnLength": 1 }); Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js @@ -5,38 +5,44 @@ /** * Builds a RelaxRNG schema of possible attack effects. - * Currently harcoded to "Damage", "Capture" and "StatusEffects". + * See globalscripts/AttackEffects.js for possible elements. * Attacks may also have a "Bonuses" element. * * @return {string} - RelaxNG schema string */ +const DamageSchema = "" + + "" + + "" + + "" + + // Armour requires Foundation to not be a damage type. + "Foundation" + + "" + + "" + + "" + + ""; + Attacking.prototype.BuildAttackEffectsSchema = function() { return "" + "" + "" + "" + - "" + - "" + - "" + - // Armour requires Foundation to not be a damage type. - "Foundation" + - "" + - "" + - "" + - "" + + DamageSchema + "" + "" + "" + "" + - "" + + "" + "" + "" + "" + "" + + "" + + "" + + "" + "" + "" + - "" + + "" + DamageSchema + "" + "" + "" + "" + @@ -79,8 +85,8 @@ if (template.Capture) ret.Capture = ApplyValueModificationsToEntity(valueModifRoot + "/Capture", +(template.Capture || 0), entity); - if (template.StatusEffects) - ret.StatusEffects = template.StatusEffects; + if (template.GiveStatus) + ret.GiveStatus = template.GiveStatus; if (template.Bonuses) ret.Bonuses = template.Bonuses;