Ticket #2162: 2162 - Rebased.patch

File 2162 - Rebased.patch, 17.5 KB (added by Stan, 8 years ago)

Rebased the original patch by boeseraupe.

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

     
    107107    return this.groups[templateName];
    108108};
    109109
     110/**
     111 * @return true if the group has at least one member
     112 */
     113EntityGroups.prototype.hasMembers = function()
     114{
     115    return Object.keys(this.groups).length != 0;
     116};
     117
    110118EntityGroups.prototype.getTotalCount = function()
    111119{
    112120    var totalCount = 0;
     
    463471function EntityGroupsContainer()
    464472{
    465473    this.groups = [];
     474    this.info = [];
     475    // Inits groups from 0 to 9
    466476    for (var i = 0; i < 10; ++i)
     477    {
    467478        this.groups[i] = new EntityGroups();
     479        var data = {}; // hitpoints, maxGroupHitPoints, numberOfUnits, initialNumberOfUnits, deathCycle, dirty, attack
     480        this.info[i] = data;
     481    }
    468482}
    469483
    470 EntityGroupsContainer.prototype.addEntities = function(groupName, ents)
     484/**
     485 * Adds entities to the group
     486 * @param groupID: id of the group
     487 * @param ents: array of entity IDs
     488 */
     489EntityGroupsContainer.prototype.addEntities = function(groupID, ents)
    471490{
    472491    for each (var ent in ents)
    473         for each (var group in this.groups)
     492        for (let gID in this.groups)
     493        {
     494            let group = this.groups[gID];
    474495            if (ent in group.ents)
     496            {
    475497                group.removeEnt(ent);
    476 
    477     this.groups[groupName].add(ents);
     498                 // Fade and icon should not be triggered if a unit comes to another group.
     499                this.removeEnt(gID, ent);
     500                break;
     501            }
     502        }
     503    // Add entities to group
     504    this.groups[groupID].add(ents);
     505    this.updateAdditionalData(groupID, ents, true);
    478506};
    479507
     508/**
     509 * Removes a entity from the info data
     510 * @param groupID: id of the group
     511 * @param ents: array of entity IDs
     512 */
     513EntityGroupsContainer.prototype.removeEnt = function(groupID, ent)
     514{
     515    if (!this.hasMembers(groupID))
     516        return;
     517    var entState = GetEntityState(+ent);
     518    if (!entState)
     519        return;
     520
     521    // update the data like this unit would never be in this group
     522    var data = this.info[groupID];
     523    data.hitpoints -= entState.hitpoints;
     524    data.maxGroupHitPoints -= entState.maxHitpoints;
     525    data.numberOfUnits--;
     526    data.initialNumberOfUnits--;
     527    data.dirty = true;
     528}
     529
     530/**
     531 * Updates the additional info of a group; can be called for init or update but should only be called, if needed.
     532 * @param groupID: id of the group
     533 * @param ents: array of entity IDs
     534 * @param init: true, if init
     535 */
     536EntityGroupsContainer.prototype.updateAdditionalData = function(groupID, ents, init)
     537{
     538    if (!this.hasMembers(groupID))
     539        return;
     540
     541    if (init)
     542    {
     543        let data = {};
     544        data.dirty = true; // True, if GUI should be updated
     545        data.attack = false; // True, if GUI should start color attack animation
     546        data.hitpoints = 0;
     547        data.maxGroupHitPoints = 0;
     548        data.numberOfUnits = ents.length;
     549        data.initialNumberOfUnits = data.numberOfUnits;
     550        data.deathCycle = 0; // Used for death icon
     551
     552        // Update the additional info
     553        if (data.numberOfUnits > 0)
     554            this.info[groupID] = data;
     555        else
     556            this.info[groupID] = {};
     557    }
     558
     559    // update the values needed for the GUI
     560    this.updateHealth(groupID);
     561    this.updateMaxHealth(groupID);
     562    this.updateMembers(groupID);
     563}
     564
     565/**
     566 * updates the current health points of a group
     567 * @param groupID: id of the group
     568 */
     569EntityGroupsContainer.prototype.updateHealth = function(groupID)
     570{
     571    if (!this.hasMembers(groupID))
     572        return;
     573
     574    var data = this.info[groupID];
     575    var hitpoints = 0;
     576    // Calculate health
     577    for (let ent in this.groups[groupID].ents)
     578    {
     579        let entState = GetEntityState(+ent);
     580        hitpoints += entState.hitpoints;
     581    }
     582
     583    // check, if attack is running.
     584    if (hitpoints < data.hitpoints)
     585        data.attack = true;
     586    // update prev points
     587    if (hitpoints != data.hitpoints)
     588    {
     589        data.hitpoints = hitpoints;
     590        data.dirty = true;
     591    }
     592}
     593
     594/**
     595 * Updates the member count of the group
     596 * @param groupID: id of the group
     597 */
     598EntityGroupsContainer.prototype.updateMembers = function(groupID)
     599{
     600    if (!this.hasMembers(groupID))
     601        return;
     602
     603    var data = this.info[groupID];
     604    var numberOfUnits = this.groups[groupID].getTotalCount();
     605    if (numberOfUnits < data.numberOfUnits)
     606    {
     607        // Update unit count
     608        data.numberOfUnits = numberOfUnits;
     609        data.deathCycle = 1; // Show death symbol
     610        // Update max health of the group
     611        this.updateMaxHealth(groupID);
     612        data.dirty = true;
     613    }
     614}
     615
     616/**
     617 * Updates the max health a group can have
     618 * @param groupID: id of the group
     619 */
     620EntityGroupsContainer.prototype.updateMaxHealth = function(groupID)
     621{
     622    if (!this.hasMembers(groupID))
     623        return;
     624
     625    var data = this.info[groupID];
     626    data.maxGroupHitPoints = 0;
     627    for (let ent in this.groups[groupID].ents)
     628    {
     629        let entState = GetEntityState(+ent);
     630        data.maxGroupHitPoints += entState.maxHitpoints;
     631    }
     632}
     633
     634/**
     635 * Updates the death cycle icon
     636 * @param groupID: id of the group
     637 */
     638EntityGroupsContainer.prototype.updateDeathCycle = function(groupID)
     639{
     640    if (!this.hasMembers(groupID))
     641        return;
     642
     643    var data = this.info[groupID];
     644    if (data.deathCycle > 0)
     645        data.deathCycle++;
     646    if (data.deathCycle >= g_groupDeathShowIconTicks)
     647        data.dirty = true;
     648}
     649
    480650EntityGroupsContainer.prototype.update = function()
    481651{
    482652    this.checkRenamedEntities();
    483     for each (var group in this.groups)
     653    for (let groupID in this.groups)
    484654    {
     655        // Nothing to do, if no members are there
     656        if (!this.hasMembers(groupID))
     657            continue;
     658
     659        let group = this.groups[groupID];
    485660        for (var ent in group.ents)
    486661        {
    487662            var entState = GetEntityState(+ent);
     
    489664            if (!entState)
    490665                group.removeEnt(ent);
    491666        }
     667        // update the values needed for the GUI
     668        this.updateHealth(groupID);
     669        this.updateMembers(groupID);
     670        this.updateDeathCycle(groupID);
    492671    }
    493672};
    494673
     
    505684        for each (var renamedEntity in renamedEntities)
    506685            renamedLookup[renamedEntity.entity] = renamedEntity.newentity;
    507686
    508         for each (var group in this.groups)
     687        for (let groupID in this.groups)
    509688        {
     689            let group = this.groups[groupID];
    510690            for each (var renamedEntity in renamedEntities)
    511691            {
    512692                // Reconstruct the group if at least one entity has been renamed.
     
    513693                if (renamedEntity.entity in group.ents)
    514694                {
    515695                    group.rebuildGroup(renamedLookup);
     696                    this.updateAdditionalData(groupID, Object.keys(group.ents), false);
    516697                    break;
    517698                }
    518699            }
     
    520701    }
    521702};
    522703
     704/**
     705 * Resets the initialNumberOfUnits
     706 * @param groupID: id of the group
     707 */
     708EntityGroupsContainer.prototype.resetMemberStatus = function(groupID)
     709{
     710    if (!this.hasMembers(groupID))
     711        return;
     712         
     713    var data = this.info[groupID];
     714    data.initialNumberOfUnits = data.numberOfUnits;
     715    data.dirty = true;
     716}
     717
     718/**
     719 * Checks if a group with that ID has members
     720 * @param groupID: id of the group
     721 */
     722EntityGroupsContainer.prototype.hasMembers = function(groupID)
     723{
     724    return this.groups[groupID].hasMembers();
     725}
     726
     727/**
     728 * Gets the dirty state for the GUI
     729 * @param groupID: id of the group
     730 */
     731EntityGroupsContainer.prototype.isDirty = function(groupID)
     732{
     733    return this.hasMembers(groupID) && this.info[groupID].dirty;
     734}
     735
     736/**
     737 * Sets the dirty state to false after the GUI redrawed the stuff
     738 * @param groupID: id of the group
     739 */
     740EntityGroupsContainer.prototype.resetDirty = function(groupID)
     741{
     742    if (!this.hasMembers(groupID))
     743        return;
     744
     745    this.info[groupID].dirty = false;
     746}
     747
     748/**
     749 * Gets the attack state for the GUI
     750 * @param groupID: id of the group
     751 */
     752EntityGroupsContainer.prototype.isAttacked = function(groupID)
     753{
     754    return this.hasMembers(groupID) && this.info[groupID].attack;
     755}
     756
     757/**
     758 * Sets the attack state to false after the color fade was started
     759 * @param groupID: id of the group
     760 */
     761EntityGroupsContainer.prototype.resetAttack = function(groupID)
     762{
     763    if (!this.hasMembers(groupID))
     764        return;
     765
     766    this.info[groupID].attack = false;
     767}
     768
     769/**
     770 * Sets the death cycle back to zero (off)
     771 * @param groupID: id of the group
     772 */
     773EntityGroupsContainer.prototype.resetDeathCycle = function(groupID)
     774{
     775    if (!this.hasMembers(groupID))
     776        return;
     777
     778    this.info[groupID].deathCycle = 0;
     779}
     780
    523781var g_Groups = new EntityGroupsContainer();
  • binaries/data/mods/public/gui/session/session.js

     
    6161// always through the list of all ongoing attacks...
    6262var g_previousHeroHitPoints = undefined;
    6363
     64// Simulation ticks until the deathIcon of a group is removed.
     65var g_groupDeathShowIconTicks = 60;
     66
    6467function GetSimState()
    6568{
    6669    if (!g_SimState)
     
    183186    gameSpeed.selected = gameSpeedIdx != -1 ? gameSpeedIdx : g_GameSpeeds.Default;
    184187    gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); };
    185188    initMenuPosition(); // set initial position
     189    initLayoutGroupButtons(); // Init group buttons
    186190
    187191    // Populate player selection dropdown
    188192    var playerNames = [];
     
    227231    //setTimeout(function() { reportPerformance(60); }, 60000);
    228232}
    229233
     234/** Inits the layout of the group buttons (adds spacers, labels and events to buttons)
     235 */
     236function initLayoutGroupButtons()
     237{
     238    var guiName = "Group";
     239    for (let i = 0; i < 10; ++i)
     240    {
     241        // add label
     242        let label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
     243
     244        // add button events and hide it
     245        let button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
     246        button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
     247        button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
     248        button.onpressright = (function(i) { return function() { performGroup("breakUp", i); } })(i);
     249        button.hidden = true;
     250        setPanelObjectPosition(button, i, 1);
     251        // get the members status bar and add a event
     252        let memberButton = Engine.GetGUIObjectByName("unit"+guiName+"MembersButton["+i+"]");
     253        memberButton.onpress = (function(i) { return function() { g_Groups.resetMemberStatus(i); } })(i);
     254    }
     255}
     256
    230257function selectViewPlayer(playerID)
    231258{
    232259    Engine.SetPlayerID(playerID);
     
    373400    var data = {};
    374401    data.playerAssignments = g_PlayerAssignments;
    375402    data.groups = g_Groups.groups;
     403    data.additionalGroupInfo = g_Groups.info;
    376404    // TODO: any other gui state?
    377405    return data;
    378406}
     
    392420    {
    393421        g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups;
    394422        g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents;
     423        g_Groups.info[groupNumber] = data.additionalGroupInfo[groupNumber];
     424        g_Groups.info[groupNumber].dirty = true;
    395425    }
    396426    updateGroups();
    397427}
     
    580610*/
    581611function updateGUIStatusBar(nameOfBar, points, maxPoints, direction)
    582612{
     613    // Check if values are valid
     614    if (!(points && maxPoints))
     615        return;
     616
    583617    // check, if optional direction parameter is valid.
    584618    if (!direction || !(direction >= 0 && direction < 4))
    585619        direction = 0;
     
    593627    var value = 100*Math.max(0, Math.min(1, points / maxPoints));
    594628   
    595629    // inverse bar
    596     if(direction == 2 || direction == 3)
     630    if (direction == 2 || direction == 3)
    597631        value = 100 - value;
    598632
    599     if(direction == 0)
     633    if (direction == 0)
    600634        healthSize.rright = value;
    601     else if(direction == 1)
     635    else if (direction == 1)
    602636        healthSize.rbottom = value;
    603     else if(direction == 2)
     637    else if (direction == 2)
    604638        healthSize.rleft = value;
    605     else if(direction == 3)
     639    else if (direction == 3)
    606640        healthSize.rtop = value;
    607641   
    608642    // update bar
     
    609643    statusBar.size = healthSize;
    610644}
    611645
     646/**
     647 * Changes the visibility of a status bar
     648 * @param nameOfBar: name of the bar
     649 * @param hide: true, if bar should be hidden
     650 */
     651function changeVisibilityStatusBar(nameOfBar, hide)
     652{
     653    // Get the bar and update it
     654    var statusBar = Engine.GetGUIObjectByName(nameOfBar);
     655    if (!statusBar)
     656        return;
    612657
     658    statusBar.hidden = hide;
     659}
     660
    613661function updateHero()
    614662{
    615663    var playerState = GetSimState().players[Engine.GetPlayerID()];
     
    665713    g_previousHeroHitPoints = heroState.hitpoints;
    666714}
    667715
    668 
     716/** Update groups on the GUI.
     717 */
    669718function updateGroups()
    670719{
    671720    var guiName = "Group";
    672721    g_Groups.update();
    673     for (var i = 0; i < 10; i++)
     722    for (let i = 0; i < 10; ++i)
    674723    {
    675         var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
    676         var label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
    677         if (g_Groups.groups[i].getTotalCount() == 0)
     724        let button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
     725        // check, if group has members
     726        if (!g_Groups.hasMembers(i))
     727        {
    678728            button.hidden = true;
     729            stopColorFade("unitGroupHitOverlay["+i+"]");
     730            let deathIcon = Engine.GetGUIObjectByName("unitGroupIconDeath["+ i +"]");
     731            deathIcon.hidden = true;
     732            continue; // nothing to do, if group has no members
     733        }
    679734        else
    680             button.hidden = false;
    681         button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
    682         button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
    683         button.onpressright = (function(i) { return function() { performGroup("breakUp", i); } })(i);
    684         setPanelObjectPosition(button, i, 1);
     735            button.hidden = false; // make button visible
     736
     737        // check, if status bars must be redrawn
     738        if (!g_Groups.isDirty(i))
     739        continue;
     740
     741        // update the status bars
     742        let data = g_Groups.info[i];
     743        updateGUIStatusBar("unitGroupHealthBar["+ i +"]", data.hitpoints, data.maxGroupHitPoints);
     744
     745        if(data.initialNumberOfUnits == 1)
     746            changeVisibilityStatusBar("unitGroupMembersButton["+ i +"]", true);
     747        else
     748        {
     749            changeVisibilityStatusBar("unitGroupMembersButton["+ i +"]", false);
     750            updateGUIStatusBar("unitGroupMembersBar["+ i +"]", data.numberOfUnits, data.initialNumberOfUnits, 3);
     751        }
     752
     753        // check, if the attack should be started
     754        if (g_Groups.isAttacked(i))
     755        {
     756            startColorFade("unitGroupHitOverlay["+i+"]", 100, 10000, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
     757            g_Groups.resetAttack(i);
     758        }
     759
     760        // check, if the death symbol should be triggered
     761        if (data.deathCycle > 0)
     762        {
     763            let deathIcon = Engine.GetGUIObjectByName("unitGroupIconDeath["+ i +"]");
     764            // show the symbol
     765            if (data.deathCycle >= g_groupDeathShowIconTicks)
     766            {
     767                deathIcon.hidden = true;
     768                g_Groups.resetDeathCycle(i);
     769            }
     770            else if (data.deathCycle >= 1)
     771                deathIcon.hidden = false;
     772        }
     773
     774        // reset the dirty state after the updates are complete
     775        g_Groups.resetDirty(i);
    685776    }
    686777}
    687778
  • binaries/data/mods/public/gui/session/session_objects/selection_group_icons.xml

     
    77        <object name="unitGroupButton[n]" size="0 0 36 36" type="button" hidden="false" style="iconButton" tooltip_style="sessionToolTipBottomBold">
    88            <translatableAttribute id="tooltip">Click to select grouped units, double-click to focus the grouped units and right-click to disband the group.</translatableAttribute>
    99            <object name="unitGroupIcon[n]" size="3 3 33 33" type="image" sprite="groupsIcon" ghost="true"/>
     10            <object name="unitGroupHitOverlay[n]" hidden="true" type="image" ghost="true" size="3 3 33 33"/>
    1011            <object name="unitGroupLabel[n]" type="text" style="largeCenteredOutlinedText" ghost="true"/>
     12            <object name="unitGroupIconDeath[n]" size="21 3 35 17" type="image" sprite="killIcon" hidden="true" ghost="true"/>
     13            <!-- Group Health bar -->
     14            <object size="2 37 34 40" name="unitGroupHealthSection[n]">
     15                <object size="0 0 100% 4" name="unitGroupHealth[n]" ghost="true" type="image">
     16                <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
     17                <object type="image" sprite="healthBackground" ghost="true"/>
     18                    <object type="image" sprite="healthForeground" ghost="true" name="unitGroupHealthBar[n]"/>
     19                    <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/>
     20                </object>
     21            </object>
     22            <!-- Group Health bar -->
     23            <object size="37 2 40 34" name="unitGroupMembersButton[n]" type="button" tooltip_style="sessionToolTipBottomBold"
     24                tooltip="Click to reset unit counter bar.">
     25                <object size="0 0 4 100%" name="unitGroupMembers[n]" ghost="true" type="image">
     26                <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
     27                <object type="image" sprite="groupSizeBackground" ghost="true"/>
     28                    <object type="image" sprite="groupSizeForeground" ghost="true" name="unitGroupMembersBar[n]"/>
     29                <object type="image" sprite="statsBarShaderVertical" ghost="true"/>
     30                </object>
     31            </object>
    1132        </object>
    1233    </repeat>
    1334</object>
  • binaries/data/mods/public/gui/session/sprites.xml

     
    99        />
    1010    </sprite>
    1111
     12    <sprite name="killIcon">
     13        <image
     14            texture="session/icons/kill.png"
     15            size="0 0 100% 100%"
     16        />
     17    </sprite>
     18
     19    <sprite name="groupSizeForeground">
     20        <image backcolor="255 184 0 255"/>
     21    </sprite>
     22
     23    <sprite name="groupSizeBackground">
     24        <image backcolor="184 184 184 255"/>
     25    </sprite>
     26
    1227    <!-- ================================  ================================ -->
    1328    <!-- Menu -->
    1429    <!-- ================================  ================================ -->