Ticket #1919: pack.patch

File pack.patch, 13.6 KB (added by Doménique, 11 years ago)

Patch for enabling unit and structure packing/unpacking

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

     
    11361136            else
    11371137                rightUsed = false;
    11381138        }
    1139         else if (entState.pack)
     1139        else
     1140            rightUsed = false;
     1141
     1142        if (entState.pack)
    11401143        {
    11411144            var items = [];
    11421145            var packButton = false;
     
    11801183            else
    11811184                rightUsed = false;
    11821185        }
    1183         else
    1184             rightUsed = false;
    11851186
    11861187        if (!rightUsed)
    11871188        {
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    476476    case "pack":
    477477        for each (var ent in entities)
    478478        {
    479             var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
    480             if (cmpUnitAI)
     479            // figure out whether entity is a unit or a building
     480            var cmpEntityAI = Engine.QueryInterface(ent, IID_UnitAI);
     481            if (!cmpEntityAI)
     482                cmpEntityAI = Engine.QueryInterface(ent, IID_BuildingAI);
     483            if (cmpEntityAI)
    481484            {
    482485                if (cmd.pack)
    483                     cmpUnitAI.Pack(cmd.queued);
     486                    cmpEntityAI.Pack(cmd.queued);
    484487                else
    485                     cmpUnitAI.Unpack(cmd.queued);
     488                    cmpEntityAI.Unpack(cmd.queued);
    486489            }
    487490        }
    488491        break;
     
    490493    case "cancel-pack":
    491494        for each (var ent in entities)
    492495        {
    493             var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
    494             if (cmpUnitAI)
     496            // figure out whether entity is a unit or a building
     497            var cmpEntityAI = Engine.QueryInterface(ent, IID_UnitAI);
     498            if (!cmpEntityAI)
     499                cmpEntityAI = Engine.QueryInterface(ent, IID_BuildingAI);
     500            if (cmpEntityAI)
    495501            {
    496502                if (cmd.pack)
    497                     cmpUnitAI.CancelPack(cmd.queued);
     503                    cmpEntityAI.CancelPack(cmd.queued);
    498504                else
    499                     cmpUnitAI.CancelUnpack(cmd.queued);
     505                    cmpEntityAI.CancelUnpack(cmd.queued);
    500506            }
    501507        }
    502508        break;
  • binaries/data/mods/public/simulation/components/BuildingAI.js

     
    252252    }
    253253};
    254254
     255/**
     256 * All packing code taken and adapted from UnitAI
     257 */
     258BuildingAI.prototype.Pack = function()
     259{
     260    // Check that we can pack
     261    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     262    if (cmpPack && this.CanPack())
     263        cmpPack.Pack();
     264};
     265
     266BuildingAI.prototype.Unpack = function()
     267{
     268    // Check that we can unpack
     269    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     270    if (cmpPack && this.CanUnpack())
     271        cmpPack.Unpack();
     272};
     273
     274BuildingAI.prototype.CancelPack = function()
     275{
     276    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     277    if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
     278        cmpPack.CancelPack();
     279};
     280
     281BuildingAI.prototype.CancelUnpack = function()
     282{
     283    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     284    if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
     285        cmpPack.CancelUnpack();
     286};
     287
     288BuildingAI.prototype.CanPack = function()
     289{
     290    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     291    return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked() && cmpPack.CanTransformHere());
     292};
     293
     294BuildingAI.prototype.CanUnpack = function()
     295{
     296    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     297    return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked() && cmpPack.CanTransformHere());
     298};
     299
     300BuildingAI.prototype.IsPacking = function()
     301{
     302    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     303    return (cmpPack && cmpPack.IsPacking());
     304};
     305
    255306Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI);
  • binaries/data/mods/public/simulation/components/Pack.js

     
    4949    return this.packing;
    5050};
    5151
     52/**
     53 * @return True if the entity to transform into is allowed on the
     54 *         current terrain/territory.
     55 */
     56Pack.prototype.CanTransformHere = function ()
     57{
     58    var canTransform = true;
     59    // temporarily creating an entity for a check
     60    var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     61    var previewEntityTemplate = this.template.Entity;
     62    if (cmpIdentity)
     63        previewEntityTemplate = previewEntityTemplate.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     64    var previewEntity = Engine.AddEntity(previewEntityTemplate);
     65    if (previewEntity == INVALID_ENTITY)
     66        canTransform = false; // Error (e.g. invalid template names)
     67    else
     68    {
     69        var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
     70        if (cmpBuildRestrictions)
     71        {
     72            // Position and ownership needs to be set for placement checks
     73            var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     74            var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
     75            if (cmpPosition.IsInWorld())
     76            {
     77                var pos = cmpPosition.GetPosition2D();
     78                var angle = cmpPosition.GetRotation();
     79                cmpNewPosition.JumpTo(pos.x, pos.y);
     80                cmpNewPosition.SetYRotation(angle.y);
     81            }
     82            var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     83            var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
     84            cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     85
     86            var cmpObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction);
     87            if (cmpObstruction)
     88                cmpObstruction.SetActive(false); // entity is not really obstructing anything
     89
     90            // TODO: territory checks via CheckPlacement are currently not functional,
     91            // since in that function the tileOwner always gives the current playerID
     92            // (when calling the same code here, it does give the correct owner...)
     93            var checkPlacement = cmpBuildRestrictions.CheckPlacement();
     94            if (checkPlacement && !checkPlacement.success)
     95                canTransform = false;
     96            // move temporary entity out of world so it won't interfere with additional calls
     97            // to this function for this entity on the same sim update.
     98            // specifically, CheckPlacement() would incorrectly spot and fail on earlier temp entities
     99            // if those are left in the world.
     100            cmpNewPosition.MoveOutOfWorld();
     101        }
     102    }
     103
     104    Engine.DestroyEntity(previewEntity);
     105    return canTransform;
     106};
     107
    52108Pack.prototype.Pack = function()
    53109{
    54110    // Ignore pointless pack command
     
    124180        Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
    125181
    126182        // Done un/packing, copy our parameters to the final entity
    127         var newEntity = Engine.AddEntity(this.template.Entity);
     183        // Replace the "{civ}" codes with this entity's civ ID
     184        var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     185        var newEntityTemplate = this.template.Entity;
     186        if (cmpIdentity)
     187            newEntityTemplate = newEntityTemplate.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     188        var newEntity = Engine.AddEntity(newEntityTemplate);
    128189        if (newEntity == INVALID_ENTITY)
    129190        {
    130191            // Error (e.g. invalid template names)
     
    164225            cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
    165226        }
    166227
     228        // Transfer production queue
     229        // note: default behaviour upon destroying an entity is to reset queue,
     230        //       that case is thus not handled here.
     231        var cmpQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
     232        var cmpNewQueue = Engine.QueryInterface(newEntity, IID_ProductionQueue);
     233        if (cmpQueue && cmpNewQueue)
     234        {
     235            var queue = cmpQueue.GetQueue(true); // with true returns copy, not just summary
     236            if (queue.length > 0)
     237            {
     238                var transferableQueue = [];
     239                for (var i = queue.length-1; i >= 0; i--)
     240                {
     241                    var templateName = queue[i].unitTemplate ? queue[i].unitTemplate : queue[i].technologyTemplate;
     242                    if (cmpNewQueue.CanAcceptTemplate(templateName))
     243                        transferableQueue.push( queue.splice(i,1)[0] );
     244                }
     245                cmpQueue.SetQueue(queue); // set old queue with untransferable batches
     246                cmpNewQueue.SetQueue(transferableQueue); // set new queue with transferable batches
     247            }
     248        }
     249
     250        // Transfer garrisoned units if possible, or unload them
     251        var cmpGarrison = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     252        var cmpNewGarrison = Engine.QueryInterface(newEntity, IID_GarrisonHolder);
     253        if (cmpGarrison && cmpGarrison.GetEntities().length > 0)
     254        {
     255            if (cmpNewGarrison)
     256            {
     257                // note: the code cannot rely on the garrisoning entity's indexing
     258                //       due to removal of elements. This is why a reference to the
     259                //       garrisoned entity is stored in a temporary variable
     260                var garrisonedEntity,  // initiated outside for loop to reuse (reduces garbage)
     261                    garrisonedEntities = cmpGarrison.GetEntities();
     262                for (var j = garrisonedEntities.length-1; j >= 0; j--)
     263                {
     264                    garrisonedEntity = garrisonedEntities[j];
     265                    cmpGarrison.Unload(garrisonedEntity, true); // always kick out entity
     266                    cmpNewGarrison.Garrison(garrisonedEntity);  // then try to garrison
     267                }
     268            }
     269            else
     270                cmpGarrison.UnloadAll(true);
     271        }
     272       
    167273        Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity });
    168274
    169275        // Play notification sound
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    43804380UnitAI.prototype.CanPack = function()
    43814381{
    43824382    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
    4383     return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked());
     4383    return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked() && cmpPack.CanTransformHere());
    43844384};
    43854385
    43864386UnitAI.prototype.CanUnpack = function()
    43874387{
    43884388    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
    4389     return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked());
     4389    return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked() && cmpPack.CanTransformHere());
    43904390};
    43914391
    43924392UnitAI.prototype.IsPacking = function()
  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    148148    return ret;
    149149};
    150150
     151/*
     152 * Returns true if the given template can be produced by this entity
     153 * @param templateName String of the template name
     154 */
     155ProductionQueue.prototype.CanAcceptTemplate = function(templateName)
     156{
     157    var unitTemplates = this.GetEntitiesList();
     158    if (unitTemplates.indexOf(templateName) !== -1)
     159        return true;
     160
     161    var techTemplates = this.GetTechnologiesList();
     162    for (var tech in techTemplates)
     163    {
     164        if (typeof tech === 'string' && tech === templateName)
     165            return true;
     166        else if (typeof tech === 'object' && (tech.top === templateName || tech.bottom === templateName))
     167            return true;
     168    }
     169
     170    return false;
     171};
     172
    151173ProductionQueue.prototype.IsTechnologyResearchedOrInProgress = function(tech)
    152174{
    153175    if (!tech)
     
    361383
    362384/*
    363385 * Returns basic data from all batches in the production queue.
     386 * With complete = true, returns a full copy of the queue.
    364387 */
    365 ProductionQueue.prototype.GetQueue = function()
     388ProductionQueue.prototype.GetQueue = function(complete)
    366389{
    367390    var out = [];
    368391    for each (var item in this.queue)
    369392    {
    370         out.push({
    371             "id": item.id,
    372             "unitTemplate": item.unitTemplate,
    373             "technologyTemplate": item.technologyTemplate,
    374             "count": item.count,
    375             "neededSlots": item.neededSlots,
    376             "progress": 1-(item.timeRemaining/item.timeTotal),
    377             "metadata": item.metadata,
    378         });
     393        if (complete)
     394        {
     395            var batch = {
     396                "id": item.id,
     397                "player": item.player,
     398                "count": item.count,
     399                "metadata": item.metadata,
     400                "resources": item.resources, // need to copy to avoid serialization problems
     401                "population": item.population,
     402                "productionStarted": item.productionStarted,
     403                "timeTotal": item.timeTotal,
     404                "timeRemaining": item.timeRemaining
     405            };
     406            if (item.unitTemplate)
     407                batch["unitTemplate"] = item.unitTemplate;
     408            if (item.technologyTemplate)
     409                batch["technologyTemplate"] = item.technologyTemplate;
     410            out.push(batch);
     411        }
     412        else
     413        {
     414            out.push({
     415                "id": item.id,
     416                "unitTemplate": item.unitTemplate,
     417                "technologyTemplate": item.technologyTemplate,
     418                "count": item.count,
     419                "neededSlots": item.neededSlots,
     420                "progress": 1-(item.timeRemaining/item.timeTotal),
     421                "metadata": item.metadata,
     422            });
     423        }
    379424    }
    380425    return out;
    381426};
     
    394439};
    395440
    396441/*
     442 * Swaps the current queue for a new one. Only use this functionality
     443 * to transfer a queue from one entity to another without rechecking validity.
     444 * For other uses, please go with the other functions in this Component.
     445 * @param queue New queue which replaces the current one.
     446 */
     447ProductionQueue.prototype.SetQueue = function(queue)
     448{
     449    if (queue && typeof queue === 'object' && queue.length) // checks for type array
     450    {
     451        this.queue = queue;
     452        Engine.PostMessage(this.entity, MT_ProductionQueueChanged, {});
     453        if (this.queue.length > 0 && !this.timer) // if 0 timer will expire on its own
     454        {
     455            var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     456            this.timer = cmpTimer.SetTimeout(this.entity, IID_ProductionQueue, "ProgressTimeout", g_ProgressInterval, {});
     457        }
     458    }
     459};
     460
     461/*
    397462 * Returns batch build time.
    398463 */
    399464ProductionQueue.prototype.GetBatchTime = function(batchSize)