From d2a17557b42fda18c666bf1cf498654c9a07f13e Mon Sep 17 00:00:00 2001
From: "Stephen A. Imhoff" <clockwork-muse@outlook.com>
Date: Sat, 14 May 2016 11:03:03 +0900
Subject: [PATCH] heros
---
binaries/data/mods/public/gui/session/session.js | 149 +++++++++++++++------
.../gui/session/session_objects/hero_icon.xml | 29 ++--
2 files changed, 120 insertions(+), 58 deletions(-)
diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js
index c1a0d9d..bee337e 100644
a
|
b
|
var g_ShowGuarded = false;
|
143 | 143 | var g_AdditionalHighlight = []; |
144 | 144 | |
145 | 145 | /** |
146 | | * Blink the hero selection if that entity has lost health since the last turn. |
| 146 | * List of heroes to display. |
147 | 147 | */ |
148 | | var g_PreviousHeroHitPoints; |
| 148 | var g_Heroes = []; |
149 | 149 | |
150 | 150 | /** |
151 | 151 | * Unit classes to be checked for the idle-worker-hotkey. |
… |
… |
function init(initData, hotloadData)
|
269 | 269 | gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); }; |
270 | 270 | initMenuPosition(); |
271 | 271 | |
| 272 | for (let slot = 0, length = Engine.GetGUIObjectByName("unitHeroPanel").children.length; slot < length; ++slot) |
| 273 | initGUIHeroes(slot); |
| 274 | |
272 | 275 | // Populate player selection dropdown |
273 | 276 | let playerNames = [translate("Observer")]; |
274 | 277 | let playerIDs = [-1]; |
… |
… |
function initHotkeyTooltips()
|
320 | 323 | "session.fulltradeswap"); |
321 | 324 | } |
322 | 325 | |
| 326 | function initGUIHeroes(slot) |
| 327 | { |
| 328 | let button = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]"); |
| 329 | button.onPress = function() |
| 330 | { |
| 331 | let hero = g_Heroes.find(hero => hero.slot !== undefined && hero.slot == slot); |
| 332 | if (!hero) |
| 333 | return; |
| 334 | if (!Engine.HotkeyIsPressed("selection.add")) |
| 335 | g_Selection.reset(); |
| 336 | g_Selection.addList([hero.ent]); |
| 337 | }; |
| 338 | |
| 339 | button.onDoublePress = function() { |
| 340 | let hero = g_Heroes.find(hero => hero.slot !== undefined && hero.slot == slot); |
| 341 | if (hero) |
| 342 | selectAndMoveTo(getEntityOrHolder(hero.ent)); |
| 343 | }; |
| 344 | } |
| 345 | |
323 | 346 | function initializeMusic() |
324 | 347 | { |
325 | 348 | initMusic(); |
… |
… |
function updateGUIObjects()
|
726 | 749 | if (g_ShowGuarding || g_ShowGuarded) |
727 | 750 | updateAdditionalHighlight(); |
728 | 751 | |
729 | | updateHero(); |
| 752 | updateHeroes(); |
| 753 | displayHeroes(); |
730 | 754 | updateGroups(); |
731 | 755 | updateDebug(); |
732 | 756 | updatePlayerDisplay(); |
… |
… |
function updateGUIStatusBar(nameOfBar, points, maxPoints, direction)
|
802 | 826 | } |
803 | 827 | |
804 | 828 | |
805 | | function updateHero() |
| 829 | function updateHeroes() |
806 | 830 | { |
807 | | let unitHeroPanel = Engine.GetGUIObjectByName("unitHeroPanel"); |
808 | | let heroButton = Engine.GetGUIObjectByName("unitHeroButton"); |
809 | | |
810 | 831 | let playerState = GetSimState().players[g_ViewedPlayer]; |
811 | | if (!playerState || playerState.heroes.length <= 0) |
812 | | { |
813 | | g_PreviousHeroHitPoints = undefined; |
814 | | unitHeroPanel.hidden = true; |
815 | | return; |
816 | | } |
| 832 | let heroes = playerState ? playerState.heroes : []; |
817 | 833 | |
818 | | let heroImage = Engine.GetGUIObjectByName("unitHeroImage"); |
819 | | let heroState = GetExtendedEntityState(playerState.heroes[0]); |
820 | | let template = GetTemplateData(heroState.template); |
821 | | heroImage.sprite = "stretched:session/portraits/" + template.icon; |
822 | | let hero = playerState.heroes[0]; |
| 834 | g_Heroes = g_Heroes.filter(hero => heroes.find(ent => ent == hero.ent)); |
823 | 835 | |
824 | | heroButton.onpress = function() |
| 836 | for (let ent of heroes) |
825 | 837 | { |
826 | | if (!Engine.HotkeyIsPressed("selection.add")) |
827 | | g_Selection.reset(); |
828 | | g_Selection.addList([hero]); |
829 | | }; |
830 | | heroButton.ondoublepress = function() { selectAndMoveTo(getEntityOrHolder(hero)); }; |
831 | | unitHeroPanel.hidden = false; |
| 838 | let heroState = GetExtendedEntityState(ent); |
| 839 | let template = GetTemplateData(heroState.template); |
| 840 | |
| 841 | let hero = g_Heroes.find(hero => ent == hero.ent); |
| 842 | |
| 843 | if (!hero) |
| 844 | { |
| 845 | hero = { |
| 846 | "ent": ent, |
| 847 | "tooltip": undefined, |
| 848 | "sprite": "stretched:session/portraits/" + template.icon, |
| 849 | "maxHitpoints": undefined, |
| 850 | "currentHitpoints": heroState.hitpoints, |
| 851 | "previousHitpoints": undefined |
| 852 | }; |
| 853 | g_Heroes.push(hero); |
| 854 | } |
| 855 | |
| 856 | hero.tooltip = createHeroTooltip(heroState, template); |
| 857 | hero.previousHitpoints = hero.currentHitpoints; |
| 858 | hero.currentHitpoints = heroState.hitpoints; |
| 859 | hero.maxHitpoints = heroState.maxHitpoints; |
| 860 | } |
| 861 | } |
832 | 862 | |
| 863 | function createHeroTooltip(heroState, template) |
| 864 | { |
833 | 865 | // Setup tooltip |
834 | | let tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]"; |
835 | | let healthLabel = "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]"; |
836 | | tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), { |
837 | | "label": healthLabel, |
838 | | "current": Math.ceil(heroState.hitpoints), |
839 | | "max": Math.ceil(heroState.maxHitpoints) |
| 866 | let tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]" + "\n" |
| 867 | + sprintf(translate("%(label)s %(current)s / %(max)s"), { |
| 868 | "label": "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]", |
| 869 | "current": Math.ceil(heroState.hitpoints), |
| 870 | "max": Math.ceil(heroState.maxHitpoints) |
840 | 871 | }); |
841 | 872 | if (heroState.attack) |
842 | 873 | tooltip += "\n" + getAttackTooltip(heroState); |
… |
… |
function updateHero()
|
844 | 875 | tooltip += "\n" + getArmorTooltip(heroState.armour); |
845 | 876 | if (template.tooltip) |
846 | 877 | tooltip += "\n" + template.tooltip; |
| 878 | return tooltip; |
| 879 | } |
847 | 880 | |
848 | | heroButton.tooltip = tooltip; |
| 881 | function displayHeroes() |
| 882 | { |
| 883 | let buttons = Engine.GetGUIObjectByName("unitHeroPanel").children; |
849 | 884 | |
850 | | // update heros health bar |
851 | | updateGUIStatusBar("heroHealthBar", heroState.hitpoints, heroState.maxHitpoints); |
| 885 | buttons.forEach((button, slot) => |
| 886 | { |
| 887 | if (button.hidden || g_Heroes.find(hero => hero.slot !== undefined && hero.slot == slot)) |
| 888 | return; |
852 | 889 | |
853 | | let heroHP = { |
854 | | "hitpoints": heroState.hitpoints, |
855 | | "player": g_ViewedPlayer |
856 | | }; |
| 890 | Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]").hidden = true; |
| 891 | stopColorFade("heroHitOverlay[" + slot + "]"); |
857 | 892 | |
858 | | if (!g_PreviousHeroHitPoints) |
859 | | g_PreviousHeroHitPoints = heroHP; |
| 893 | }); |
860 | 894 | |
861 | | // if the health of the hero changed since the last update, trigger the animation |
862 | | if (g_PreviousHeroHitPoints.player == heroHP.player && g_PreviousHeroHitPoints.hitpoints > heroHP.hitpoints) |
863 | | startColorFade("heroHitOverlay", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit); |
| 895 | for (let displayIndex = 0; displayIndex < Math.min(g_Heroes.length, buttons.length); ++displayIndex) |
| 896 | { |
| 897 | let hero = g_Heroes[displayIndex]; |
| 898 | // Find the first unused slot if new, otherwise reuse previous. |
| 899 | let slot = hero.slot === undefined ? |
| 900 | buttons.findIndex(button => button.hidden) : |
| 901 | hero.slot; |
| 902 | |
| 903 | let heroButton = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]"); |
| 904 | heroButton.tooltip = hero.tooltip; |
| 905 | updateGUIStatusBar("heroHealthBar[" + slot + "]", hero.currentHitpoints, hero.maxHitpoints); |
864 | 906 | |
865 | | g_PreviousHeroHitPoints = heroHP; |
| 907 | if (hero.slot === undefined) |
| 908 | { |
| 909 | let heroImage = Engine.GetGUIObjectByName("unitHeroImage[" + slot + "]"); |
| 910 | heroImage.sprite = hero.sprite; |
| 911 | heroButton.hidden = false; |
| 912 | |
| 913 | hero.slot = slot; |
| 914 | } |
| 915 | |
| 916 | // If the health of the hero changed since the last update, trigger the animation. |
| 917 | if (hero.previousHitpoints > hero.currentHitpoints) |
| 918 | startColorFade("heroHitOverlay[" + slot + "]", 100, 0, |
| 919 | colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit); |
| 920 | |
| 921 | // The "slot" is which button, but it's the identity (name) of the button, not its position. |
| 922 | // Heroes stay in the same slot, but the position of the button may change. |
| 923 | // TODO: Instead of instant position changes, animate button movement. |
| 924 | setPanelObjectPosition(heroButton, displayIndex, buttons.length); |
| 925 | |
| 926 | } |
866 | 927 | } |
867 | 928 | |
868 | 929 | function updateGroups() |
diff --git a/binaries/data/mods/public/gui/session/session_objects/hero_icon.xml b/binaries/data/mods/public/gui/session/session_objects/hero_icon.xml
index 35d7053..6219006 100644
a
|
b
|
|
2 | 2 | <object |
3 | 3 | name="unitHeroPanel" |
4 | 4 | size="0 36 50 93" |
5 | | hidden="true" |
| 5 | hidden="false" |
6 | 6 | > |
7 | | <object name="unitHeroButton" size="0 0 50 50" type="button" style="iconButton" |
8 | | tooltip_style="sessionToolTip"> |
9 | | <object name="unitHeroImage" size="5 5 100%-5 100%-5" type="image" ghost="true"/> |
10 | | <object name="heroHitOverlay" hidden="true" type="image" ghost="true" size="5 5 100%-5 100%-5"/> |
11 | | </object> |
12 | | <!-- Hero Health bar --> |
13 | | <object size="3 100%-7 100%-3 100%-2" name="heroHealthSection" ghost="true"> |
14 | | <object size="0 0 100% 5" name="heroHealth" type="image" ghost="true"> |
15 | | <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/> |
16 | | <object type="image" sprite="healthBackground" ghost="true"/> |
17 | | <object type="image" sprite="healthForeground" ghost="true" name="heroHealthBar"/> |
18 | | <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/> |
| 7 | <repeat count="10" var="n"> |
| 8 | <object name="unitHeroButton[n]" size="0 0 50 50" type="button" hidden="true" style="iconButton" tooltip_style="sessionToolTip"> |
| 9 | <object name="unitHeroImage[n]" size="5 5 100%-5 100%-5" type="image" ghost="true"/> |
| 10 | <object name="heroHitOverlay[n]" hidden="true" type="image" ghost="true" size="5 5 100%-5 100%-5"/> |
| 11 | <!-- Hero Health bar --> |
| 12 | <object size="3 100%-7 100%-3 100%-2" name="heroHealthSection[n]" ghost="true"> |
| 13 | <object size="0 0 100% 5" name="heroHealth[n]" type="image" ghost="true"> |
| 14 | <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/> |
| 15 | <object type="image" sprite="healthBackground" ghost="true"/> |
| 16 | <object type="image" sprite="healthForeground" ghost="true" name="heroHealthBar[n]"/> |
| 17 | <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/> |
| 18 | </object> |
| 19 | </object> |
19 | 20 | </object> |
20 | | </object> |
| 21 | </repeat> |
21 | 22 | </object> |