Ticket #3000: heropanel4.patch

File heropanel4.patch, 9.8 KB (added by Stephen Imhoff, 8 years ago)

remove separate free-slot array

  • binaries/data/mods/public/gui/session/session.js

    From 6fa0e69334ec1a629c615012cc7310dd4f0f4e0c 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   | 165 +++++++++++++++------
     .../gui/session/session_objects/hero_icon.xml      |  29 ++--
     2 files changed, 135 insertions(+), 59 deletions(-)
    
    diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js
    index f944549..6b62cb3 100644
    a b const g_DefaultPopulationColor = "white";  
    88const g_PopulationAlertColor = "orange";
    99
    1010/**
     11 * Maximum number of heroes to display (must stay in sync with hero_icon.xml).
     12 */
     13const g_HeroPanelCount = 10;
     14
     15/**
    1116 * A random file will be played. TODO: more variety
    1217 */
    1318const g_Ambient = [ "audio/ambient/dayscape/day_temperate_gen_03.ogg" ];
    var g_ShowGuarded = false;  
    143148var g_AdditionalHighlight = [];
    144149
    145150/**
    146  * Blink the hero selection if that entity has lost health since the last turn.
     151 * Previous set of heroes to display.
    147152 */
    148 var g_PreviousHeroHitPoints;
     153var g_PreviousHeroes = [];
    149154
    150155/**
    151156 * Unit classes to be checked for the idle-worker-hotkey.
    function init(initData, hotloadData)  
    271276    gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); };
    272277    initMenuPosition();
    273278
     279    // Initialize heroes and buttons.
     280    for (let i = 0; i < g_HeroPanelCount; ++i)
     281    {
     282        // "Save" off the value to deal with the closure.
     283        let slot = i;
     284        let button = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]");
     285        button.onPress = function()
     286        {
     287            let hero = g_PreviousHeroes[slot];
     288            if (!hero)
     289                return;
     290
     291            if (!Engine.HotkeyIsPressed("selection.add"))
     292                g_Selection.reset();
     293            g_Selection.addList([hero.ent]);
     294        };
     295
     296        button.onDoublePress = function() {
     297            let hero = g_PreviousHeroes[slot];
     298            if (hero)
     299                selectAndMoveTo(getEntityOrHolder(hero.ent));
     300        };
     301    }
     302
    274303    // Populate player selection dropdown
    275304    let playerNames = [translate("Observer")];
    276305    let playerIDs = [-1];
    function updateGUIObjects()  
    712741    if (g_ShowGuarding || g_ShowGuarded)
    713742        updateAdditionalHighlight();
    714743
    715     updateHero();
     744    updateHeroes();
    716745    updateGroups();
    717746    updateDebug();
    718747    updatePlayerDisplay();
    function updateGUIStatusBar(nameOfBar, points, maxPoints, direction)  
    788817}
    789818
    790819
    791 function updateHero()
     820function updateHeroes()
    792821{
    793     let unitHeroPanel = Engine.GetGUIObjectByName("unitHeroPanel");
    794     let heroButton = Engine.GetGUIObjectByName("unitHeroButton");
    795 
    796822    let playerState = GetSimState().players[g_ViewedPlayer];
    797     if (!playerState || playerState.heroes.length <= 0)
     823    if (!playerState || playerState.heroes.length == 0)
    798824    {
    799         g_PreviousHeroHitPoints = undefined;
    800         unitHeroPanel.hidden = true;
     825        if (g_PreviousHeroes.length > 0)
     826        {
     827            g_PreviousHeroes.forEach((hero, slot) => {
     828                if (hero)
     829                    Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]").hidden = true;
     830            });
     831            g_PreviousHeroes = [];
     832        }
    801833        return;
    802834    }
    803835
    804     let heroImage = Engine.GetGUIObjectByName("unitHeroImage");
    805     let heroState = GetExtendedEntityState(playerState.heroes[0]);
    806     let template = GetTemplateData(heroState.template);
    807     heroImage.sprite = "stretched:session/portraits/" + template.icon;
    808     let hero = playerState.heroes[0];
    809 
    810     heroButton.onpress = function()
     836    let mapped = playerState.heroes.map(ent => {
     837        // Retain current ordering, if displayed previously.
     838        // This does a linear search through the array,
     839        // but the multiple-hero case is extremely rare.
     840        let previous = g_PreviousHeroes.find(hero => hero && hero.ent == ent);
     841        if (previous)
     842            return {
     843                "ent": ent,
     844                "index": previous.index,
     845                "previous": previous
     846            };
     847
     848        // Place new heroes at the end of the list.
     849        return {
     850            "ent": ent,
     851            "index": g_HeroPanelCount
     852        };
     853    }).sort((l, r) => l.index == r.index ? l.ent - r.ent : l.index - r.index);
     854
     855    let heroes = new Array(g_HeroPanelCount).fill(undefined);
     856
     857    for (let displayIndex = 0; displayIndex < Math.min(g_HeroPanelCount, mapped.length); ++displayIndex)
    811858    {
    812         if (!Engine.HotkeyIsPressed("selection.add"))
    813             g_Selection.reset();
    814         g_Selection.addList([hero]);
    815     };
    816     heroButton.ondoublepress = function() { selectAndMoveTo(getEntityOrHolder(hero)); };
    817     unitHeroPanel.hidden = false;
     859        let hero = mapped[displayIndex];
     860
     861        // Find the first unused slot if new, otherwise reuse previous.
     862        let slot = hero.previous ?
     863            hero.previous.slot :
     864            heroes.findIndex(slot => !slot);
     865
     866        let heroButton = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]");
     867        let heroState = GetExtendedEntityState(hero.ent);
     868        let template = GetTemplateData(heroState.template);
     869
     870        if (!hero.previous)
     871        {
     872            // Ensure no pre-existing animations.
     873            stopColorFade("heroHitOverlay[" + slot + "]");
     874            let heroImage = Engine.GetGUIObjectByName("unitHeroImage[" + slot + "]");
     875            heroImage.sprite = "stretched:session/portraits/" + template.icon;
    818876
     877            heroButton.hidden = false;
     878        }
     879
     880        heroButton.tooltip = createHeroTooltip(heroState, template);
     881        updateGUIStatusBar("heroHealthBar[" + slot + "]",
     882                heroState.hitpoints, heroState.maxHitpoints);
     883
     884        // The "slot" is which button, but it's the identity (name) of the button, not its position.
     885        // Heroes stay in the same slot, but the position of the button may change.
     886        // TODO: Instead of instant position changes, animate button movement.
     887        setPanelObjectPosition(heroButton, displayIndex, g_HeroPanelCount);
     888
     889        // If the health of the hero changed since the last update, trigger the animation.
     890        if (hero.previous && hero.previous.hitpoints > heroState.hitpoints)
     891            startColorFade("heroHitOverlay[" + slot + "]", 100, 0,
     892                    colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
     893
     894        heroes[slot] = {
     895            "ent": hero.ent,
     896            "slot": slot,
     897            "index": displayIndex,
     898            "hitpoints": heroState.hitpoints
     899        };
     900    }
     901
     902    // In case heroes were removed and their slot/button was freed, hide it.
     903    g_PreviousHeroes.forEach((hero, slot) => {
     904        if (!heroes[slot])
     905            Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]").hidden = true;
     906    });
     907
     908    g_PreviousHeroes = heroes;
     909}
     910
     911function createHeroTooltip(heroState, template)
     912{
    819913    // Setup tooltip
    820914    let tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]";
    821     let healthLabel = "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]";
    822     tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), {
    823         "label": healthLabel,
     915    tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), {
     916        "label": "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]",
    824917        "current": Math.ceil(heroState.hitpoints),
    825918        "max": Math.ceil(heroState.maxHitpoints)
    826919    });
    function updateHero()  
    830923    tooltip += "\n" + getArmorTooltip(heroState.armour);
    831924    if (template.tooltip)
    832925        tooltip += "\n" + template.tooltip;
    833 
    834     heroButton.tooltip = tooltip;
    835 
    836     // update heros health bar
    837     updateGUIStatusBar("heroHealthBar", heroState.hitpoints, heroState.maxHitpoints);
    838 
    839     let heroHP = {
    840         "hitpoints": heroState.hitpoints,
    841         "player": g_ViewedPlayer
    842     };
    843 
    844     if (!g_PreviousHeroHitPoints)
    845         g_PreviousHeroHitPoints = heroHP;
    846 
    847     // if the health of the hero changed since the last update, trigger the animation
    848     if (g_PreviousHeroHitPoints.player == heroHP.player && g_PreviousHeroHitPoints.hitpoints > heroHP.hitpoints)
    849         startColorFade("heroHitOverlay", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
    850 
    851     g_PreviousHeroHitPoints = heroHP;
     926    return tooltip;
    852927}
    853928
    854929function updateGroups()
  • binaries/data/mods/public/gui/session/session_objects/hero_icon.xml

    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  
    22<object
    33    name="unitHeroPanel"
    44    size="0 36 50 93"
    5     hidden="true"
     5    hidden="false"
    66>
    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>
    1920        </object>
    20     </object>
     21    </repeat>
    2122</object>