home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2003 January / maximum-cd-2003-01.iso / Software / Games / AoM / mtrial.exe / AOM / AI / SCN25P2.XS < prev    next >
Encoding:
Text File  |  2002-09-06  |  27.5 KB  |  795 lines

  1. //==============================================================================
  2. // Scn25p2: AI Scenario Script for scenario 25 player 2
  3. //==============================================================================
  4. /*
  5.    AI owner:  Dave Leary
  6.    Scenario owner: Joe "the Golem" Gillum
  7.  
  8.     Overview: A semi-cheating AI for the "back field" main player in scenario
  9.     25.  This guy does actually use villagers and such, but on higher levels and
  10.     at particular points, he'll get extra resources to kick ass with.  Note that
  11.     some difficulty-level adjustments for this scenario are made in the other AIs
  12.     as well (for players 3 and 4).
  13.  
  14.                                   *** DIFFICULTY LEVEL NOTES ***
  15.  
  16.    Easy level - Fewer researches.
  17.  
  18.    Moderate level - Base level.
  19.  
  20.    Difficult level - More ulfsarks and wolves. More resource cheating.
  21.  
  22.    Nightmare - More ulfsarks and wolves. More resource cheating.  Possible
  23.     Fimbulwinter (ow).
  24. */
  25. //==============================================================================
  26. // Need scn lib for some stuff here.
  27. include "scn lib.xs";
  28.  
  29. // Variable for main base.
  30. int gMainBaseID=-1;
  31.  
  32.  
  33. //==============================================================================
  34. // Set Town Location
  35. //==============================================================================
  36. void setTownLocation(void)
  37. {
  38.    //Look for the "Town Location" marker.
  39.    kbSetTownLocation(kbGetBlockPosition("8088"));
  40. }
  41.  
  42. //==============================================================================
  43. // miscStartup
  44. //==============================================================================
  45. void miscStartup(void)
  46. {
  47.     // Difficulty Level check.
  48.     int difflevel=-1;        
  49.     difflevel=aiGetWorldDifficulty();
  50.  
  51.    //Startup message(s).
  52.    aiEcho("");
  53.    aiEcho("");
  54.    aiEcho("Scn25P2 AI Start, filename='"+cFilename+"'.");
  55.     aiEcho("Difficulty Level="+difflevel+".");
  56.    //Spit out the map size.
  57.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  58.    //Cheat like a bastard.  Once only, though.
  59.    kbLookAtAllUnitsOnMap();
  60.    //Calculate some areas.
  61.    kbAreaCalculate(1200.0);
  62.    //Set our town location.
  63.    setTownLocation();
  64.     //Reset random seed
  65.     aiRandSetSeed();
  66.  
  67.     //Set the base location.
  68.     gMainBaseID=kbBaseGetMainID(cMyID);
  69.  
  70.     //Workarounds for crazy Ruediger bugs.
  71.     kbTechTreeAddMinorGodPref( cTechAge3Njord );
  72.     kbTechTreeAddMinorGodPref( cTechAge4Tyr );
  73. }
  74.  
  75. //==============================================================================
  76. //==============================================================================
  77. // Attack stuff.
  78. //==============================================================================
  79. //==============================================================================
  80. //Shared variables.
  81. int numberAttacks=0;
  82. int attackPlayerID=-1;
  83. bool goingAge4=false;
  84.  
  85. //TODO: Decide how to rep attack group size.
  86. //int attackMinimumGroupSize=3;
  87. //int attackMaximumGroupSize=5;
  88.  
  89. //Attack 1 vars.
  90. int attackPlan1ID=-1;
  91.  
  92. //Attack 2 vars.
  93. int attackPlan2ID=-1;
  94.  
  95. // Route and path vars
  96. int attackRoute1ID=-1;
  97. int attackPath1ID=-1;
  98. int attackRoute2ID=-1;
  99. int attackPath2ID=-1;
  100.  
  101. // Saved plan IDs
  102. int maintainPlan1ID=-1;
  103. int maintainPlan2ID=-1;
  104. int maintainPlan3ID=-1;
  105. int maintainPlan4ID=-1;
  106. int exploreID1=-1;
  107.  
  108. // Unit types
  109. int attackerUnitTypeID1=cUnitTypeVillagerNorse;
  110. int attackerUnitTypeID2=cUnitTypeUlfsark;
  111. int attackerUnitTypeID3=cUnitTypeRaidingCavalry;
  112. int attackerUnitTypeID4=cUnitTypeFenrisWolf;
  113.  
  114. // Initial gather percentages
  115. float totalFoodGathererPercentage  = 0.6;
  116. float totalWoodGathererPercentage  = 0.2;
  117. float totalGoldGathererPercentage  = 0.2;
  118. float totalFavorGathererPercentage = 0.0;
  119.  
  120.  
  121. //==============================================================================
  122. // initMainBase - Mike's spiffy function to relocate the main base.
  123. //==============================================================================
  124. void initMainBase()
  125. {
  126.     vector basePosition=kbGetBlockPosition("8153");
  127.  
  128.    // Nuke bases, add one base to rule them all
  129.    kbBaseDestroyAll(cMyID);
  130.  
  131.    gMainBaseID = kbBaseCreate(cMyID, "Base "+kbBaseGetNextID(), basePosition, 50.0);
  132.    
  133.     if (gMainBaseID < 0)
  134.       aiEcho("***** Main base creation failed. *****");
  135.  
  136.    vector baseFront=xsVectorNormalize(kbGetMapCenter()-basePosition);     // Set front
  137.    kbBaseSetFrontVector(cMyID, gMainBaseID, baseFront);                 
  138.    kbBaseSetMaximumResourceDistance(cMyID, gMainBaseID, 50.0);
  139.    kbBaseSetMain(cMyID, gMainBaseID, true);     // Make this the main base
  140.  
  141.    // Add the buildings
  142.    int buildingQuery = -1;
  143.    int count = 0;
  144.    buildingQuery = kbUnitQueryCreate("Building Query");     // All buildings in the base
  145.    configQuery(buildingQuery, cUnitTypeBuilding, -1, cUnitStateAliveOrBuilding, cMyID, basePosition, false, 50.0);
  146.    kbUnitQueryResetResults(buildingQuery);
  147.    count = kbUnitQueryExecute(buildingQuery);
  148.  
  149.    int i = 0;
  150.    int buildingID = -1;
  151.     echoQuery(buildingID);
  152.    for (i=0; < count)
  153.    {
  154.       buildingID = kbUnitQueryGetResult(buildingQuery, i);
  155.       // Add it to the base
  156.       kbBaseAddUnit( cMyID, gMainBaseID, buildingID );
  157.    }
  158. }
  159.  
  160. //==============================================================================
  161. // initEcon
  162. //
  163. // Set Up the initial Econonomy.
  164. //==============================================================================
  165. void initEcon()
  166. {
  167.    aiEcho("Economy Init.");
  168.  
  169.     /* Don't need this for what we're doing here.
  170.    // Set our update resource handler.
  171.    aiSetUpdateResourceEventHandler("updateResourceHandler");
  172.     */
  173.  
  174.     // Set oxcarts as the trainable dropsite.
  175.     aiSetTrainedDropsiteUnitTypeID(cUnitTypeOxCart);
  176.  
  177.    //-- Setup AI Cost weights.
  178.    kbSetAICostWeight(cResourceFood, 1.5);
  179.    kbSetAICostWeight(cResourceWood, 1.0);
  180.    kbSetAICostWeight(cResourceGold, 1.5);
  181.    kbSetAICostWeight(cResourceFavor, 10.0);
  182.  
  183.    //-- Dont auto gather favor
  184.    //totalFavorGathererPercentage = 0;
  185.  
  186.    //-- Set initial gatherer percentages.
  187.    aiSetResourceGathererPercentage(cResourceFood, totalFoodGathererPercentage, false, cRGPScript);
  188.    aiSetResourceGathererPercentage(cResourceWood, totalWoodGathererPercentage, false, cRGPScript);
  189.    aiSetResourceGathererPercentage(cResourceGold, totalGoldGathererPercentage, false, cRGPScript);
  190.    aiSetResourceGathererPercentage(cResourceFavor, totalFavorGathererPercentage, false, cRGPScript);
  191.    aiNormalizeResourceGathererPercentages(cRGPScript);
  192.  
  193.    //-- Set up the initial resource subtype break downs.
  194.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, 1, 50, 0.4, gMainBaseID);
  195.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, 1, 50, 0.1, gMainBaseID);
  196.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 50, 0.5, gMainBaseID);
  197.     aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  198.     aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  199.    aiSetResourceBreakdown(cResourceFavor, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  200.     
  201.    //-- Set up auto-gather escrows
  202.    aiSetAutoGatherEscrowID(cRootEscrowID);
  203.    aiSetAutoFarmEscrowID(cRootEscrowID);
  204.  
  205.     //Allocate all resources to the root escrow by setting percentage of military/economy to 0.
  206.     kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 );
  207.     kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 );
  208.  
  209.     //-- create a herd plan to gather all herdables that we ecounter.
  210.    int herdPlanID=aiPlanCreate("HerdTest", cPlanHerd);
  211.    if (herdPlanID >= 0)
  212.    {
  213.       aiPlanAddUnitType(herdPlanID, cUnitTypeHerdable, 0, 100, 100);
  214.       aiPlanSetVariableInt(herdPlanID, cHerdPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1);
  215.       aiPlanSetActive(herdPlanID);
  216.    }
  217.  
  218.     //Allocate all resources
  219.    kbEscrowAllocateCurrentResources();
  220. }
  221.  
  222. //==============================================================================
  223. // initAttack: Creates attack routes, etc.
  224. //==============================================================================
  225. void initAttack(int playerID=-1)
  226. {
  227.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  228.    if (playerID != attackPlayerID)
  229.    {
  230.       //Reset the attack player ID.
  231.       attackPlayerID=-1;
  232.       //Destroy any previous attack plan.
  233.       aiPlanDestroy(attackPlan1ID);
  234.       attackPlan1ID=-1;
  235.       aiPlanDestroy(attackPlan2ID);
  236.       attackPlan2ID=-1;
  237.   
  238.       //Destroy our previous attack paths.
  239.       kbPathDestroy(attackPath1ID);
  240.       attackPath1ID=-1;
  241.       kbPathDestroy(attackPath2ID);
  242.       attackPath2ID=-1;
  243.  
  244.       //Destroy our previous attack routes.
  245.       attackRoute1ID=-1;
  246.       attackRoute2ID=-1;
  247.  
  248.       //Reset the number of attacks.
  249.       numberAttacks=0;
  250.    }
  251.  
  252.    //Save the player to attack.
  253.    attackPlayerID=playerID;
  254. }
  255.  
  256. //==============================================================================
  257. // setupAttack1 - Ulfsark Attack
  258. //==============================================================================
  259. bool setupAttack1(int playerID=-1)
  260. {
  261.     // Difficulty Level check.
  262.     int difflevel=-1;        
  263.     difflevel=aiGetWorldDifficulty();
  264.  
  265.    //Info.
  266.     aiEcho("Ulfsarks attacking Player "+playerID+".");
  267.  
  268.    //If the player to attack doesn't match, init the attack.
  269.    if (attackPlayerID != playerID)
  270.    {
  271.       initAttack(playerID);
  272.       if (attackPlayerID < 0)
  273.          return(false);
  274.    }
  275.  
  276.    //Create an attack plan.
  277.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  278.    if (newAttackPlanID < 0)
  279.       return(false);
  280.  
  281.    //Target player (required).  This must work.
  282.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  283.       return(false);
  284.  
  285.    //Gather point.
  286.     vector gatherPoint=kbGetBlockPosition("8088");
  287.  
  288.     //Set the target type.  This must work.
  289.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  290.       return(false);
  291.  
  292.    //Unit types to attack.
  293.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  294.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  295.  
  296.    //Set the gather point and gather point distance.
  297.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  298.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0);
  299.  
  300.     //Add the unit types to the plan - ulfsarks, at least four (depending on age)
  301.     if ( goingAge4 == true || difflevel > 1 )
  302.     {
  303.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 8, 10, 16);
  304.     }
  305.     else
  306.     {
  307.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 4, 4, 8);
  308.     }
  309.     
  310.     //Set the initial position.
  311.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  312.    //Plan requires all need units to work (can be false).
  313.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  314.    //Activate the plan.
  315.    aiPlanSetActive(newAttackPlanID);
  316.  
  317.    //Now, save the attack plan ID appropriately.
  318.    aiPlanSetOrphan(attackPlan1ID, true);
  319.    attackPlan1ID=newAttackPlanID;
  320.  
  321.    //Increment our overall number of attacks.
  322.    numberAttacks++;
  323. }
  324.  
  325.  
  326. //==============================================================================
  327. // setupAttack2 - Wolf Attack
  328. //==============================================================================
  329. bool setupAttack2(int playerID=-1)
  330. {
  331.     // Difficulty Level check.
  332.     int difflevel=-1;        
  333.     difflevel=aiGetWorldDifficulty();
  334.  
  335.    //Info.
  336.     aiEcho("Wolves attacking Player "+playerID+".");
  337.  
  338.    //If the player to attack doesn't match, init the attack.
  339.    if (attackPlayerID != playerID)
  340.    {
  341.       initAttack(playerID);
  342.       if (attackPlayerID < 0)
  343.          return(false);
  344.    }
  345.  
  346.    //Create an attack plan.
  347.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  348.    if (newAttackPlanID < 0)
  349.       return(false);
  350.  
  351.    //Target player (required).  This must work.
  352.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  353.       return(false);
  354.  
  355.    //Gather point.
  356.     vector gatherPoint=kbGetBlockPosition("8153");
  357.  
  358.     //Set the target type.  This must work.
  359.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  360.       return(false);
  361.  
  362.    //Unit types to attack.
  363.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  364.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  365.  
  366.    //Set the gather point and gather point distance.
  367.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  368.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0);
  369.  
  370.     //Add the unit types to the plan - wolves
  371.     if ( difflevel < 2 )
  372.     {
  373.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 4, 4, 4);
  374.     }
  375.     else
  376.     {
  377.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 4, 8, 8);
  378.     }
  379.  
  380.    //Set the initial position.
  381.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  382.    //Plan requires all need units to work (can be false).
  383.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  384.    //Activate the plan.
  385.    aiPlanSetActive(newAttackPlanID);
  386.  
  387.    //Now, save the attack plan ID appropriately.
  388.    aiPlanSetOrphan(attackPlan2ID, true);
  389.    attackPlan2ID=newAttackPlanID;
  390.  
  391.    //Increment our overall number of attacks.
  392.    numberAttacks++;
  393. }
  394.  
  395. //==============================================================================
  396. // createUnitProgression
  397. //==============================================================================
  398. int createUnitProgression(int unitTypeID=-1, string name="BUG")
  399. {
  400.    int pID=aiPlanCreate(name, cPlanProgression);
  401.    
  402.     if (pID < 0)
  403.       return(-1);
  404.  
  405.    //This is a military plan.
  406.    //aiPlanSetMilitary(pID, true);
  407.    //Set it for the building that we get our unit from.
  408.    aiPlanSetVariableInt(pID, cProgressionPlanGoalUnitID, 0, kbTechTreeGetUnitIDByTrain(unitTypeID, cMyCiv));
  409.    
  410.     //Build it in our main base using the root escrow.
  411.     aiPlanSetBaseID(pID, gMainBaseID);
  412.     aiPlanSetEscrowID(pID, cRootEscrowID);
  413.    
  414.     //Go.
  415.    aiPlanSetActive(pID);
  416.    return(pID);
  417. }
  418.  
  419. //==============================================================================
  420. // createTechProgression
  421. //==============================================================================
  422. int createTechProgression(int techID=-1, string name="BUG", int researchFromProto=-1)
  423. {
  424.    //Check for old plan.
  425.     int oldPlanID=aiPlanGetIDByTypeAndVariableType(cPlanProgression, cProgressionPlanGoalTechID, techID);
  426.    if(oldPlanID != -1)
  427.    {
  428.       aiEcho("createTechProgression: already have a plan("+oldPlanID+") for this Tech("+techID+").");
  429.       return(oldPlanID);
  430.    }
  431.    
  432.     //Create a new one.
  433.    int pID=aiPlanCreate(name, cPlanProgression);
  434.    if (pID < 0)
  435.    {
  436.       aiEcho("createTechProgression: couldn't create Progression.");
  437.       return(-1);
  438.    }
  439.    //This is a military plan.
  440.    //aiPlanSetMilitary(pID, true);
  441.    aiPlanSetVariableInt(pID, cProgressionPlanGoalTechID, 0, techID);
  442.    aiPlanSetVariableInt(pID, cProgressionPlanBuildingPref, 0, researchFromProto);
  443.    
  444.     //Build it in our main base using the root escrow.
  445.    aiPlanSetBaseID(pID, gMainBaseID);
  446.     aiPlanSetEscrowID(pID, cRootEscrowID);
  447.  
  448.    //Go.
  449.    aiPlanSetActive(pID);
  450.    aiEcho("createTechProgression: creating Tech Progression("+name+") to TechID("+techID+").");
  451.    return(pID);
  452. }
  453.  
  454. //==============================================================================
  455. // Attack Generator 1 - Send dudes now!  Or, well, soon.
  456. //==============================================================================
  457. rule attackGenerator1
  458.    minInterval 90
  459.    inactive
  460.    group AttackRules
  461. {
  462.     // Difficulty Level check.
  463.     int difflevel=-1;        
  464.     difflevel=aiGetWorldDifficulty();
  465.  
  466.    //See how many "idle" attack plans we have.  Don't create any more if we have
  467.    //idle plans.
  468.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  469.  
  470.    if (numberIdleAttackPlans > 0)
  471.       return;
  472.  
  473.    //If we have enough unassigned military units, create a new attack plan.
  474.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2);
  475.    aiEcho("There are "+numberAvailableUnits+" ulfsarks available for a new attack.");
  476.    
  477.     if ( goingAge4 == true || difflevel > 1 )
  478.     {
  479.         if (numberAvailableUnits >= 8 )
  480.             setupAttack1(1);
  481.     }
  482.     else 
  483.     {
  484.         if (numberAvailableUnits >= 4 )
  485.             setupAttack1(1);
  486.     }
  487. }
  488.  
  489. //==============================================================================
  490. // Attack Generator 2 - Send wolves
  491. //==============================================================================
  492. rule attackGenerator2
  493.    minInterval 120
  494.    inactive
  495.    group AttackRules
  496. {
  497.    //See how many "idle" attack plans we have.  Don't create any more if we have
  498.    //idle plans.
  499.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  500.  
  501.    if (numberIdleAttackPlans > 0)
  502.       return;
  503.  
  504.    //If we have enough unassigned military units, create a new attack plan.
  505.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID4);
  506.    aiEcho("There are "+numberAvailableUnits+" wolves available for a new attack.");
  507.    
  508.     if (numberAvailableUnits >= 4)
  509.         setupAttack2(1);
  510. }
  511.  
  512. //==============================================================================
  513. // Fimbulwinter Fun
  514. //==============================================================================
  515. rule fimbulwinterFun
  516.     minInterval 240
  517.     inactive
  518.     group GodPowers
  519. {
  520.     vector fimbulPoint=kbGetBlockPosition("8088");
  521.     aiEcho("*** TIME FOR FIMBULWINTER - EAT IT!!! ***");
  522.     if ( aiCastGodPowerAtPosition(cTechSnowStorm, fimbulPoint) == true )
  523.         xsDisableSelf();
  524.     else
  525.         aiEcho("Fimbulwinter failed - try again later.");
  526. }
  527.  
  528. //==============================================================================
  529. // Favor cheat
  530. //==============================================================================
  531. rule favorCheat
  532.    minInterval 120
  533.    inactive
  534.    group AttackRules
  535. {
  536.     // Cheat for favor.  It's tough to be Norse.
  537.     aiResourceCheat( 2, cResourceFavor, 25.0 );
  538. }
  539.  
  540. //==============================================================================
  541. // Resource Cheat: periodic boosts at higher levels.
  542. //==============================================================================
  543. rule diffResourceCheat
  544.    minInterval 90
  545.    active
  546.    group DifficultyStuff
  547. {
  548.     // Difficulty Level check.
  549.     int difflevel=-1;        
  550.     difflevel=aiGetWorldDifficulty();
  551.  
  552.     if ( difflevel == 2 )
  553.     {
  554.         aiResourceCheat( 2, cResourceFood, 100.0 );
  555.         aiResourceCheat( 2, cResourceWood, 100.0 );
  556.         aiResourceCheat( 2, cResourceGold, 100.0 );
  557.     }
  558.     else if ( difflevel == 3 )
  559.     {
  560.         aiResourceCheat( 2, cResourceFood, 150.0 );
  561.         aiResourceCheat( 2, cResourceWood, 150.0 );
  562.         aiResourceCheat( 2, cResourceGold, 150.0 );
  563.     }
  564.     else
  565.     {
  566.         xsDisableSelf();
  567.     }
  568. }
  569.  
  570. //==============================================================================
  571. // mapCheat; look at the map at the five-minute mark (once).
  572. //==============================================================================
  573. rule mapCheat
  574.     minInterval 300
  575.     active
  576. {
  577.    kbLookAtAllUnitsOnMap();    
  578.     xsDisableSelf();
  579. }
  580.  
  581. //==============================================================================
  582. // hillFortBuilt - called with an AI FUNC when he has one hill fort.
  583. // This fires off the next progression, and starts some basic attacks up.
  584. //==============================================================================
  585. void hillFortBuilt( int parameter=-1 )
  586. {
  587.     // Slightly more villagers.
  588.     aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, 20);
  589.     
  590.     // More ulfsarks maintained.  We're going to start raiding the player with
  591.     // groups of 6-8.  We'll maintain 10 to provide for the scout plan and
  592.     // a little town defense.
  593.     aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, 10);
  594.  
  595.    //-- Mostly farms now that we've got the thing built
  596.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, 1, 50, 0.1, gMainBaseID);
  597.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, 1, 50, 0.1, gMainBaseID);
  598.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 50, 0.8, gMainBaseID);
  599.  
  600.     // Change percentage of who's on what.
  601.    aiSetResourceGathererPercentage(cResourceFood, 0.5, false, cRGPScript);
  602.    aiSetResourceGathererPercentage(cResourceWood, 0.2, false, cRGPScript);
  603.    aiSetResourceGathererPercentage(cResourceGold, 0.3, false, cRGPScript);
  604.     
  605.     // Enable basic attacks
  606.     xsEnableRule("attackGenerator1");
  607.  
  608.     // Cheat!!
  609.     aiResourceCheat( 2, cResourceFood, 750.0 );
  610.     aiResourceCheat( 2, cResourceGold, 750.0 );
  611.  
  612.     // Get some stone wall action going on
  613.     createTechProgression(cTechStoneWall, "Research Stone Walls", cUnitTypeWallLong);
  614.  
  615.     // Get better weapons
  616.     createTechProgression(cTechBronzeWeapons, "Progress to Bronze Weapons", cUnitTypeArmory);
  617.  
  618.     // Progression to Omniscience.  Should get us to Age 4, yes?
  619.     createTechProgression(cTechOmniscience, "Progress Age 4 (Omniscience)", cUnitTypeTemple);
  620.  
  621.     // Go to heavy infantry
  622.     createTechProgression(cTechHeavyInfantry, "Progress to Heavy Infantry", cUnitTypeLonghouse);
  623. }
  624.  
  625. //==============================================================================
  626. // researchingFourthAge - called with an AI FUNC when the player starts
  627. // researching fourth age (duh).  Changes up some attacks and starts maintaining
  628. // Fenris Wolves.  Also sets things up for the player to launch Fimbulwinter
  629. // four minutes from now, and shifts more wood resource gatherers to gold.
  630. //==============================================================================
  631. void researchingFourthAge( int parameter=-1 )
  632. {
  633.     // Difficulty Level check.
  634.     int difflevel=-1;        
  635.     difflevel=aiGetWorldDifficulty();
  636.  
  637.     vector gatherPointMilitary=kbGetBlockPosition("8153");
  638.  
  639.    //Maintain Fenris Wolves.
  640.    maintainPlan4ID=aiPlanCreate("Maintain 4 "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  641.    if (maintainPlan4ID >= 0)
  642.    {
  643.         //Must set the type of unit to train.
  644.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  645.       //You can limit the number of units that are ever trained by this plan with this call.
  646.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  647.       //Set the number of units to maintain in the world at one time.
  648.         if ( difflevel < 2 )
  649.         {
  650.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, 4);
  651.         }
  652.         else
  653.         {
  654.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, 8);
  655.         }
  656.       //Don't train units too quickly.
  657.         if ( difflevel < 2 )
  658.         {
  659.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 30);
  660.         }
  661.         else if ( difflevel == 2 )
  662.         {
  663.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 20);
  664.         }
  665.         else if ( difflevel == 3 )
  666.         {
  667.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 10);
  668.         }
  669.       //Set a gather point.
  670.       aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointMilitary);
  671.       //Activate the plan.
  672.       aiPlanSetActive(maintainPlan4ID);
  673.    }
  674.  
  675.     // Change percentage of who's on what.
  676.    aiSetResourceGathererPercentage(cResourceFood, 0.5, false, cRGPScript);
  677.    aiSetResourceGathererPercentage(cResourceWood, 0.1, false, cRGPScript);
  678.    aiSetResourceGathererPercentage(cResourceGold, 0.4, false, cRGPScript);
  679.  
  680.     // Cheats!!
  681.     aiResourceCheat( 2, cResourceFood, 750.0 );
  682.     aiResourceCheat( 2, cResourceWood, 750.0 );
  683.     aiResourceCheat( 2, cResourceGold, 750.0 );
  684.  
  685.     // Upgrade like mad.  Except on easy.
  686.     if ( difflevel > 0 )
  687.     {
  688.         createTechProgression(cTechIronWeapons, "Progress to Iron Weapons", cUnitTypeArmory);
  689.         createTechProgression(cTechIronMail, "Progress to Iron Mail", cUnitTypeArmory);
  690.         createTechProgression(cTechIronShields, "Progress to Iron Shields", cUnitTypeArmory);
  691.         createTechProgression(cTechChampionInfantry, "Progress to Champion Infantry", cUnitTypeLonghouse);
  692.     }
  693.  
  694.     // More ulfsarks maintained, and fewer villagers...we've already done this on higher levels.
  695.     if ( difflevel < 2 )
  696.     {
  697.         aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, 10);
  698.     }
  699.     aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, 20);
  700.  
  701.     // Start cheating for favor.
  702.     xsEnableRule("favorCheat");
  703.  
  704.     // Enable Fimbulwinter...  Nightmare only, now.
  705.     if ( difflevel == 3 )
  706.     {
  707.         xsEnableRule("fimbulwinterFun");
  708.     }
  709.  
  710.     // Enable attacks by the scary wolf things
  711.     xsEnableRule("attackGenerator2");
  712.  
  713.     // Set a saved boolean so we know what's up.
  714.     goingAge4 = true;
  715. }
  716.  
  717.  
  718. //==============================================================================
  719. // MAIN.
  720. //==============================================================================
  721. void main(void)
  722. {
  723.     // Difficulty Level check.
  724.     int difflevel=-1;        
  725.     difflevel=aiGetWorldDifficulty();
  726.  
  727.    //Startup.
  728.    miscStartup();
  729.     initMainBase();
  730.     initEcon();
  731.  
  732.    //A couple o' gather points.
  733.    vector gatherPointVillager=kbGetBlockPosition("8153");
  734.     vector gatherPointMilitary=kbGetBlockPosition("8088");
  735.  
  736.     // Cheat up some favor.  It's tough to be Norse.
  737.     aiResourceCheat( 2, cResourceFavor, 45.0 );
  738.     
  739.    //Maintain villagers.  Fifteen should be plenty.
  740.    maintainPlan1ID=aiPlanCreate("Maintain 15 "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  741.    if (maintainPlan1ID >= 0)
  742.    {
  743.         // Up the desired priority of this plan a litte.
  744.         aiPlanSetDesiredPriority(maintainPlan1ID, 60);
  745.         //Must set the type of unit to train.
  746.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  747.       //You can limit the number of units that are ever trained by this plan with this call.
  748.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  749.       //Set the number of units to maintain in the world at one time.
  750.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, 15);
  751.       //Don't train units faster than every 15 seconds
  752.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 15);
  753.       //Set a gather point.
  754.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointVillager);
  755.       //Activate the plan.
  756.       aiPlanSetActive(maintainPlan1ID);
  757.    }
  758.  
  759.     //Maintain ulfsarks!  Five for now, except on higher diff levels.
  760.    maintainPlan2ID=aiPlanCreate("Maintain 5 "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  761.    if (maintainPlan2ID >= 0)
  762.    {
  763.         //Must set the type of unit to train.
  764.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  765.       //You can limit the number of units that are ever trained by this plan with this call.
  766.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  767.       //Set the number of units to maintain in the world at one time.
  768.         if ( difflevel < 2 )
  769.         {
  770.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, 5);
  771.         }
  772.         else
  773.         {
  774.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, 12);
  775.         }
  776.       //Don't train units faster than every 20 seconds
  777.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 20);
  778.       //Set a gather point.
  779.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointMilitary);
  780.       //Activate the plan.
  781.       aiPlanSetActive(maintainPlan2ID);
  782.    }
  783.  
  784.     // Explore plan 1
  785.    exploreID1 = aiPlanCreate("Explore 1", cPlanExplore);
  786.    if(exploreID1 >= 0)
  787.    {
  788.       aiPlanAddUnitType(exploreID1, cUnitTypeUlfsark, 1, 1);
  789.       aiPlanSetActive(exploreID1);
  790.    }
  791.  
  792.     // Progress to Huskarls - kind of a hack, used to get him to build a hill fort and advance to Age 3.
  793.     createUnitProgression(cUnitTypeHuskarl, "Progression to Huskarls");
  794. }
  795.