Ticket #2357: idle_worker_blink_v9.patch

File idle_worker_blink_v9.patch, 24.2 KB (added by Michael, 10 years ago)

added: select all idle workers with right click.

  • 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
    188 //[END] of hero fade functions
    189  No newline at end of file
     228//[END] of hero fade functions
     229//[START] idle workers icon
     230
     231function colorFade_IdleWorker(data)
     232{
     233    var rgb = data.rgb;
     234    // init color
     235    if (data.tickCounter == 0)
     236    {
     237        rgb.r = 255;
     238        rgb.g = 255;
     239        rgb.b = 255;
     240    }
     241    // hide it every tick
     242    if (data.tickCounter % 2 == 0)
     243        rgb.o = 0;
     244    else
     245        rgb.o = 100;
     246}
     247
     248//[END] of idle workers icon functions
     249 No newline at end of file
  • binaries/data/mods/public/gui/session/input.js

     
    20842084    lastIdleType = undefined;
    20852085}
    20862086
    2087 function findIdleUnit(classes)
     2087// selectionLimit [optional]: positive number greater than zero or false, if all should be selected
     2088function findIdleUnit(classes, selectionLimit)
    20882089{
    20892090    var append = Engine.HotkeyIsPressed("selection.add");
    2090     var selectall = Engine.HotkeyIsPressed("selection.offscreen");
     2091    var selectall = Engine.HotkeyIsPressed("selection.offscreen") || selectionLimit === false;
     2092    if (!selectionLimit || selectionLimit <= 0)
     2093        selectionLimit = 1;
    20912094
    20922095    // Reset the last idle unit, etc., if the selection type has changed.
    20932096    var type = classes.join();
     
    21012104
    21022105    for (var i = 0; i < classes.length; ++i)
    21032106    {
    2104         var data = { idleClass: classes[currIdleClass], prevUnit: lastIdleUnit, limit: 1 };
     2107        var data = { idleClass: classes[currIdleClass], prevUnit: lastIdleUnit, limit: selectionLimit };
    21052108        if (append)
    21062109            data.excludeUnits = g_Selection.toList();
    21072110
  • binaries/data/mods/public/gui/session/session.js

     
    5656// always through the list of all ongoing attacks...
    5757var g_previousHeroHitPoints = undefined;
    5858
     59// cache idle worker button class names
     60var g_idleWorkerClasses = undefined;
     61
    5962function GetSimState()
    6063{
    6164    if (!g_SimState)
     
    148151    {
    149152        g_Players = getPlayerData(null);
    150153    }
    151 
     154    // cache worker class data
     155    g_idleWorkerClasses = Engine.GuiInterfaceCall("GetIdleWorkerClasses");
     156    // set function to button
     157    var idleWorkerButton = Engine.GetGUIObjectByName("idleWorkerButton");
     158    if (idleWorkerButton)
     159    {
     160        idleWorkerButton.onpress = function() { findIdleUnit(g_idleWorkerClasses); };
     161        idleWorkerButton.onpressright = function() { findIdleUnit(g_idleWorkerClasses, false); };
     162    }
     163   
    152164    // Cache civ data
    153165    g_CivData = loadCivData();
    154166    g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia" };
     
    486498
    487499    updateHero();
    488500    updateGroups();
     501    updateIdleWorkerButton();
    489502    updateDebug();
    490503    updatePlayerDisplay();
    491504    updateSelectionDetails();
     
    499512    if (battleState)
    500513        global.music.setState(global.music.states[battleState]);
    501514   
     515};
     516
     517/**
     518 * updates the idle worker button
     519 */
     520function updateIdleWorkerButton()
     521{
     522    var simState = GetSimState();
     523    var idleUnitsCounter = simState.players[Engine.GetPlayerID()].idleWorkerButtonCount;
     524    // set color fade and label
     525    if (idleUnitsCounter > 0)
     526    {
     527        startColorFade("idleWorkerOverlay", 750, 0, colorFade_IdleWorker, false);
     528        var label = Engine.GetGUIObjectByName("idleWorkerLabel")
     529        label.caption = idleUnitsCounter;
     530        label.hidden = false;
     531    }
     532    else
     533    {
     534        stopColorFade("idleWorkerOverlay");
     535        var label = Engine.GetGUIObjectByName("idleWorkerLabel");
     536        label.hidden = true;
     537    }
    502538}
    503539
    504540/**
     
    595631    {   
    596632        g_previousHeroHitPoints = heroState.hitpoints;
    597633        // trigger the animation
    598         fadeColour("heroHitOverlay", 100, 10000, {"r": 175,"g": 0,"b": 0,"o": 100}, colourFade_attackUnit, smoothColourFadeRestart_attackUnit);
     634        startColorFade("heroHitOverlay", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit);
    599635        return;
    600636    }
    601637}
  • binaries/data/mods/public/gui/session/session.xml

     
    952952        >
    953953            <object type="button"
    954954                style="iconButton"
    955                 tooltip_style="sessionToolTip"
    956                 tooltip="Find idle worker"
    957                 hotkey="selection.idleworker"
    958             >
    959                 <!-- TODO: should highlight the button if there's non-zero idle workers -->
    960                 <object size="0 0 100% 100%" type="image" sprite="idleWorker" ghost="true" />
    961                 <action on="Press">findIdleUnit(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"]);</action>
    962             </object>
     955                tooltip_style="sessionToolTipBottom"
     956                tooltip="Click to find an idle worker, right-click to select all idle workers."
     957                hotkey="selection.idleworker"
     958                name="idleWorkerButton"
     959            >
     960            <object size="0 0 100% 100%" type="image" sprite="idleWorker" ghost="true" />
     961            <object name="idleWorkerOverlay" hidden="true" type="image" ghost="true" size="2 2 100%-2 100%-2"/>
     962            <object name="idleWorkerLabel" type="text" style="groupIconsText" size="0 0 100% 100%" ghost="true"/>
     963            </object>
    963964        </object>
    964965    </object>
    965966
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    8989            "team": cmpPlayer.GetTeam(),
    9090            "teamsLocked": cmpPlayer.GetLockTeams(),
    9191            "cheatsEnabled": cmpPlayer.GetCheatsEnabled(),
     92            "idleWorkerButtonCount": cmpPlayer.GetIdleWorkerButtonCount(),
    9293            "phase": phase,
    9394            "isAlly": allies,
    9495            "isMutualAlly": mutualAllies,
     
    727728}
    728729
    729730// Returns the battle state of the player.
     731GuiInterface.prototype.GetIdleWorkerClasses = function(player)
     732{
     733    // try to get the data
     734    var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
     735    if (!cmpPlayer)
     736        return [];
     737    else
     738        return cmpPlayer.GetIdleWorkerButtonClassesList();
     739};
     740
     741// Returns the battle state of the player.
    730742GuiInterface.prototype.GetBattleState = function(player)
    731743{
    732744    var cmpBattleDetection = QueryPlayerIDInterface(player, IID_BattleDetection);
     
    19761988    "SetWallPlacementPreview": 1,
    19771989    "GetFoundationSnapData": 1,
    19781990    "PlaySound": 1,
     1991    "GetIdleWorkerClasses" :1,
    19791992    "FindIdleUnits": 1,
    19801993    "GetTradingRouteGain": 1,
    19811994    "GetTradingDetails": 1,
  • binaries/data/mods/public/simulation/components/Player.js

     
    11function Player() {}
    22
    33Player.prototype.Schema =
    4     "<a:component type='system'/><empty/>";
    5 
     4    "<element name='IdleWorkerButtonClasses' a:help='Classes of entities which are used for the idle worker button.'>" +
     5        "<attribute name='datatype'>" +
     6            "<value>tokens</value>" +
     7        "</attribute>" +
     8        "<text/>" +
     9    "</element>";
     10   
    611Player.prototype.Init = function()
    712{
    813    this.playerID = undefined;
     
    3742    this.cheatsEnabled = false;
    3843    this.cheatTimeMultiplier = 1;
    3944    this.heroes = [];
     45    this.idleWorkerButtonCount = 0;
     46    this.idleWorkerButtonClasses = this.template.IdleWorkerButtonClasses._string ? this.template.IdleWorkerButtonClasses._string.split(/\s+/) : []; // classes used for the work idle button
    4047    Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).CheckConquestCriticalEntities();
    4148};
    4249
     50/**
     51 * Returns an array of unit classes which are used for the idle worker button
     52 */
     53Player.prototype.GetIdleWorkerButtonClassesList = function()
     54{
     55    return this.idleWorkerButtonClasses;
     56};
     57
    4358Player.prototype.SetPlayerID = function(id)
    4459{
    4560    this.playerID = id;
     
    560575            if (index >= 0)
    561576                this.heroes.splice(index, 1);
    562577        }
     578        // check, if new idle worker is gone
     579        var cmpUnitAI = Engine.QueryInterface(msg.entity, IID_UnitAI);
     580        if (cmpUnitAI)
     581            if (cmpUnitAI.IsIdle() && cmpUnitAI.IsUsedForIdleWorkerButton())
     582                this.DecreaseWorkerButtonCount();
    563583    }
    564584    if (msg.to == this.playerID)
    565585    {
     
    571591
    572592        if (cmpIdentity && cmpIdentity.HasClass("Hero"))
    573593            this.heroes.push(msg.entity);
     594           
     595        // check, if new idle worker is there
     596        var cmpUnitAI = Engine.QueryInterface(msg.entity, IID_UnitAI);
     597        if (cmpUnitAI)
     598            if (cmpUnitAI.IsIdle() && cmpUnitAI.IsUsedForIdleWorkerButton())
     599                this.IncreaseWorkerButtonCount();
    574600    }
    575601};
    576602
     
    664690        cmpGUIInterface.PushNotification(notification);
    665691};
    666692
     693Player.prototype.GetIdleWorkerButtonCount = function()
     694{
     695    return this.idleWorkerButtonCount;
     696};
     697
     698Player.prototype.IncreaseWorkerButtonCount = function()
     699{
     700    this.idleWorkerButtonCount++;
     701};
     702
     703Player.prototype.DecreaseWorkerButtonCount = function()
     704{
     705    this.idleWorkerButtonCount--;
     706    if (this.idleWorkerButtonCount < 0)
     707    {
     708        this.idleWorkerButtonCount = 0;
     709        error("Negative worker idle count for player '" + this.playerID + "'");
     710    }
     711};
     712
    667713Engine.RegisterComponentType(IID_Player, "Player", Player);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    169169    "GuardedAttacked": function(msg) {
    170170        // ignore
    171171    },
     172   
     173    "UnitIdleChanged": function(msg) {     
     174        // do only if its not animal and is used for the idle worker button
     175        if (!this.IsAnimal() && this.IsUsedForIdleWorkerButton())
     176        {
     177            var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     178            if (cmpPlayer)
     179            {
     180                if(msg.idle)
     181                    cmpPlayer.IncreaseWorkerButtonCount();
     182                else
     183                    cmpPlayer.DecreaseWorkerButtonCount();
     184            }
     185        }
     186    },
    172187
    173188    // Formation handlers:
    174189
     
    11391154
    11401155            // Stop moving as soon as the formation disbands
    11411156            this.StopMoving();
     1157           
     1158            // if unit is dead, stop here
     1159            if (!msg.isAlive)
     1160                return;
    11421161
    11431162            // If the controller handled an order but some members rejected it,
    11441163            // they will have no orders and be in the FORMATIONMEMBER.IDLE state.
     
    30503069    this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to
    30513070    this.isGarrisoned = false;
    30523071    this.isIdle = false;
     3072    this.isUsedForIdleWorkerButton = undefined; // will be initialised, if IsUsedForIdleWorkerButton() is called the first time
    30533073    this.lastFormationTemplate = "";
    30543074    this.finishedOrder = false; // used to find if all formation members finished the order
    30553075   
     
    31283148    return Engine.QueryInterface(this.entity, IID_Heal);
    31293149};
    31303150
     3151UnitAI.prototype.IsUsedForIdleWorkerButton = function()
     3152{
     3153    // return cached value, if some is stored
     3154    if (this.isUsedForIdleWorkerButton !== undefined)
     3155        return this.isUsedForIdleWorkerButton;
     3156   
     3157    // calculate value
     3158    this.isUsedForIdleWorkerButton = false;
     3159    var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     3160    if (!cmpIdentity)
     3161        return false;
     3162    var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     3163    if (!cmpPlayer)
     3164        return false;
     3165    // check idle class names   
     3166    for each(var className in cmpPlayer.GetIdleWorkerButtonClassesList())
     3167        if (cmpIdentity.HasClass(className)) {
     3168            this.isUsedForIdleWorkerButton = true;
     3169            break;
     3170        }
     3171    // return the value
     3172    return this.isUsedForIdleWorkerButton;
     3173};
     3174
    31313175UnitAI.prototype.IsIdle = function()
    31323176{
    31333177    return this.isIdle;
     
    37733817    UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed});
    37743818};
    37753819
     3820UnitAI.prototype.OnUnitIdleChanged = function(msg)
     3821{
     3822    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     3823    if (!cmpOwnership)
     3824        return;
     3825    UnitFsm.ProcessMessage(this, {"type": "UnitIdleChanged", "idle": msg.idle, "player": cmpOwnership.GetOwner()});
     3826}
     3827
    37763828//// Helper functions to be called by the FSM ////
    37773829
    37783830UnitAI.prototype.GetWalkSpeed = function()
     
    45224574    }
    45234575
    45244576    // If we were removed from a formation, let the FSM switch back to INDIVIDUAL
     4577    // but only, if unit is not dead
    45254578    if (ent == INVALID_ENTITY)
    4526         UnitFsm.ProcessMessage(this, { "type": "FormationLeave" });
     4579    {
     4580        var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
     4581        if (!cmpHealth)
     4582            return;
     4583        UnitFsm.ProcessMessage(this, { "type": "FormationLeave", "isAlive": cmpHealth.GetHitpoints() > 0 });
     4584    }
    45274585};
    45284586
    45294587UnitAI.prototype.GetFormationController = function()
  • binaries/data/mods/public/simulation/templates/special/player.xml

     
    3838      </Pillar>
    3939    </LimitChangers>
    4040  </EntityLimits>
    41   <Player/>
     41  <Player>
     42    <IdleWorkerButtonClasses datatype="tokens">Worker Trade FishingBoat</IdleWorkerButtonClasses>
     43  </Player>
    4244  <StatisticsTracker/>
    4345  <TechnologyManager/>
    4446</Entity>