Ticket #3586: petra_headquarters_template.patch

File petra_headquarters_template.patch, 14.0 KB (added by otero, 8 years ago)
  • binaries/data/mods/public/simulation/ai/petra/headquarters.js

     
    1717m.HQ = function(Config)
    1818{
    1919    this.Config = Config;
    20    
     20
    2121    this.econState = "growth";  // existing values: growth, townPhasing.
    2222    this.currentPhase = undefined;
    2323
    2424    // cache the rates.
    25     this.turnCache = {};   
     25    this.turnCache = {};
    2626    this.wantedRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
    2727    this.currentRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
    28     this.lastFailedGather = { "wood": undefined, "stone": undefined, "metal": undefined }; 
     28    this.lastFailedGather = { "wood": undefined, "stone": undefined, "metal": undefined };
    2929
    3030    // workers configuration
    3131    this.targetNumWorkers = this.Config.Economy.targetNumWorkers;
     
    123123        let ent = gameState.getEntityById(evt.entity);
    124124        if (!ent || !ent.isOwn(PlayerID))
    125125            continue;
    126            
     126
    127127        if (ent.getMetadata(PlayerID, "base") == -1)
    128128        {
    129129            // Okay so let's try to create a new base around this.
     
    211211            ent.setMetadata(PlayerID, "role", undefined);
    212212            ent.setMetadata(PlayerID, "subrole", undefined);
    213213            ent.setMetadata(PlayerID, "plan", undefined);
    214             ent.setMetadata(PlayerID, "PartOfArmy", undefined);         
     214            ent.setMetadata(PlayerID, "PartOfArmy", undefined);
    215215            if (ent.hasClass("Trader"))
    216216            {
    217217                ent.setMetadata(PlayerID, "role", "trader");
    218218                ent.setMetadata(PlayerID, "route", undefined);
    219219            }
    220             if (ent.hasClass("Female") || ent.hasClass("CitizenSoldier"))
     220            if (ent.hasClass("Support") || ent.hasClass("CitizenSoldier"))
    221221            {
    222222                ent.setMetadata(PlayerID, "role", "worker");
    223223                ent.setMetadata(PlayerID, "subrole", "idle");
     
    335335                this.decayingStructures.add(evt.entity);
    336336        }
    337337        else if (ent.isGarrisonHolder())
    338             this.garrisonManager.removeDecayingStructure(evt.entity);       
     338            this.garrisonManager.removeDecayingStructure(evt.entity);
    339339    }
    340340
    341341    // then deals with decaying structures
     
    381381{
    382382    if (this.Config.difficulty > 2 && this.femaleRatio > 0.4)
    383383        this.femaleRatio = 0.4;
    384        
    385     var phaseName = gameState.getTemplate(gameState.townPhase()).name(); 
    386     m.chatNewPhase(gameState, phaseName, true); 
     384
     385    var phaseName = gameState.getTemplate(gameState.townPhase()).name();
     386    m.chatNewPhase(gameState, phaseName, true);
    387387};
    388388
    389389// Called by the "city phase" research plan once it's started
     
    394394
    395395    // increase the priority of defense buildings to free this queue for our first fortress
    396396    gameState.ai.queueManager.changePriority("defenseBuilding", 2*this.Config.priorities.defenseBuilding);
    397    
    398     var phaseName = gameState.getTemplate(gameState.cityPhase()).name();   
    399     m.chatNewPhase(gameState, phaseName, true); 
     397
     398    var phaseName = gameState.getTemplate(gameState.cityPhase()).name();
     399    m.chatNewPhase(gameState, phaseName, true);
    400400};
    401401
    402402// This code trains females and citizen workers, trying to keep close to a ratio of females/CS
    403 // TODO: this should choose a base depending on which base need workers
    404 // TODO: also there are several things that could be greatly improved here.
    405403m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
    406404{
    407     // Count the workers in the world and in progress
    408     var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen"), true);
     405    // default template
     406    var requirementsDef = [["cost", 1], ["costsResource", 1, "food"]];
     407    var classesDef = ["Support"];
     408    var templateDef = this.findBestTrainableUnit(gameState, classesDef, requirementsDef);
    409409
     410    // Count the workers in the world and in progress
     411    var numFemales = gameState.countEntitiesAndQueuedByType(templateDef);
     412
    410413    // counting the workers that aren't part of a plan
    411414    var numWorkers = 0;
    412415    gameState.getOwnUnits().forEach (function (ent) {
     
    453456    if (numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
    454457        return;
    455458
    456     // default template
    457     var templateDef = gameState.applyCiv("units/{civ}_support_female_citizen");
    458459    // Choose whether we want soldiers instead.
    459460    let femaleRatio = (gameState.isDisabledTemplates(gameState.applyCiv("structures/{civ}_field")) ? Math.min(this.femaleRatio, 0.2) : this.femaleRatio);
    460461    let template;
    461     if (!gameState.templates[templateDef] || ((numFemales+numQueuedF) > 8 && (numFemales+numQueuedF)/numTotal > femaleRatio))
     462    if ((numFemales+numQueuedF) > 8 && (numFemales+numQueuedF)/numTotal > femaleRatio)
    462463    {
    463464        let requirements;
    464465        if (numTotal < 45)
     
    466467        else
    467468            requirements = [ ["strength", 1] ];
    468469
    469         let proba = Math.random();
    470         if (proba < 0.6)
    471         {   // we require at least 30% ranged and 30% melee
    472             let classes = proba < 0.3 ? ["CitizenSoldier", "Infantry", "Ranged"] : ["CitizenSoldier", "Infantry", "Melee"];
    473             template = this.findBestTrainableUnit(gameState, classes, requirements);
    474         }
    475         if (!template)
    476             template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], requirements);
     470        var classes = ["CitizenSoldier", "Infantry"];
     471        let proba = Math.random();
     472        //  we require at least 30% ranged and 30% melee
     473        if ( proba < 0.3 )
     474        {
     475            classes.push("Ranged")
     476        }
     477        else if ( proba < 0.6 )
     478        {
     479            classes.push("Melee");
     480        }
     481        template = this.findBestTrainableUnit(gameState, classes, requirements);
    477482    }
    478     if (!template && gameState.templates[templateDef])
    479         template = templateDef;
    480483
    481     // base "0" means "auto"
    482     if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
    483         queues.villager.addPlan(new m.TrainingPlan(gameState, template, { "role": "worker", "base": 0 }, size, size));
    484     else if (template)
    485         queues.citizenSoldier.addPlan(new m.TrainingPlan(gameState, template, { "role": "worker", "base": 0 }, size, size));
     484    // If the template variable is empty, the default unit (Support unit)
     485    // would be used in the next turn
     486    //
     487    // base "0" means "auto"
     488    if (!template && gameState.templates[templateDef])
     489    {
     490        queues.villager.addPlan(new m.TrainingPlan(gameState, templateDef, { "role": "worker", "base": 0 }, size, size));
     491    }
     492    else if (template)
     493    {
     494        queues.citizenSoldier.addPlan(new m.TrainingPlan(gameState, template, { "role": "worker", "base": 0 }, size, size));
     495    }
    486496};
    487497
    488498// picks the best template based on parameters and classes
     
    495505        units = gameState.findTrainableUnits(classes, ["SiegeTower"]);
    496506    else                        // We do not want hero when not explicitely specified
    497507        units = gameState.findTrainableUnits(classes, ["Hero"]);
    498    
     508
    499509    if (units.length == 0)
    500510        return undefined;
    501511
     
    504514    var availableResources = gameState.ai.queueManager.getAvailableResources(gameState); // available (gathered) resources
    505515    for (let type in remainingResources)
    506516    {
    507         if (type === "food")
    508             continue;
    509517        if (availableResources[type] > 800)
    510518            continue;
    511519        if (remainingResources[type] > 800)
     
    515523        for (let param of parameters)
    516524        {
    517525            if (param[0] !== "costsResource" || param[2] !== type)
    518                 continue;           
     526                continue;
    519527            param[1] = Math.min( param[1], costsResource );
    520528            toAdd = false;
    521529            break;
     
    547555                aTopParam += a[1].walkSpeed() * param[1];
    548556                bTopParam += b[1].walkSpeed() * param[1];
    549557            }
    550            
     558
    551559            if (param[0] == "cost") {
    552560                aDivParam += a[1].costSum() * param[1];
    553561                bDivParam += b[1].costSum() * param[1];
     
    638646        }
    639647        this.turnCache.gatherRates = true;
    640648    }
    641        
     649
    642650    return this.currentRates;
    643651};
    644652
     
    662670    needed.sort(function(a, b) {
    663671        var va = (Math.max(0, a.wanted - a.current))/ (a.current+1);
    664672        var vb = (Math.max(0, b.wanted - b.current))/ (b.current+1);
    665        
     673
    666674        // If they happen to be equal (generally this means "0" aka no need), make it fair.
    667675        if (va === vb)
    668676            return (a.current - b.current);
     
    674682// Returns the best position to build a new Civil Centre
    675683// Whose primary function would be to reach new resources of type "resource".
    676684m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource, proximity, fromStrategic)
    677 {   
     685{
    678686    // This builds a map. The procedure is fairly simple. It adds the resource maps
    679687    //  (which are dynamically updated and are made so that they will facilitate DP placement)
    680688    // Then checks for a good spot in the territory. If none, and town/city phase, checks outside
     
    721729    var cellSize = this.territoryMap.cellSize;
    722730
    723731    for (let j = 0; j < this.territoryMap.length; ++j)
    724     {   
     732    {
    725733        if (this.territoryMap.getOwnerIndex(j) != 0)
    726734            continue;
    727735        // with enough room around to build the cc
     
    739747        // checking distance to other cc
    740748        let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
    741749
    742         if (proximity)  // this is our first cc, let's do it near our units 
     750        if (proximity)  // this is our first cc, let's do it near our units
    743751        {
    744752            let dist = API3.SquareVectorDistance(proximity, pos);
    745753            norm /= (1 + dist/scale);
     
    806814
    807815        if (this.borderMap.map[j] > 0)  // disfavor the borders of the map
    808816            norm *= 0.5;
    809        
     817
    810818        let val = 2*gameState.sharedScript.CCResourceMaps[resource].map[j] +
    811819                gameState.sharedScript.CCResourceMaps.wood.map[j] +
    812820                gameState.sharedScript.CCResourceMaps.stone.map[j] +
     
    852860// Returns the best position to build a new Civil Centre
    853861// Whose primary function would be to assure territorial continuity with our allies
    854862m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
    855 {   
     863{
    856864    // This builds a map. The procedure is fairly simple.
    857865    // We minimize the Sum((dist-300)**2) where the sum is on the three nearest allied CC
    858866    // with the constraints that all CC have dist > 200 and at least one have dist < 400
     
    954962        }
    955963        // disfavor border of the map
    956964        if (this.borderMap.map[j] > 0)
    957             currentVal += 10000;       
     965            currentVal += 10000;
    958966
    959967        if (bestVal !== undefined && currentVal > bestVal)
    960968            continue;
     
    965973    }
    966974
    967975    if (this.Config.debug > 1)
    968         API3.warn("We've found a strategic base with bestVal = " + bestVal);   
     976        API3.warn("We've found a strategic base with bestVal = " + bestVal);
    969977
    970978    Engine.ProfileStop();
    971979
     
    10611069    }
    10621070
    10631071    if (this.Config.debug > 1)
    1064         API3.warn("We found a market position with bestVal = " + bestVal); 
     1072        API3.warn("We found a market position with bestVal = " + bestVal);
    10651073
    10661074    if (bestVal === undefined)  // no constraints. For the time being, place it arbitrarily by the ConstructionPlan
    10671075        return [-1, -1, -1, 0];
     
    10691077    var expectedGain = Math.round(bestVal / this.Config.distUnitGain);
    10701078    if (this.Config.debug > 1)
    10711079        API3.warn("this would give a trading gain of " + expectedGain);
    1072     // do not keep it if gain is too small, except if this is our first BarterMarket 
     1080    // do not keep it if gain is too small, except if this is our first BarterMarket
    10731081    if (expectedGain < this.tradeManager.minimalGain ||
    10741082        (expectedGain < 8 && (!template.hasClass("BarterMarket") || gameState.getOwnEntitiesByClass("BarterMarket", true).length > 0)))
    10751083        return false;
     
    10821090// Returns the best position to build defensive buildings (fortress and towers)
    10831091// Whose primary function is to defend our borders
    10841092m.HQ.prototype.findDefensiveLocation = function(gameState, template)
    1085 {   
     1093{
    10861094    // We take the point in our territory which is the nearest to any enemy cc
    10871095    // but requiring a minimal distance with our other defensive structures
    10881096    // and not in range of any enemy defensive structure to avoid building under fire.
     
    11601168            if (!strPos)
    11611169                continue;
    11621170            let dist = API3.SquareVectorDistance(strPos, pos);
    1163             if (dist < 6400) //  TODO check on true attack range instead of this 80*80 
     1171            if (dist < 6400) //  TODO check on true attack range instead of this 80*80
    11641172            {
    11651173                minDist = -1;
    11661174                break;
     
    13321340        if (gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length >= requirements.number)
    13331341            this.requireHouses = undefined;
    13341342    }
    1335    
     1343
    13361344    // When population limit too tight
    13371345    //    - if no room to build, try to improve with technology
    13381346    //    - otherwise increase temporarily the priority of houses
     
    14511459    }
    14521460
    14531461    if (gameState.currentPhase() < 2 || !this.canBuild(gameState, "structures/{civ}_defense_tower"))
    1454         return; 
     1462        return;
    14551463
    14561464    let numTowers = gameState.getOwnEntitiesByClass("DefenseTower", true).filter(API3.Filters.byClass("Town")).length;
    14571465    if ((!numTowers || gameState.ai.elapsedTime > (1 + 0.10*numTowers)*this.towerLapseTime + this.towerStartTime) &&
     
    15781586/**
    15791587 * train with highest priority ranged infantry in the nearest civil centre from a given set of positions
    15801588 * and garrison them there for defense
    1581  */ 
     1589 */
    15821590m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
    15831591{
    15841592    if (gameState.ai.queues.emergency.hasQueuedUnits())
     
    16851693
    16861694m.HQ.prototype.canBuild = function(gameState, structure)
    16871695{
    1688     var type = gameState.applyCiv(structure); 
     1696    var type = gameState.applyCiv(structure);
    16891697    // available room to build it
    16901698    if (this.stopBuilding.has(type))
    16911699    {
     
    19131921    if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID] !== undefined)
    19141922        ++this.turnCache.resourceGatherer[supplyID];
    19151923    else
    1916     { 
     1924    {
    19171925        if (!this.turnCache.resourceGatherer)
    19181926            this.turnCache.resourceGatherer = {};
    19191927        this.turnCache.resourceGatherer[supplyID] = 1;
     
    19861994        this.currentPhase = gameState.currentPhase();
    19871995        let phaseName = "Unknown Phase";
    19881996        if (this.currentPhase == 2)
    1989             phaseName = gameState.getTemplate(gameState.townPhase()).name(); 
     1997            phaseName = gameState.getTemplate(gameState.townPhase()).name();
    19901998        else if (this.currentPhase == 3)
    19911999            phaseName = gameState.getTemplate(gameState.cityPhase()).name();
    1992            
    1993         m.chatNewPhase(gameState, phaseName, false); 
     2000
     2001        m.chatNewPhase(gameState, phaseName, false);
    19942002        this.updateTerritories(gameState);
    19952003    }
    19962004    else if (gameState.ai.playedTurn - this.lastTerritoryUpdate > 100)