Ticket #1919: pack_v2.patch

File pack_v2.patch, 14.2 KB (added by Doménique, 11 years ago)

Improved patch for enabling unit and structure packing/unpacking

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

     
    11481148            else
    11491149                rightUsed = false;
    11501150        }
    1151         else if (entState.pack)
     1151        else
     1152            rightUsed = false;
     1153       
     1154        // An entity can have both trainable actions and pack/unpack commands
     1155        if (entState.pack)
    11521156        {
    11531157            var items = [];
    11541158            var packButton = false;
     
    11881192                items.push({ "packing": true, "packed": true, "tooltip": "Cancel unpacking", "callback": function() { cancelPackUnit(false); } });
    11891193
    11901194            if (items.length)
     1195            {
     1196                rightUsed = true; // might have gone false before, around line 1152
    11911197                setupUnitPanel(PACK, usedPanels, entState, playerState, items);
    1192             else
    1193                 rightUsed = false;
     1198            }
    11941199        }
    1195         else
    1196             rightUsed = false;
    11971200
    11981201        if (!rightUsed)
    11991202        {
  • 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

     
    44
    55function BuildingAI() {}
    66
    7 BuildingAI.prototype.Schema = 
     7BuildingAI.prototype.Schema =
    88    "<element name='DefaultArrowCount'>" +
    99        "<data type='nonNegativeInteger'/>" +
    1010    "</element>" +
     
    329329    return true;
    330330};
    331331
     332/**
     333 * All packing code taken and adapted from UnitAI
     334 */
     335BuildingAI.prototype.Pack = function()
     336{
     337    // Check that we can pack
     338    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     339    if (cmpPack && this.CanPack())
     340        cmpPack.Pack();
     341};
     342
     343BuildingAI.prototype.Unpack = function()
     344{
     345    // Check that we can unpack
     346    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     347    if (cmpPack && this.CanUnpack())
     348        cmpPack.Unpack();
     349};
     350
     351BuildingAI.prototype.CancelPack = function()
     352{
     353    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     354    if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
     355        cmpPack.CancelPack();
     356};
     357
     358BuildingAI.prototype.CancelUnpack = function()
     359{
     360    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     361    if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
     362        cmpPack.CancelUnpack();
     363};
     364
     365BuildingAI.prototype.CanPack = function()
     366{
     367    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     368    return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked() && cmpPack.CanTransformHere());
     369};
     370
     371BuildingAI.prototype.CanUnpack = function()
     372{
     373    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     374    return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked() && cmpPack.CanTransformHere());
     375};
     376
     377BuildingAI.prototype.IsPacking = function()
     378{
     379    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
     380    return (cmpPack && cmpPack.IsPacking());
     381};
     382
    332383Engine.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("preview|"+previewEntityTemplate);
     65
     66    if (previewEntity == INVALID_ENTITY)
     67        return false; // Error (e.g. invalid template name)
     68    else
     69    {
     70        var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
     71        if (cmpBuildRestrictions)
     72        {
     73            // Position and ownership needs to be set for placement checks
     74            var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     75            var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
     76            if (cmpPosition.IsInWorld())
     77            {
     78                var pos = cmpPosition.GetPosition2D();
     79                var angle = cmpPosition.GetRotation();
     80                cmpNewPosition.JumpTo(pos.x, pos.y);
     81                cmpNewPosition.SetYRotation(angle.y);
     82            }
     83            var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     84            var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
     85            cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     86
     87            var checkPlacement = cmpBuildRestrictions.CheckPlacement();
     88            if (checkPlacement && !checkPlacement.success)
     89                canTransform = false; print('\n'+JSON.stringify(checkPlacement, null, 2));
     90            // Move preview entity out of world so it won't interfere with additional calls
     91            //    to this function for this entity on the same sim update.
     92            // Specifically, CheckPlacement() would incorrectly spot and fail on earlier preview entities
     93            //    if those are left in the world.
     94            cmpNewPosition.MoveOutOfWorld();
     95        }
     96    }
     97
     98    Engine.DestroyEntity(previewEntity); print('\nCanTransformHere: '+canTransform+'\n');
     99    return canTransform;
     100};
     101
    52102Pack.prototype.Pack = function()
    53103{
    54104    // Ignore pointless pack command
     
    124174        Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
    125175
    126176        // Done un/packing, copy our parameters to the final entity
    127         var newEntity = Engine.AddEntity(this.template.Entity);
     177        // Replace the "{civ}" codes with this entity's civ ID
     178        var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     179        var newEntityTemplate = this.template.Entity;
     180        if (cmpIdentity)
     181            newEntityTemplate = newEntityTemplate.replace(/\{civ\}/g, cmpIdentity.GetCiv());
     182        var newEntity = Engine.AddEntity(newEntityTemplate);
    128183        if (newEntity == INVALID_ENTITY)
    129184        {
    130185            // Error (e.g. invalid template names)
     
    164219            cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
    165220        }
    166221
     222        // Transfer production queue
     223        // Note: default behaviour upon destroying an entity is to reset queue,
     224        //       that case is thus not handled here.
     225        var cmpQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
     226        var cmpNewQueue = Engine.QueryInterface(newEntity, IID_ProductionQueue);
     227        if (cmpQueue && cmpNewQueue)
     228        {
     229            var queue = cmpQueue.GetFullQueue(); // fn returns copy, not just summary
     230            if (queue.length > 0)
     231            {
     232                var transferableQueue = [];
     233                for (var i = queue.length-1; i >= 0; i--)
     234                {
     235                    var templateName = queue[i].unitTemplate ? queue[i].unitTemplate : queue[i].technologyTemplate;
     236                    if (cmpNewQueue.CanAcceptTemplate(templateName))
     237                        transferableQueue.push( queue.splice(i,1)[0] );
     238                }
     239                cmpQueue.SetQueue(queue); // set old queue with untransferable batches
     240                cmpNewQueue.SetQueue(transferableQueue); // set new queue with transferable batches
     241            }
     242        }
     243
     244        // Transfer garrisoned units if possible, or unload them
     245        var cmpGarrison = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     246        var cmpNewGarrison = Engine.QueryInterface(newEntity, IID_GarrisonHolder);
     247        if (cmpGarrison && cmpGarrison.GetEntities().length > 0)
     248        {
     249            if (cmpNewGarrison)
     250            {
     251                // Note: the code cannot rely on the garrisoning entity's indexing
     252                //       due to removal of elements. This is why a reference to the
     253                //       garrisoned entity is stored in a temporary variable.
     254                //       Also, this is why a counting down for loop is used.
     255                var garrisonedEntity,  // initiated outside for loop to reuse (reduces garbage)
     256                    garrisonedEntities = cmpGarrison.GetEntities();
     257                for (var j = garrisonedEntities.length-1; j >= 0; j--)
     258                {
     259                    garrisonedEntity = garrisonedEntities[j];
     260                    cmpGarrison.Unload(garrisonedEntity, true); // always kick out entity
     261                    cmpNewGarrison.Garrison(garrisonedEntity);  // then try to garrison
     262                }
     263            }
     264            else
     265                cmpGarrison.UnloadAll(true);
     266        }
     267       
    167268        Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity });
    168269
    169270        // Play notification sound
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    44914491UnitAI.prototype.CanPack = function()
    44924492{
    44934493    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
    4494     return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked());
     4494    return (cmpPack && !cmpPack.IsPacking() && !cmpPack.IsPacked() && cmpPack.CanTransformHere());
    44954495};
    44964496
    44974497UnitAI.prototype.CanUnpack = function()
    44984498{
    44994499    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
    4500     return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked());
     4500    return (cmpPack && !cmpPack.IsPacking() && cmpPack.IsPacked() && cmpPack.CanTransformHere());
    45014501};
    45024502
    45034503UnitAI.prototype.IsPacking = function()
  • binaries/data/mods/public/simulation/components/BuildRestrictions.js

     
    185185
    186186    var pos = cmpPosition.GetPosition2D();
    187187    var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
     188    //print('\nTileOwner: '+tileOwner);
     189    //print('\nPlayerID: '+cmpPlayer.GetPlayerID());
    188190    var isOwn = (tileOwner == cmpPlayer.GetPlayerID());
    189191    var isNeutral = (tileOwner == 0);
    190192    var isAlly = !isOwn && cmpPlayer.IsAlly(tileOwner);
  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    153153    return ret;
    154154};
    155155
     156/*
     157 * Returns true if the given template can be produced by this entity
     158 * @param templateName String of the template name
     159 */
     160ProductionQueue.prototype.CanAcceptTemplate = function(templateName)
     161{
     162    var unitTemplates = this.GetEntitiesList();
     163    if (unitTemplates.indexOf(templateName) !== -1)
     164        return true;
     165
     166    var techTemplates = this.GetTechnologiesList();
     167    for (var tech in techTemplates)
     168    {
     169        if (typeof tech === 'string' && tech === templateName)
     170            return true;
     171        else if (typeof tech === 'object' && (tech.top === templateName || tech.bottom === templateName))
     172            return true;
     173    }
     174
     175    return false;
     176};
     177
    156178ProductionQueue.prototype.IsTechnologyResearchedOrInProgress = function(tech)
    157179{
    158180    if (!tech)
     
    386408};
    387409
    388410/*
     411 * Returns a copy of the batches in the production queue
     412 * Compare with GetQueue function, which returns only basic data.
     413 */
     414ProductionQueue.prototype.GetFullQueue = function()
     415{
     416    var out = [];
     417    for each (var item in this.queue)
     418    {
     419        var batch = {
     420            "id": item.id,
     421            "player": item.player,
     422            "count": item.count,
     423            "metadata": item.metadata,
     424            "resources": item.resources, // need to copy to avoid serialization problems
     425            "population": item.population,
     426            "productionStarted": item.productionStarted,
     427            "timeTotal": item.timeTotal,
     428            "timeRemaining": item.timeRemaining
     429        };
     430        if (item.unitTemplate)
     431            batch["unitTemplate"] = item.unitTemplate;
     432        if (item.technologyTemplate)
     433            batch["technologyTemplate"] = item.technologyTemplate;
     434        out.push(batch);
     435    }
     436    return out;
     437};
     438
     439/*
    389440 * Removes all existing batches from the queue.
    390441 */
    391442ProductionQueue.prototype.ResetQueue = function()
     
    399450};
    400451
    401452/*
     453 * Swaps the current queue for a new one. Only use this functionality
     454 * to transfer a queue from one entity to another without rechecking validity.
     455 * This is useful when swapping one entity for another, e.g. when transforming.
     456 * For other uses, please go with the other functions in this Component.
     457 *
     458 * @param queue New queue which replaces the current one.
     459 */
     460ProductionQueue.prototype.SetQueue = function(queue)
     461{
     462    if (queue && typeof queue === 'object' && queue.length) // checks for type array
     463    {
     464        this.queue = queue;
     465        Engine.PostMessage(this.entity, MT_ProductionQueueChanged, {});
     466        if (this.queue.length > 0 && !this.timer) // if 0 timer will expire on its own
     467        {
     468            var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     469            this.timer = cmpTimer.SetTimeout(this.entity, IID_ProductionQueue, "ProgressTimeout", g_ProgressInterval, {});
     470        }
     471    }
     472};
     473
     474/*
    402475 * Returns batch build time.
    403476 */
    404477ProductionQueue.prototype.GetBatchTime = function(batchSize)