Ticket #2162: group_health_patch_v2.2.patch

File group_health_patch_v2.2.patch, 30.0 KB (added by Michael, 10 years ago)

resolved confict with current SVN version.

  • binaries/data/mods/public/gui/common/colorFades.js

     
    11/*
    2     DESCRIPTION : Some functions to make colour fades on GUI elements (f.e. used for hero and group icons)
     2    DESCRIPTION : Some functions to make color fades on GUI elements (f.e. used for hero and group icons)
    33    NOTES       :
    44*/
    55
    6 // Used for storing object names of running color fades in order to stop them, if the fade is restarted before the old ended
    7 var g_colorFade = {};
    8 g_colorFade["id"] = {};
    9 g_colorFade["tick"] = {};
     6// Used for storing information about color fades
     7var g_colorFade = {};
    108
    119/**
    12  * starts fading a colour of a GUI object using the sprite argument
    13  * name: name of the object which colour should be faded
    14  * changeInterval: interval in ms when the next colour change should be made
    15  * duration: maximal duration of the complete fade
    16  * colour: RGB + opacity object with keys r,g,b and o
     10 * returns the init RGB color setting
     11 */
     12function getInitColorFadeRGB()
     13{
     14    var rgb = {};
     15    rgb.r = 0;
     16    rgb.g = 0;
     17    rgb.b = 0;
     18    rgb.o = 100;
     19    return rgb;
     20}
     21
     22/**
     23 * starts fading a color of a GUI object using the sprite argument
     24 * name: name of the object which color should be faded
     25 * tickInterval: interval in ms when the next color change should be made
     26 * duration: maximal duration of the complete fade (if 0 it runs until it is stopped)
    1727 * fun_colorTransform: function which transform the colors;
    18  *                    arguments: [colour object, tickCounter]
     28 *                     arguments: [var data]
     29 * restartAble [optional: if false, the fade can not be restarted; default: true
    1930 * fun_smoothRestart [optional]: a function, which returns a smooth tick counter, if the fade should be started;
    20  *                              arguments: [tickCounter of current fade; not smaller than 1 or it restarts at 0] returns: smooth tick counter value
    21  * tickCounter [optional]: should not be set by hand! - how often the function was called recursively
     31 *                               arguments: [var data]; must return false, if smooth restart was not possible and true, if it was ok
    2232 */
    23 function fadeColour(name, changeInterval, duration, colour, fun_colorTransform, fun_smoothRestart, tickCounter)
     33function startColorFade(name, tickInterval, duration, fun_colorTransform, restartAble, fun_smoothRestart) // TODO: replace restartAble = true, fun_smoothRestart = null when it is supported (spiderMonkey upgrade #1886)
    2434{
    2535    // get the overlay
    2636    var overlay = Engine.GetGUIObjectByName(name);
     
    2838        return;
    2939
    3040    // check, if fade overlay was started just now
    31     if (!tickCounter)
     41    if (!isColorFadeRunning(name))
    3242    {
    33         tickCounter = 1;
    3443        overlay.hidden = false;
    35                    
    36         // check, if another animation is running and restart it, if it's the case
    37         if (isColourFadeRunning(name))
    38         {
    39             restartColourFade(name, changeInterval, duration, colour, fun_colorTransform, fun_smoothRestart, g_colorFade.tick[name]);
    40             return;
    41         }
     44       
     45        // store the values into a var to make it more flexible (can be changed from every function)
     46        var data = {};
     47        data.timerId = -1;
     48        data.tickInterval = tickInterval;
     49        data.duration = duration;
     50        data.fun_colorTransform = fun_colorTransform;
     51        data.restartAble = restartAble !== false;  // TODO: ' !== false' and add default parameter
     52        data.fun_smoothRestart = fun_smoothRestart;
     53        data.tickCounter = 0;
     54        data.runsUntilStop = duration == 0;
     55        data.stopFade = false;
     56        data.rgb = getInitColorFadeRGB();
     57
     58        // store it!
     59        g_colorFade[name] = data;
     60
     61        // start with fading
     62        fadeColorTick(name);
    4263    }
     64    else if (restartAble)
     65    {
     66        restartColorFade(name, tickInterval, duration, fun_colorTransform, restartAble, fun_smoothRestart);
     67        return;
     68    }
     69}
     70
     71/**
     72 * makes the color changes in a tick
     73 * name: name of the object which color should be faded
     74 */
     75function fadeColorTick(name)
     76{
     77    // make some checks
     78    if (!isColorFadeRunning(name))
     79        return;
     80       
     81    var overlay = Engine.GetGUIObjectByName(name);
     82    if (!overlay)
     83        return;
     84    var data = g_colorFade[name];
     85       
     86    // change the color
     87    data.fun_colorTransform(data);
    4388   
    44     // get colors
    45     fun_colorTransform(colour, tickCounter);
    46    
    47     // set new colour
    48     overlay.sprite="colour: "+colour.r+" "+colour.g+" "+colour.b+" "+colour.o;
     89    // set new color
     90    var rgb = data.rgb;
     91    overlay.sprite="colour: "+rgb.r+" "+rgb.g+" "+rgb.b+" "+rgb.o;
    4992
    5093    // recusive call, if duration is positive
    51     duration-= changeInterval;
    52     if (duration > 0 && colour.o > 0)
     94    if (!data.stopFade && (data.runsUntilStop || data.duration - (data.tickInterval * data.tickCounter) > 0))
    5395    {
    54         var id = setTimeout(function() { fadeColour(name, changeInterval, duration, colour, fun_colorTransform, fun_smoothRestart, ++tickCounter); }, changeInterval);
    55         g_colorFade.id[name] = id;
    56         g_colorFade.tick[name] = tickCounter;
     96        var id = setTimeout(function() { fadeColorTick(name); }, data.tickInterval);
     97        data.timerId = id;
     98        data.tickCounter++;
    5799    }
    58100    else
    59101    {
    60102        overlay.hidden = true;
    61         stopColourFade(name);
     103        stopColorFade(name);
    62104    }
    63105}
    64106
    65 
    66107/**
    67  * checks, if a colour fade on that object is running
    68  * name: name of the object which colour fade should be checked
     108 * checks, if a color fade on that object is running
     109 * name: name of the object which color fade should be checked
    69110 * return: true a running fade was found
    70111 */
    71 function isColourFadeRunning(name)
     112function isColorFadeRunning(name)
    72113{
    73     return name in g_colorFade.id;
     114    return name in g_colorFade;
    74115}
    75116
    76117/**
    77  * stops fading a colour
    78  * name: name of the object which colour fade should be stopped
    79  * hideOverlay: hides the overlay, if true
     118 * stops fading a color
     119 * name: name of the object which color fade should be stopped
     120 * hideOverlay [optional]: hides the overlay, if true [default: true]
    80121 * return: true a running fade was stopped
    81122 */
    82 function stopColourFade(name, hideOverlay)
     123function stopColorFade(name, hideOverlay) //TODO: add 'hideOverlay = true' when its supported (spiderMonkey upgrade #1886)
    83124{
    84     // check, if a colour fade is running
    85     if (!isColourFadeRunning(name))
     125    // check, if a color fade is running
     126    if (!isColorFadeRunning(name))
    86127        return false;
    87128
    88129    // delete the timer
    89     clearTimeout(g_colorFade.id[name]);
    90     delete g_colorFade.id[name];
    91     delete g_colorFade.tick[name];
    92    
     130    clearTimeout(g_colorFade[name].timerId);
     131    delete g_colorFade[name];
     132
     133    hideOverlay = hideOverlay !== false; // TODO: remove this line and add default value for hideOverlay
    93134    // get the overlay and hide it
    94135    if (hideOverlay)
    95136    {
    96137        var overlay = Engine.GetGUIObjectByName(name);
    97         if(overlay)
     138        if (overlay)
    98139            overlay.hidden = true;
    99140    }
    100141    return true;
     
    101142}
    102143
    103144/**
    104  * restarts a colour fade
    105  * see paramter in fadeColour function
     145 * restarts a color fade
     146 * see paramter in startColorFade function
    106147 */
    107 function restartColourFade(name, changeInterval, duration, colour, fun_colorTransform, fun_smoothRestart, tickCounter)
     148function restartColorFade(name)
    108149{
    109     // check, if a colour fade is running
    110     if (!isColourFadeRunning(name))
     150    // check, if a color fade is running
     151    if (!isColorFadeRunning(name))
    111152        return false;
    112153   
     154    var data = g_colorFade[name];
    113155    // check, if fade can be restarted smoothly
    114     if (fun_smoothRestart)
     156    if (data.fun_smoothRestart)
    115157    {
    116         tickCounter = fun_smoothRestart(colour, tickCounter);
    117         // set new function to existing timer
    118         var fun = function() { fadeColour(name, changeInterval, duration, colour, fun_colorTransform, fun_smoothRestart, tickCounter); };
    119         setNewTimerFunction(g_colorFade.id[name], fun);
     158        // if call was too late
     159        if (!data.fun_smoothRestart(data))
     160        {
     161            data.rgb = getInitColorFadeRGB(); // set RGB start values
     162            data.tickCounter = 0;
     163        }
    120164    }
    121165    // stop it and restart it
    122166    else
    123167    {
    124         stopColourFade(name, true);
    125         fadeColour(name, changeInterval, duration, colour, fun_colorTransform);
     168        stopColorFade(name, false);
     169        startColorFade(name, data.changeInterval, data.duration, data.fun_colorTransform, data.restartAble, data.fun_smoothRestart);
    126170    }
    127171    return true;
    128172}
     
    133177
    134178var g_fadeAttackUnit = {};
    135179g_fadeAttackUnit.blinkingTicks = 50; // how many ticks should first blinking phase be
    136 g_fadeAttackUnit.blinkingChangeInterval = 5; // how often should the colour be changed during the blinking phase
    137 g_fadeAttackUnit.gbColourChangeRate = 3; // how fast should blue and green part of the colour change
     180g_fadeAttackUnit.blinkingChangeInterval = 5; // how often should the color be changed during the blinking phase
     181g_fadeAttackUnit.gbcolorChangeRate = 3; // how fast should blue and green part of the color change
    138182g_fadeAttackUnit.fadeOutStart = 100; // when should the fade out start using the opacity
    139183g_fadeAttackUnit.opacityChangeRate = 3; // how fast should opacity change
    140184
    141 /**
    142  * rgb: colour object with keys r,g,b and o
    143  * tickCounter: how often the fade was executed
    144  */
    145 function colourFade_attackUnit(rgb, tickCounter)
     185function colorFade_attackUnit(data)
    146186{
     187    var rgb = data.rgb;
     188   
     189    // init color
     190    if (data.tickCounter == 0)
     191        rgb.r = 175;
    147192    // blinking
    148     if (tickCounter < g_fadeAttackUnit.blinkingTicks)
     193    if (data.tickCounter < g_fadeAttackUnit.blinkingTicks)
    149194    {
    150195        // slow that process down
    151         if (tickCounter % g_fadeAttackUnit.blinkingChangeInterval != 0)
     196        if (data.tickCounter % g_fadeAttackUnit.blinkingChangeInterval != 0)
    152197            return;
    153198           
    154         rgb.g = rgb.g == 0 ? 255 : rgb.g = 0;
    155         rgb.b = rgb.b == 0 ? 255 : rgb.b = 0;
     199        rgb.g = rgb.g == 0 ? 255 : 0;
    156200    }
    157     // wait a short time and then colour fade from red to grey to nothing
    158     else if ( tickCounter >= g_fadeAttackUnit.blinkingTicks + g_fadeAttackUnit.blinkingChangeInterval)
     201    // wait a short time and then color fade from red to grey to nothing
     202    else if ( data.tickCounter >= g_fadeAttackUnit.blinkingTicks + g_fadeAttackUnit.blinkingChangeInterval)
    159203    {
    160         rgb.g = rgb.g < 255 ? rgb.g += g_fadeAttackUnit.gbColourChangeRate * Math.sqrt(tickCounter - g_fadeAttackUnit.blinkingTicks) : 255;
    161         rgb.b = rgb.g;
     204        rgb.g = rgb.g < 255 ? rgb.g += g_fadeAttackUnit.gbcolorChangeRate * Math.sqrt(data.tickCounter - g_fadeAttackUnit.blinkingTicks) : 255;
    162205       
    163206        // start with fading it out
    164207        if (rgb.g > g_fadeAttackUnit.fadeOutStart)
    165208            rgb.o = rgb.o > g_fadeAttackUnit.opacityChangeRate ? rgb.o -= g_fadeAttackUnit.opacityChangeRate : 0;
     209        // check for end
     210        if (rgb.o == 0)
     211            data.stopFade = true;
    166212    }
     213    rgb.b = rgb.g;
    167214}
    168215
    169 /**
    170  * makes a smooth fade, if the attack on the unit has not stopped yet
    171  * rgb: colour object with keys r,g,b and o
    172  * tickCounter: how often the fade was executed
    173  */
    174 function smoothColourFadeRestart_attackUnit(rgb, tickCounter)
     216function smoothColorFadeRestart_attackUnit(data)
    175217{
    176218    // check, if in blinking phase
    177     if (tickCounter < g_fadeAttackUnit.blinkingTicks)
     219    if (data.tickCounter < g_fadeAttackUnit.blinkingTicks)
    178220    {
    179         // get rgb to current state
    180         for (var i = 1; i <= tickCounter; i++)
    181             colourFade_attackUnit(rgb, i);
    182         // set the tick counter back to start
    183         return (tickCounter % (g_fadeAttackUnit.blinkingChangeInterval * 2)) + 1;
     221        data.tickCounter = data.tickCounter % (g_fadeAttackUnit.blinkingChangeInterval * 2);
     222        data.rgb.o = getInitColorFadeRGB().o;
     223        return true;
    184224    }
    185     return 1;
     225    return false;
    186226}
    187227
    188228//[END] of hero fade functions
     229 No newline at end of file
  • binaries/data/mods/public/gui/session/selection.js

     
    106106    return this.groups[templateName];
    107107};
    108108
     109EntityGroups.prototype.hasMembers = function()
     110{
     111    return Object.keys(this.groups).length != 0;
     112};
     113
    109114EntityGroups.prototype.getTotalCount = function()
    110115{
    111116    var totalCount = 0;
     
    442447function EntityGroupsContainer()
    443448{
    444449    this.groups = [];
     450    this.info = [];
     451    // init groups 0 to 9
    445452    for (var i = 0; i < 10; ++i)
    446453    {
    447454        this.groups[i] = new EntityGroups();
     455        var data = {}; // hitpoints, maxGroupHitPoints, numberOfUnits, initialNumberOfUnits, deathCycle, dirty, attack
     456        this.info[i] = data;
    448457    }
    449458}
    450459
    451 EntityGroupsContainer.prototype.addEntities = function(groupName, ents)
     460/**
     461 * adds entities to the group
     462 * groupID: id of the group
     463 * ents: array of entity IDs
     464 */
     465EntityGroupsContainer.prototype.addEntities = function(groupID, ents)
    452466{
    453467    for each (var ent in ents)
    454468    {
    455         for each (var group in this.groups)
     469        // remove the entity out of all other groups
     470        for (var gID in this.groups)
    456471        {
     472            var group = this.groups[gID];
    457473            if (ent in group.ents)
    458474            {
    459475                group.removeEnt(ent);
     476                this.removeEnt(gID, ent); // is used because fade and icon should not be triggered if a unit comes to another group
     477                break;
    460478            }
    461479        }
    462480    }
    463     this.groups[groupName].add(ents);
     481    // add entities to group
     482    this.groups[groupID].add(ents);
     483    this.updateAdditionalData(groupID, ents, true);
    464484}
    465485
     486/**
     487 * removes a entity from the info data
     488 * groupID: id of the group
     489 * ent: entity ID
     490 */
     491EntityGroupsContainer.prototype.removeEnt = function(groupID, ent)
     492{
     493    if (!this.hasMembers(groupID))
     494        return;
     495    var entState = GetEntityState(+ent);
     496    if (!entState)
     497        return;
     498   
     499    // update the data like this unit would never be in this group
     500    var data = this.info[groupID]; 
     501    data.hitpoints -= entState.hitpoints;
     502    data.maxGroupHitPoints -= entState.maxHitpoints;
     503    data.numberOfUnits--;
     504    data.initialNumberOfUnits--;
     505    data.dirty = true;
     506    warn(JSON.stringify(data));
     507}
     508
     509/**
     510 * updates the additional info of a group; can be called for init or update but should only be called, if needed
     511 * groupID: id of the group
     512 * ents: array of entity IDs
     513 * init: true, if init
     514 */
     515EntityGroupsContainer.prototype.updateAdditionalData = function(groupID, ents, init)
     516{
     517    if (!this.hasMembers(groupID))
     518        return;
     519
     520    if (init)
     521    {
     522        var data = {};
     523        data.dirty = true; // true, if GUI should be updated
     524        data.attack = false; // true, if GUI should start color attack animation
     525        data.hitpoints = 0;
     526        data.maxGroupHitPoints = 0;
     527        data.numberOfUnits = ents.length;
     528        data.initialNumberOfUnits = data.numberOfUnits;
     529        data.deathCycle = 0; // used for death icon
     530       
     531        // update the additional info
     532        if (data.numberOfUnits > 0)
     533            this.info[groupID] = data;
     534        else
     535            this.info[groupID] = {};
     536    }
     537
     538    // update the values needed for the GUI
     539    this.updateHealth(groupID);
     540    this.updateMaxHealth(groupID);
     541    this.updateMembers(groupID);
     542}
     543
     544/**
     545 * updates the current health points of a group
     546 * groupID: id of the group
     547 */
     548EntityGroupsContainer.prototype.updateHealth = function(groupID)
     549{
     550    if (!this.hasMembers(groupID))
     551        return;
     552       
     553    var data = this.info[groupID]; 
     554    var hitpoints = 0;
     555    // calculate health
     556    for (var ent in this.groups[groupID].ents)
     557    {
     558        var entState = GetEntityState(+ent);
     559        hitpoints += entState.hitpoints;
     560    }
     561
     562    // check, if attack is running.
     563    if (hitpoints < data.hitpoints)
     564        data.attack = true;
     565    // update prev points
     566    if (hitpoints != data.hitpoints)
     567    {
     568        data.hitpoints = hitpoints;
     569        data.dirty = true;
     570    }
     571}
     572
     573/**
     574 * updates the member count of the group
     575 * groupID: id of the group
     576 */
     577EntityGroupsContainer.prototype.updateMembers = function(groupID)
     578{
     579    if (!this.hasMembers(groupID))
     580        return;
     581
     582    var data = this.info[groupID];
     583    var numberOfUnits = this.groups[groupID].getTotalCount();
     584    if (numberOfUnits < data.numberOfUnits)
     585    {
     586        // update unit count
     587        data.numberOfUnits = numberOfUnits;
     588        data.deathCycle = 1; // show death symbol
     589        // update max health of the group
     590        this.updateMaxHealth(groupID);
     591        data.dirty = true;
     592    }   
     593}
     594
     595/**
     596 * updates the max health a group can have
     597 * groupID: id of the group
     598 */
     599EntityGroupsContainer.prototype.updateMaxHealth = function(groupID)
     600{
     601    if (!this.hasMembers(groupID))
     602        return;
     603
     604    var data = this.info[groupID];
     605    data.maxGroupHitPoints = 0;
     606    for (var ent in this.groups[groupID].ents)
     607    {
     608        var entState = GetEntityState(+ent);
     609        data.maxGroupHitPoints += entState.maxHitpoints;
     610    }
     611}
     612
     613/**
     614 * updates the death cycle icon
     615 * groupID: id of the group
     616 */
     617EntityGroupsContainer.prototype.updateDeathCycle = function(groupID)
     618{
     619    if (!this.hasMembers(groupID))
     620        return;
     621
     622    var data = this.info[groupID];
     623    if (data.deathCycle > 0)
     624        data.deathCycle++;
     625    if (data.deathCycle >= g_groupDeathShowIconTicks)
     626        data.dirty = true;
     627}
     628
     629/**
     630 * checks, if some entities where removed or renamed
     631 */
    466632EntityGroupsContainer.prototype.update = function()
    467633{
    468634    this.checkRenamedEntities();
    469     for each (var group in this.groups)
     635    for (var groupID in this.groups)
    470636    {
     637        // nothing to do, if no members are there
     638        if (!this.hasMembers(groupID))
     639            continue;
     640           
     641        var group = this.groups[groupID];
    471642        for (var ent in group.ents)
    472643        {
    473644            var entState = GetEntityState(+ent);
     
    474645
    475646            // Remove deleted units
    476647            if (!entState)
    477             {
    478648                group.removeEnt(ent);
    479             }
    480649        }
     650        // update the values needed for the GUI
     651        this.updateHealth(groupID);
     652        this.updateMembers(groupID);   
     653        this.updateDeathCycle(groupID);
    481654    }
    482655}
    483656
     
    494667        for each (var renamedEntity in renamedEntities)
    495668            renamedLookup[renamedEntity.entity] = renamedEntity.newentity;
    496669
    497         for each (var group in this.groups)
     670        for (var groupID in this.groups)
    498671        {
     672            var group = this.groups[groupID];
    499673            for each (var renamedEntity in renamedEntities)
    500674            {
    501675                // Reconstruct the group if at least one entity has been renamed.
     
    502676                if (renamedEntity.entity in group.ents)
    503677                {
    504678                    group.rebuildGroup(renamedLookup);
     679                    this.updateAdditionalData(groupID, Object.keys(group.ents), false);
    505680                    break;
    506681                }
    507682            }
     
    509684    }
    510685}
    511686
     687/**
     688 * resets the initialNumberOfUnits
     689 * groupID: id of the group
     690 */
     691EntityGroupsContainer.prototype.resetMemberStatus = function(groupID)
     692{
     693    if (!this.hasMembers(groupID))
     694        return;
     695       
     696    var data = this.info[groupID];
     697    data.initialNumberOfUnits = data.numberOfUnits;
     698    data.dirty = true;
     699}
     700
     701/**
     702 * checks, if a group with that ID has members
     703 * groupID: id of the group
     704 */
     705EntityGroupsContainer.prototype.hasMembers = function(groupID)
     706{
     707    return this.groups[groupID].hasMembers();
     708}
     709
     710/**
     711 * gets the dirty state for the GUI
     712 * groupID: id of the group
     713 */
     714EntityGroupsContainer.prototype.isDirty = function(groupID)
     715{
     716    return this.hasMembers(groupID) && this.info[groupID].dirty;
     717}
     718
     719/**
     720 * sets the dirty state to false after the GUI redrawed the stuff
     721 * groupID: id of the group
     722 */
     723EntityGroupsContainer.prototype.resetDirty = function(groupID)
     724{
     725    if (!this.hasMembers(groupID))
     726        return;
     727       
     728    this.info[groupID].dirty = false;
     729}
     730
     731/**
     732 * gets the attack state for the GUI
     733 * groupID: id of the group
     734 */
     735EntityGroupsContainer.prototype.isAttacked = function(groupID)
     736{
     737    return this.hasMembers(groupID) && this.info[groupID].attack;
     738}
     739
     740/**
     741 * sets the attack state to false after the color fade was started
     742 * groupID: id of the group
     743 */
     744EntityGroupsContainer.prototype.resetAttack = function(groupID)
     745{
     746    if (!this.hasMembers(groupID))
     747        return;
     748       
     749    this.info[groupID].attack = false;
     750}
     751
     752/**
     753 * sets the death cycle back to zero (off)
     754 * groupID: id of the group
     755 */
     756EntityGroupsContainer.prototype.resetDeathCycle = function(groupID)
     757{
     758    if (!this.hasMembers(groupID))
     759        return;
     760       
     761    this.info[groupID].deathCycle = 0;
     762}
     763
    512764var g_Groups = new EntityGroupsContainer();
  • binaries/data/mods/public/gui/session/session.js

     
    5656// always through the list of all ongoing attacks...
    5757var g_previousHeroHitPoints = undefined;
    5858
     59// simulation ticks until the deathIcon of a group is removed.
     60var g_groupDeathShowIconTicks = 60;
     61
    5962function GetSimState()
    6063{
    6164    if (!g_SimState)
     
    165168    Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem;
    166169    Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name;
    167170    initMenuPosition(); // set initial position
     171    initLayoutGroupButtons(); // init group buttons
    168172
    169173    // Populate player selection dropdown
    170174    var playerNames = [];
     
    211215    setTimeout(function() { reportPerformance(60); }, 60000);
    212216}
    213217
     218// inits the layout of the group buttons (adds spacers, labels and events to buttons)
     219function initLayoutGroupButtons()
     220{
     221    var guiName = "Group";
     222    for (var i = 0; i < 10; i++)
     223    {
     224        // add spacers
     225        var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]").size.bottom;
     226        var buttonSpacer = buttonSideLength+12;
     227        layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, i, i+1);
     228           
     229        // add label
     230        var label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
     231       
     232        // add button events and hide it
     233        var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
     234        button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
     235        button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
     236        button.onpressright = (function(i) { return function() { performGroup("breakUp", i); } })(i);
     237        button.hidden = true;
     238       
     239        // get the members status bar and add a event
     240        var memberButton = Engine.GetGUIObjectByName("unit"+guiName+"MembersButton["+i+"]");
     241        memberButton.onpress = (function(i) { return function() { g_Groups.resetMemberStatus(i); } })(i);
     242    }
     243}
     244
    214245function selectViewPlayer(playerID)
    215246{
    216247    Engine.SetPlayerID(playerID);
     
    311342    var data = {};
    312343    data.playerAssignments = g_PlayerAssignments;
    313344    data.groups = g_Groups.groups;
     345    data.additionalGroupInfo = g_Groups.info;
    314346    // TODO: any other gui state?
    315347    return data;
    316348}
     
    325357    {
    326358        g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups;
    327359        g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents;
     360        g_Groups.info[groupNumber] = data.additionalGroupInfo[groupNumber];
     361        g_Groups.info[groupNumber].dirty = true;
    328362    }
    329363    updateGroups();
    330364}
     
    498532    var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID());
    499533    if (battleState)
    500534        global.music.setState(global.music.states[battleState]);
    501    
    502535}
    503536
    504537/**
     
    510543*/
    511544function updateGUIStatusBar(nameOfBar, points, maxPoints, direction)
    512545{
     546    // check, if values are valid
     547    if (!(points && maxPoints))
     548        return;
    513549    // check, if optional direction parameter is valid.
    514550    if (!direction || !(direction >= 0 && direction < 4))
    515551        direction = 0;
     
    523559    var value = 100*Math.max(0, Math.min(1, points / maxPoints));
    524560   
    525561    // inverse bar
    526     if(direction == 2 || direction == 3)
     562    if (direction == 2 || direction == 3)
    527563        value = 100 - value;
    528564
    529     if(direction == 0)
     565    if (direction == 0)
    530566        healthSize.rright = value;
    531     else if(direction == 1)
     567    else if (direction == 1)
    532568        healthSize.rbottom = value;
    533     else if(direction == 2)
     569    else if (direction == 2)
    534570        healthSize.rleft = value;
    535     else if(direction == 3)
     571    else if (direction == 3)
    536572        healthSize.rtop = value;
    537573   
    538574    // update bar
     
    539575    statusBar.size = healthSize;
    540576}
    541577
     578/**
     579* changes the visibility of a status bar
     580* nameOfBar: name of the bar
     581* hide: true, if bar should be hidden
     582*/
     583function changeVisibilityStatusBar(nameOfBar, hide)
     584{
     585    // get the bar and update it
     586    var statusBar = Engine.GetGUIObjectByName(nameOfBar);
     587    if (!statusBar)
     588        return;
     589   
     590    statusBar.hidden = hide;
     591}
    542592
     593
     594
    543595function updateHero()
    544596{
    545597    var simState = GetSimState();
     
    595647    {   
    596648        g_previousHeroHitPoints = heroState.hitpoints;
    597649        // trigger the animation
    598         fadeColour("heroHitOverlay", 100, 10000, {"r": 175,"g": 0,"b": 0,"o": 100}, colourFade_attackUnit, smoothColourFadeRestart_attackUnit);
     650        startColorFade("heroHitOverlay", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
    599651        return;
    600652    }
    601653}
    602654
    603655
     656// updates the groups on the GUI
    604657function updateGroups()
    605658{
    606659    var guiName = "Group";
    607660    g_Groups.update();
    608     for (var i = 0; i < 10; i++)
    609     {
    610         var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
    611         var label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
    612         if (g_Groups.groups[i].getTotalCount() == 0)
    613             button.hidden = true;
     661        for (var i = 0; i < 10; i++)
     662    {
     663        var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
     664        // check, if group has members
     665        if (!g_Groups.hasMembers(i))
     666        {
     667            button.hidden = true;
     668            stopColorFade("unitGroupHitOverlay["+i+"]");
     669            var deathIcon = Engine.GetGUIObjectByName("unitGroupIconDeath["+ i +"]");
     670            deathIcon.hidden = true;       
     671            continue; // nothing to do, if group has no members
     672        }
     673        else
     674            button.hidden = false; // make button visible
     675                       
     676        // check, if status bars must be redrawn
     677        if (!g_Groups.isDirty(i))
     678        continue;
     679
     680        // update the status bars
     681        var data = g_Groups.info[i];
     682        updateGUIStatusBar("unitGroupHealthBar["+ i +"]", data.hitpoints, data.maxGroupHitPoints);
     683       
     684        if(data.initialNumberOfUnits == 1)
     685            changeVisibilityStatusBar("unitGroupMembersButton["+ i +"]", true);
    614686        else
    615             button.hidden = false;
    616         button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
    617         button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
    618         button.onpressright = (function(i) { return function() { performGroup("breakUp", i); } })(i);
    619     }
    620     var numButtons = i;
    621     var rowLength = 1;
    622     var numRows = Math.ceil(numButtons / rowLength);
    623     var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
    624     var buttonSpacer = buttonSideLength+1;
    625     for (var i = 0; i < numRows; i++)
    626         layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*i, rowLength*(i+1) );
     687        {
     688            changeVisibilityStatusBar("unitGroupMembersButton["+ i +"]", false);
     689            updateGUIStatusBar("unitGroupMembersBar["+ i +"]", data.numberOfUnits, data.initialNumberOfUnits, 3);
     690        }
     691       
     692        // check, if the attack should be started
     693        if (g_Groups.isAttacked(i))
     694        {
     695            startColorFade("unitGroupHitOverlay["+i+"]", 100, 10000, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
     696            g_Groups.resetAttack(i);
     697        }
     698       
     699        // check, if the death symbol should be triggered
     700        if (data.deathCycle > 0)
     701        {
     702            var deathIcon = Engine.GetGUIObjectByName("unitGroupIconDeath["+ i +"]");
     703            // show the symbol
     704            if (data.deathCycle >= g_groupDeathShowIconTicks)
     705            {
     706                deathIcon.hidden = true;
     707                g_Groups.resetDeathCycle(i);
     708            }
     709            else if (data.deathCycle >= 1)
     710                deathIcon.hidden = false;
     711        }
     712       
     713        // reset the dirty state after the updates are complete
     714        g_Groups.resetDirty(i);
     715    }
    627716}
    628717
    629718function updateDebug()
  • binaries/data/mods/public/gui/session/session.xml

     
    904904    >
    905905        <repeat count="10">
    906906            <object name="unitGroupButton[n]" size="0 0 36 36" type="button" hidden="false" style="iconButton" tooltip_style="sessionToolTipBottomBold"
    907                 tooltip="Click to select grouped units, double-click to focus the grouped units and right-click to disband the group."
    908             >
     907                tooltip="Click to select grouped units, double-click to focus the grouped units and right-click to disband the group.">
    909908                <object name="unitGroupIcon[n]" size="3 3 33 33" type="image" sprite="groupsIcon" ghost="true"/>
     909                <object name="unitGroupHitOverlay[n]" hidden="true" type="image" ghost="true" size="3 3 33 33"/>
    910910                <object name="unitGroupLabel[n]" type="text" style="largeCenteredOutlinedText" ghost="true"/>
     911                <object name="unitGroupIconDeath[n]" size="21 3 35 17" type="image" sprite="killIcon" hidden="true" ghost="true"/>
     912                <!-- Group Health bar -->
     913                <object size="2 37 34 40" name="unitGroupHealthSection[n]">
     914                    <object size="0 0 100% 4" name="unitGroupHealth[n]" ghost="true" type="image">
     915                    <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
     916                    <object type="image" sprite="healthBackground" ghost="true"/>
     917                        <object type="image" sprite="healthForeground" ghost="true" name="unitGroupHealthBar[n]"/>
     918                        <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/>
     919                    </object>
     920                </object>
     921                <!-- Group Health bar -->
     922                <object size="37 2 40 34" name="unitGroupMembersButton[n]" type="button" tooltip_style="sessionToolTipBottomBold"
     923                    tooltip="Click to reset unit counter bar.">
     924                    <object size="0 0 4 100%" name="unitGroupMembers[n]" ghost="true" type="image">
     925                    <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
     926                    <object type="image" sprite="groupSizeBackground" ghost="true"/> 
     927                        <object type="image" sprite="groupSizeForeground" ghost="true" name="unitGroupMembersBar[n]"/>
     928                    <object type="image" sprite="statsBarShaderVertical" ghost="true"/>
     929                    </object>
     930                </object>           
    911931            </object>
    912932        </repeat>
    913933    </object>
  • binaries/data/mods/public/gui/session/sprites.xml

     
    88            size="0 0 100% 100%"
    99        />
    1010    </sprite>
    11 
     11   
     12    <sprite name="killIcon">
     13        <image
     14            texture="session/icons/kill.png"
     15            size="0 0 100% 100%"
     16        />
     17    </sprite>
     18   
    1219    <sprite name="idleWorker">
    1320        <image
    1421            texture="session/icons/repair.png"
     
    1623            size="0 0 100% 100%"
    1724        />
    1825    </sprite>
     26   
     27    <sprite name="groupSizeForeground">
     28        <image backcolor="255 184 0 255"/>
     29    </sprite>
     30   
     31    <sprite name="groupSizeBackground">
     32        <image backcolor="184 184 184 255"/>
     33    </sprite>
    1934
    2035    <!-- ================================  ================================ -->
    2136    <!-- Menu -->