This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Changeset 10081 for ps


Ignore:
Timestamp:
08/24/11 00:43:34 (13 years ago)
Author:
ben
Message:

Fixes training queue behavior when spawning fails due to lack of space (the queue will be blocked). Fixes #893.
Changes training queue to support partial batch success and removal.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/public/simulation/components/TrainingQueue.js

    r9970 r10081  
    2929    //     "template": "units/example",
    3030    //     "count": 10,
    31     //     "resources": { "wood": 100, ... },
    32     //     "population": 10,
     31    //     "resources": { "wood": 100, ... },   // resources per unit, multiply by count to get total
     32    //     "population": 1, // population per unit, multiply by count to get total
    3333    //     "trainingStarted": false, // true iff we have reserved population
    3434    //     "timeTotal": 15000, // msecs
     
    3737   
    3838    this.timer = undefined; // g_ProgressInterval msec timer, active while the queue is non-empty
    39 };
    40 
     39   
     40    this.entityCache = [];
     41    this.spawnNotified = false;
     42};
     43
     44/*
     45 * Returns list of entities that can be trained by this building.
     46 */
    4147TrainingQueue.prototype.GetEntitiesList = function()
    4248{
     
    5157};
    5258
     59/*
     60 * Adds a new batch of identical units to the training queue.
     61 */
    5362TrainingQueue.prototype.AddBatch = function(templateName, count, metadata)
    5463{
     
    6675            return;
    6776
    68         var costMult = count;
    69 
    7077        // Apply a time discount to larger batches.
    7178        // TODO: work out what equation we should use here.
     
    7481        var time = timeMult * template.Cost.BuildTime;
    7582
    76         var costs = {};
     83        var totalCosts = {};
    7784        for each (var r in ["food", "wood", "stone", "metal"])
    78             costs[r] = Math.floor(costMult * template.Cost.Resources[r]);
    79 
    80         var population = template.Cost.Population * count;
     85            totalCosts[r] = Math.floor(count * template.Cost.Resources[r]);
     86
     87        var population = template.Cost.Population;
    8188   
    8289        // TrySubtractResources should report error to player (they ran out of resources)
    83         if (!cmpPlayer.TrySubtractResources(costs))
     90        if (!cmpPlayer.TrySubtractResources(totalCosts))
    8491            return;
    8592
     
    9097            "count": count,
    9198            "metadata": metadata,
    92             "resources": costs,
     99            "resources": template.Cost.Resources,
    93100            "population": population,
    94101            "trainingStarted": false,
     
    113120};
    114121
     122/*
     123 * Removes an existing batch of units from the training queue.
     124 * Refunds resource costs and population reservations.
     125 */
    115126TrainingQueue.prototype.RemoveBatch = function(id)
    116127{
     128    // Destroy any cached entities (those which didn't spawn for some reason)
     129    for (var i = 0; i < this.entityCache.length; ++i)
     130    {
     131        Engine.DestroyEntity(this.entityCache[i]);
     132    }
     133    this.entityCache = [];
     134   
    117135    for (var i = 0; i < this.queue.length; ++i)
    118136    {
     
    126144
    127145        // Refund the resource cost for this batch
    128         cmpPlayer.AddResources(item.resources);
     146        var totalCosts = {};
     147        for each (var r in ["food", "wood", "stone", "metal"])
     148            totalCosts[r] = Math.floor(item.count * item.resources[r]);
     149           
     150        cmpPlayer.AddResources(totalCosts);
    129151
    130152        // Remove reserved population slots if necessary
    131153        if (item.trainingStarted)
    132             cmpPlayer.UnReservePopulationSlots(item.population);
     154            cmpPlayer.UnReservePopulationSlots(item.population * item.count);
    133155
    134156        // Remove from the queue
     
    141163};
    142164
     165/*
     166 * Returns basic data from all batches in the training queue.
     167 */
    143168TrainingQueue.prototype.GetQueue = function()
    144169{
     
    157182};
    158183
     184/*
     185 * Removes all existing batches from the queue.
     186 */
    159187TrainingQueue.prototype.ResetQueue = function()
    160188{
     
    197225};
    198226
    199 
     227/*
     228 * This function creates the entities and places them in world if possible.
     229 * returns the number of successfully spawned entities.
     230 */
    200231TrainingQueue.prototype.SpawnUnits = function(templateName, count, metadata)
    201232{
     
    205236    var cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
    206237   
    207     var ents = [];
     238    var spawnedEnts = [];
     239   
     240    if (this.entityCache.length == 0)
     241    {
     242        // We need entities to test spawning, but we don't want to waste resources,
     243        //  so only create them once and use as needed
     244        for (var i = 0; i < count; ++i)
     245        {
     246            this.entityCache.push(Engine.AddEntity(templateName));
     247        }
     248    }
    208249
    209250    for (var i = 0; i < count; ++i)
    210251    {
    211         var ent = Engine.AddEntity(templateName);
    212 
     252        var ent = this.entityCache[0];
    213253        var pos = cmpFootprint.PickSpawnPoint(ent);
    214254        if (pos.y < 0)
    215255        {
    216             // Whoops, something went wrong (maybe there wasn't any space to spawn the unit).
    217             // What should we do here?
    218             // For now, just move the unit into the middle of the building where it'll probably get stuck
    219             pos = cmpPosition.GetPosition();
     256            // Fail: there wasn't any space to spawn the unit
     257            break;
     258        }
     259        else
     260        {
     261            // Successfully spawned
     262            var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
     263            cmpNewPosition.JumpTo(pos.x, pos.z);
     264            // TODO: what direction should they face in?
     265
     266            var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
     267            cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
    220268           
    221             var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
    222             var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Can't find free space to spawn trained unit"};
    223             var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    224             cmpGUIInterface.PushNotification(notification);
    225         }
    226 
    227         var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
    228         cmpNewPosition.JumpTo(pos.x, pos.z);
    229         // TODO: what direction should they face in?
    230 
    231         var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
    232         cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
    233        
    234         var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
    235         cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
    236        
    237         ents.push(ent);
    238 
    239         // Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
    240         if (i == 0)
    241             PlaySound("trained", ent);
    242     }
    243 
    244     // If a rally point is set, walk towards it (in formation)
    245     if (cmpRallyPoint)
    246     {
    247         var rallyPos = cmpRallyPoint.GetPosition();
    248         if (rallyPos)
    249         {
    250             ProcessCommand(cmpOwnership.GetOwner(), {
    251                 "type": "walk",
    252                 "entities": ents,
    253                 "x": rallyPos.x,
    254                 "z": rallyPos.z,
    255                 "queued": false
    256             });
    257         }
    258     }
    259 
    260     Engine.PostMessage(this.entity, MT_TrainingFinished, {
    261         "entities": ents,
    262         "owner": cmpOwnership.GetOwner(),
    263         "metadata": metadata,
    264     });
    265 };
    266 
     269            var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
     270            cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
     271
     272            // Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
     273            if (spawnedEnts.length == 0)
     274                PlaySound("trained", ent);
     275           
     276            this.entityCache.shift();
     277            spawnedEnts.push(ent);
     278        }
     279    }
     280
     281    if (spawnedEnts.length > 0)
     282    {
     283        // If a rally point is set, walk towards it (in formation)
     284        if (cmpRallyPoint)
     285        {
     286            var rallyPos = cmpRallyPoint.GetPosition();
     287            if (rallyPos)
     288            {
     289                ProcessCommand(cmpOwnership.GetOwner(), {
     290                    "type": "walk",
     291                    "entities": spawnedEnts,
     292                    "x": rallyPos.x,
     293                    "z": rallyPos.z,
     294                    "queued": false
     295                });
     296            }
     297        }
     298
     299        Engine.PostMessage(this.entity, MT_TrainingFinished, {
     300            "entities": spawnedEnts,
     301            "owner": cmpOwnership.GetOwner(),
     302            "metadata": metadata,
     303        });
     304    }
     305   
     306    return spawnedEnts.length;
     307};
     308
     309/*
     310 * Increments progress on the first batch in the training queue, and blocks the
     311 * queue if population limit is reached or some units failed to spawn.
     312 */
    267313TrainingQueue.prototype.ProgressTimeout = function(data)
    268314{
     
    280326            // Batch's training hasn't started yet.
    281327            // Try to reserve the necessary population slots
    282             if (!cmpPlayer.TryReservePopulationSlots(item.population))
     328            if (!cmpPlayer.TryReservePopulationSlots(item.population * item.count))
    283329            {
    284330                // No slots available - don't train this batch now
     
    287333                // Set flag that training queue is blocked
    288334                cmpPlayer.BlockTrainingQueue();
    289 
    290335                break;
    291336            }
     
    304349        }
    305350
    306         // This item is finished now
    307         time -= item.timeRemaining;
    308         cmpPlayer.UnReservePopulationSlots(item.population);
    309         this.SpawnUnits(item.template, item.count, item.metadata);
    310         this.queue.shift();
    311         Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
     351        var numSpawned = this.SpawnUnits(item.template, item.count, item.metadata);
     352        if (numSpawned > 0)
     353        {
     354            // This could be only partially finised
     355            cmpPlayer.UnReservePopulationSlots(item.population * numSpawned);
     356            item.count -= numSpawned;
     357            Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
     358        }
     359       
     360        if (item.count == 0)
     361        {
     362            // All entities spawned, this batch finished
     363            time -= item.timeRemaining;
     364            this.queue.shift();
     365            // Unset flag that training queue is blocked
     366            cmpPlayer.UnBlockTrainingQueue();
     367            this.spawnNotified = false;
     368        }
     369        else
     370        {
     371            // Some entities failed to spawn
     372            // Set flag that training queue is blocked
     373            cmpPlayer.BlockTrainingQueue();
     374           
     375            if (!this.spawnNotified)
     376            {
     377                var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     378                var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Can't find free space to spawn trained units" };
     379                var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     380                cmpGUIInterface.PushNotification(notification);
     381                this.spawnNotified = true;
     382            }
     383            break;
     384        }
    312385    }
    313386
Note: See TracChangeset for help on using the changeset viewer.