Map Database  •  FAQ  •  RSS  •  Register  •  Login

Error in script, please help!

<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 07 Nov 2017, 20:05

Error in script, please help!

Hello everyone,

I have completed my new map, and started primary testing on server with real players. Everything was going fine, until I got an error. Sadly i cannot provide replay, i accidentally overwrote it.

The error is: EAssertionFailed: "We checked that Group is not dead, hence we should have a valid Unit."

When does this issue present itself? What does this sentence mean? I cannot figure it out...
<<

grayter

Rogue

Posts: 58

Joined: 18 Aug 2014, 12:06

Location: Poland

KaM Skill Level: Skilled

Post 07 Nov 2017, 21:18

Re: Error in script, please help!

Please attach your script file, I'll check if there are some commands which cause the problem. Are you using r6720?
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 07 Nov 2017, 22:07

Re: Error in script, please help!

It is more than a 1.5k lines, I do not recommend you start looking into it. I own the code, and if I know what the message means I will be able to sort it out.

What I need is the meaning of this message I posted, but suit yourself if you want to read it

  Code:
{
Mod description:

- Certain Productions are increased, in order to compensate for the smaller building space
   Tree Trunks:   +25%
   Stone:         +50%
   Iron:         +50%
   Wine:         +50%
   Corn:         +50%
   Fish:         +50%

- Various Buildings appear every 15 minutes at a random location out of 3 possible ones. Destroying these buildings will grant an extra power to the destroyer.

   Plague!:               Each enemy player lose 1 unit every 1 sec for 2 minutes. (Total 120 units lost over the duration.)
   Abundance!:               Each player in team is granted 100 wine barrels and fish, 75 breads and 50 sausages. Also, every unit is put at full health. Inns become fully stocked.
   God's Hand!:            Enemy team loses 100% of all units of a single citizen and soldier unit type each. (75% for Militia, Bowmen, Serves, Recruits and Barbarians).
   Conflagration!:            Each enemy player lose 15% of their total buildings in a fire.
   Weapon Fair in Town!:      Each player in team is given the greater value between 20 and 40% of currently in stock for each weapon type in every Barrack.
   Real Peasants' Rebellion!:   [Enemy count] * 120 Rebels spawn as allies. Rebels are granted Retribution bonus and are ordered to attack closest enemy town.
}

// New Bonuses + Schoolhouse, Statistic, Caravan and Mercenary script + Towers limit
// Made by Toxic
// Extras by Erdesz Balazs

// DECLARATION:
type aPLAYER = record
   Bonus: (btNone, btSurvivalists, btRich, btMasterCrafters, btToughPeople, btSnipers, btRetribution);
   Buildings: record
      Schools: array of Integer;
      Barracks: array of Integer;
      Markets: array of Integer;
      Merc: array of Integer;      // Special counter in Mercenary script
   end;
   SpecialKills: Integer;            // Kills caused by bonuses (Retribution, Snipers)
   Feeding: LongInt;               // Survialist feed counter (it is faster 1 global feeding every cca 20 min than scan every tick if unit have hunger)
   TowersCount: Byte;               // Towers count for TOWERS_LIMIT
   WeaponsCount: array of Integer;
end;

var
PLAYER: array of aPLAYER;
aBuildOrder: array[0..29] of array of Byte;
BonusChooseByHouses: array of Byte;
TradeRatio: array[0..27] of array [0..27] of Byte;
TradeSide: array[0..27] of array [0..27] of Boolean;

// CONSTANTS
const
TOWERS_LIMIT = 10;         // Towers limit
UPDATE_OVERLAY_DELAY = 10;   // Statistics upload delay
MERC_LINK_GROUPS_RAD = 20;   // Radius for link groups after spawn in rally point (Mercenary script)
MERCS_SPAWN_COUNT = 10;
// Prices of 10 units from Mercenary script
   REBEL_PRICE = 2;
   ROGUE_PRICE = 2;
   VAGABOND_PRICE = 3;
   WARRIOR_PRICE = 5;
REBELS = REBEL_PRICE*MERCS_SPAWN_COUNT;
ROGUES = ROGUE_PRICE*MERCS_SPAWN_COUNT;
VAGABONDS = VAGABOND_PRICE*MERCS_SPAWN_COUNT;
WARRIORS = WARRIOR_PRICE*MERCS_SPAWN_COUNT;

type tile = record
   X: integer;
   Y: integer;
   TileType: integer;
   TileDirection: integer;
end;

type ware = record
   ID: integer;
   AmountAdd: integer;
   TrackIncrease: integer;
   Amount: Integer;
end;

var h, i, j, k, l, m, n: integer;

var t: array[0..7] of array [0..5] of ware; //tracker for ware production
var Alliance: array[0..7] of array [0..9] of boolean;
var PlayerUnits: array[0..7] of array of integer;
var PlayerInns: array[0..7] of array of integer;

var FoodTypes: array[0..3] of integer;

var ExtraStart: integer;
var ExtraDuration: integer;
var ExtraEnd: integer;
var ExtraInterval: integer;
var ExtraActive: array [0..5] of boolean;
var ExtraRecipient: Integer;
var ExtraDestriptionRemoveAfter: integer;

var NextExtra: integer;

var RandomQuestHouses: array[0..5] of integer;
var RandomQuestPlace: array[0..2] of array [0..1] of integer;
var RandomQuestLoc: integer;
var RandomQuestType: integer;

var UnitTracker: array[0..7] of array of integer;
var Extra_RandomDeath_Multiplier: integer;
var Extra_RandomDeath_Unit: integer;

var Extra_RandomSoldierTypeToKill: integer;
var PortionToKill: integer;
var Extra_RandomCitizenTypeToKill: integer;

var PortionToDestroy: integer;
var HousesToDestroy: array of integer;
var BuildingToDestroy: integer;

var UnitTypeToDie: array[0..1] of integer;

var RebelSpawnLoc: array[0..5] of array[0..1] of integer;
var RebelNumber: integer;
var RebelsToSpawn: integer;

var ExtraBuildingTiles: array[0..2] of array[0..4] of array[0..3] of tile;
var UnitsMustBeRemovedFirst: boolean;
var AlreadyRemovedUnits: boolean;


procedure Initialize;
begin

   NextExtra:= States.PeaceTime + 1;
   
   RebelsToSpawn:= 120;
   
   for i:=0 to 7 do begin

      //FILL THE WARE TRACKER
      
         t[i][0].ID:= 0; //Tree Trunks
         t[i][0].AmountAdd:= 1;
         t[i][0].Amount:= 4;
         t[i][0].TrackIncrease:= 5;
         
         t[i][1].ID:= 1; //Stone
         t[i][1].AmountAdd:= 1;
         t[i][1].Amount:= 2;
         t[i][1].TrackIncrease:= 3;
         
         t[i][2].ID:= 6; //Iron
         t[i][2].AmountAdd:= 1;
         t[i][2].Amount:= 2;
         t[i][2].TrackIncrease:= 3;
         
         t[i][3].ID:= 8; //Wine Barrel
         t[i][3].AmountAdd:= 1;
         t[i][3].Amount:= 2;
         t[i][3].TrackIncrease:= 3;
         
         t[i][4].ID:= 9; //Corn
         t[i][4].AmountAdd:= 1;
         t[i][4].Amount:= 2;
         t[i][4].TrackIncrease:= 3;
         
         t[i][5].ID:= 27; //Fish
         t[i][5].AmountAdd:= 1;
         t[i][5].Amount:= 2;
         t[i][5].TrackIncrease:= 3;
      
      for j:=0 to 7 do begin
      
         //CREATE ALLIANCE MATRIX

            Alliance[i][j]:=States.PlayerAllianceCheck(i, j);

      end;

      Alliance[i][8]:= false;
      Alliance[i][9]:= true;

   end;
   
   FoodTypes[0]:= 8;
   FoodTypes[1]:= 10;
   FoodTypes[2]:= 13;
   FoodTypes[3]:= 27;
   
   ExtraDuration:= 1200;
   ExtraInterval:= 9000;
   ExtraDestriptionRemoveAfter:= 600;
   
   for i:=0 to High(ExtraActive) do begin
      ExtraActive[i]:= false;
   end;
   
   RandomQuestHouses[0]:= 11; //Storehouse - Plague!
   RandomQuestHouses[1]:= 27; //Inn - Abundance!
   RandomQuestHouses[2]:= 21; //Barrack - God's Hand!
   RandomQuestHouses[3]:= 12; //Stables - Conflagration!
   RandomQuestHouses[4]:= 29; //Market Hall - Weapon Fair in Town!
   RandomQuestHouses[5]:= 13; //Schholhouse - Real Peasants' Rebellion!
   {RandomQuestHouses[0]:= 11; //Storehouse - Plague!
   RandomQuestHouses[1]:= 27; //Inn - Abundance!
   RandomQuestHouses[2]:= 21; //Barrack - God's Hand!
   RandomQuestHouses[3]:= 12; //Stables - Conflagration!
   RandomQuestHouses[4]:= 29; //Market Hall - Weapon Fair in Town!
   RandomQuestHouses[5]:= 18; //Town Hall - Real Peasants' Rebellion!}

   RandomQuestPlace[0][0]:= 55; //pos0_X
   RandomQuestPlace[0][1]:= 126; //pos0_Y
   RandomQuestPlace[1][0]:= 123; //pos1_X
   RandomQuestPlace[1][1]:= 126; //pos1_Y
   RandomQuestPlace[2][0]:= 192; //pos2_X
   RandomQuestPlace[2][1]:= 126; //pos2_Y
   
   RebelSpawnLoc[0][0]:= 38;
   RebelSpawnLoc[0][1]:= 126;
   RebelSpawnLoc[1][0]:= 72;
   RebelSpawnLoc[1][1]:= 126;
   RebelSpawnLoc[2][0]:= 105;
   RebelSpawnLoc[2][1]:= 126;
   RebelSpawnLoc[3][0]:= 141;
   RebelSpawnLoc[3][1]:= 126;
   RebelSpawnLoc[4][0]:= 174;
   RebelSpawnLoc[4][1]:= 126;
   RebelSpawnLoc[5][0]:= 208;
   RebelSpawnLoc[5][1]:= 126;
   
   
   for h:=0 to High(RandomQuestPlace) do begin
      for i:= 0 to High(ExtraBuildingTiles[h]) do begin
         for j:= 0 to High(ExtraBuildingTiles[h][i]) do begin
            
            ExtraBuildingTiles[h][i][j].X:= RandomQuestPlace[h][0] + (i - 2);
            ExtraBuildingTiles[h][i][j].Y:= RandomQuestPlace[h][1] + (j - 3);
            ExtraBuildingTiles[h][i][j].TileType:= States.MapTileType(ExtraBuildingTiles[h][i][j].X, ExtraBuildingTiles[h][i][j].Y);
            ExtraBuildingTiles[h][i][j].TileDirection:= States.MapTileRotation(ExtraBuildingTiles[h][i][j].X, ExtraBuildingTiles[h][i][j].Y);
            
         end;
      end;
   end;
   
   UnitsMustBeRemovedFirst:= false;
   
   PortionToKill:= 75 //x% will die if it's spammable unit type (Militia, Barbarian, Bowmen, XBowmen, Serf, Recruit)
   PortionToDestroy:= 15 //x% of all buildings are destroyed.
   
end;


function Abs(const A: Integer): Integer; begin if A < 0 then RESULT := -A else RESULT := A; end;

// HOUSES management
procedure AddHouse(const ID: Integer; out Houses: array of Integer);
begin
   SetLength(Houses, Length(Houses)+1);
   Houses[High(Houses)] := ID;
end;

procedure RemoveHouse(const ID: Integer; out Houses: array of Integer);
var i: Integer;
begin
   for i := 0 to High(Houses) do
      if (Houses[i] = ID) or (States.HouseOwner(Houses[i]) > 7) then break;
   Houses[i] := Houses[High(Houses)];
   SetLength(Houses, High(Houses));
end;

procedure RemoveMarket(const ID: Integer; out Market, Merc: array of Integer);
var i: Integer;
begin
   for i := 0 to High(Market) do
      if (Market[i] = ID) then break;
   Market[i] := Market[High(Market)];
   Merc[i] := Merc[High(Merc)];
   SetLength(Market, High(Market));
   SetLength(Merc, High(Merc));
end;

// SCHOOLHOUSE script
procedure Recruits(const PL: Integer);
var i: Integer;
begin         
   for i := 0 to High(PLAYER[PL].Buildings.Schools) do
      if States.HouseRepair(PLAYER[PL].Buildings.Schools[i]) AND (States.HouseResourceAmount(PLAYER[PL].Buildings.Schools[i], 7) > 0) AND (States.HouseSchoolQueue(PLAYER[PL].Buildings.Schools[i], 0) = - 1) then
         Actions.HouseSchoolQueueAdd(PLAYER[PL].Buildings.Schools[i], 13, 1);
end;

// MERCENARY units
procedure Mercenary(const PL, ID, addGold, mercCost, mercType, groupType: Integer);
var i, X, Y, IDNew, IDClosest: Integer;
begin
   for i := 0 to High(PLAYER[PL].Buildings.Merc) do
      if ID = PLAYER[PL].Buildings.Markets[i] then break;
   PLAYER[PL].Buildings.Merc[i] := PLAYER[PL].Buildings.Merc[i] + addGold;
   
   if PLAYER[PL].Buildings.Merc[i] >= mercCost then begin
      PLAYER[PL].Buildings.Merc[i] := PLAYER[PL].Buildings.Merc[i] - mercCost;
      if length(PLAYER[PL].Buildings.Barracks) <> 0 then begin
         X := States.HouseBarracksRallyPointX(PLAYER[PL].Buildings.Barracks[0]);
         Y := States.HouseBarracksRallyPointY(PLAYER[PL].Buildings.Barracks[0]);
      end else begin
         X := States.HousePositionX(ID);
         Y := States.HousePositionY(ID);
      end;
      IDClosest := States.ClosestGroup(PL, X, Y, groupType);
      IDNew := Actions.GiveGroup(PL, mercType, States.HousePositionX(ID), States.HousePositionY(ID), 0, MERCS_SPAWN_COUNT, 5);
      if (IDClosest > 0) AND ( (Abs(X-States.UnitPositionX(States.GroupMember(IDClosest,0)))+Abs(Y-States.UnitPositionY(States.GroupMember(IDClosest,0)))) < MERC_LINK_GROUPS_RAD ) then
         Actions.GroupOrderLink(IDNew, IDClosest)
      else
         Actions.GroupOrderWalk(IDNew,X,Y,0);
   end;
end;

// CARAVAN script
procedure TradeCalculation();// Rich player trades cca 10 000x per a game so we calculate ratio once and save this value
var i,j: Byte;
ratio: Double;
begin
   for i := 0 to 27 do begin
      for j := 0 to 27 do begin
         ratio := States.MarketValue(i) / (States.MarketValue(j)*States.MarketLossFactor());
         if ratio < 1 then begin
            TradeRatio[i][j] := Round(1/ratio);
            TradeSide[i][j] := True;
         end else begin
            TradeRatio[i][j] := Round(ratio);
            TradeSide[i][j] := False;
         end;
      end;
   end;
end;

procedure GiveWares(const PL, Ware, Count: Integer);
begin
   Actions.GiveWares(PL,Ware,Count);
   if (PLAYER[PL].Bonus = btMasterCrafters) AND (Ware >=16) AND (Ware <= 26) then   // Master crafters fix
      PLAYER[PL].WeaponsCount[Ware-16] := PLAYER[PL].WeaponsCount[Ware-16] + Count;
end;

procedure TakeWares(const PL, ID, Ware, Count: Integer);
begin
   Actions.HouseTakeWaresFrom(ID,Ware,Count);
   if (PLAYER[PL].Bonus = btMasterCrafters) AND (Ware >=16) AND (Ware <= 26) then   // Master crafters fix
      PLAYER[PL].WeaponsCount[Ware-16] := PLAYER[PL].WeaponsCount[Ware-16] - Count;
end;

procedure Caravan(const ID, aFrom, aTo: Integer);
var recipient, PL: Integer;
begin
   PL := States.HouseOwner(ID);
   if (PLAYER[PL].Bonus = btMasterCrafters) AND (aTo >=16) AND (aTo <= 26) then
      Inc(PLAYER[PL].WeaponsCount[aTo-16]);
   if aFrom < aTo then recipient := aTo-1
   else recipient := aTo;
   if (recipient < 8) AND (PL <> recipient) AND (States.PlayerAllianceCheck(PL, recipient)) then begin
      if TradeSide[aFrom][aTo] then begin
         GiveWares(recipient, aFrom, TradeRatio[aFrom][aTo]);
         TakeWares(PL, ID, aFrom, 1);
      end else begin
         GiveWares(recipient, aFrom, 1);
         TakeWares(PL, ID, aFrom, TradeRatio[aFrom][aTo]);
      end;
   end
   // Mercenary hire
   else if (aTo = 22) AND (aFrom = 7) then begin
      Mercenary(PL, ID, 4, REBELS, 24, 1); // Lance -> Rebel
      TakeWares(PL, ID, aTo, 1);
   end else if (aTo = 24) AND (aFrom = 7) then begin
      Mercenary(PL, ID, 4, ROGUES, 25, 2); // Longbow -> Rogue
      TakeWares(PL, ID, aTo, 1);
   end else if (aTo = 26) AND (aFrom = 7) then begin
      Mercenary(PL, ID, 8, VAGABONDS, 27, 3); // Horse -> Vagabond
      TakeWares(PL, ID, aTo, 1);
   end else if (aTo = 20) AND (aFrom = 7) then begin
      Mercenary(PL, ID, 4, WARRIORS, 26, 0); // Handaxe -> Warrior
      TakeWares(PL, ID, aTo, 1);
   end else begin
      //Actions.HouseRepairEnable(ID,False);
      Actions.MarketSetTrade(ID, aFrom, aTo, 0);
      Actions.PlayWAV(PL, 'wCant', 1);
   end;
end;

// Weapons from destroyed barracks
procedure GiveBarracksWeapons(const aHouse, PL: Integer);
var i: Integer;
begin
   for i := 16 to 26 do begin
      Actions.GiveWeapons(PL,i,States.HouseResourceAmount(aHouse,i));
      if PLAYER[PL].Bonus = btMasterCrafters then
         PLAYER[PL].WeaponsCount[i-16] := PLAYER[PL].WeaponsCount[i-16] + States.HouseResourceAmount(aHouse,i);
   end;
end;

// BONUSES script
procedure GiveWeapons(const weapons: Array of Byte; const PL: Integer);
var i: Integer;
begin
   if (States.StatHouseTypeCount(PL, 21) > 0) then
      for i := 0 to High(weapons) do Actions.GiveWeapons(PL, weapons[i], 1);
end;

procedure GiveReward(const ware, PL, Count: Integer);
begin
   if (States.StatHouseTypeCount(PL, 11) > 0) then Actions.GiveWares(PL, ware, Count);
end;

// Survivalists
procedure Survivalist(const PL: Integer);
var aiUnits: array of Integer;
var i: Integer;
begin
   if States.GameTime div PLAYER[PL].Feeding > 1 then begin
      PLAYER[PL].Feeding := PLAYER[PL].Feeding + 5000; // feeding each cca 17 min
      aiUnits := States.PlayerGetAllUnits(PL);
      for i := 0 to High(aiUnits) do
         Actions.UnitHungerSet(aiUnits[i], States.UnitMaxHunger);
   end;
end;
// Rich - schoolhouse gold
procedure GiveGold(const PL: Integer);
var i: Integer;
begin
   for i := 0 to High(PLAYER[PL].Buildings.Schools) do
      if States.HouseResourceAmount(PLAYER[PL].Buildings.Schools[i], 7) < 5 then Actions.HouseAddWaresTo(PLAYER[PL].Buildings.Schools[i], 7, 1);
end;
procedure CloseSchool(const ID: Integer);
begin
   Actions.HouseDeliveryBlock(ID, True);
   if States.HouseResourceAmount(ID, 7) < 5 then
      Actions.HouseAddWaresTo(ID, 7, 5-States.HouseResourceAmount(ID, 7));
end;
// Rich - trade
procedure DoubleTrade(const aMarket, aFromWare, aToWare: Integer);
begin
   if TradeSide[aFromWare][aToWare] then Actions.HouseAddWaresTo(aMarket, aToWare,1)
   else Actions.HouseAddWaresTo(aMarket, aToWare, TradeRatio[aFromWare][aToWare]);
end;
// Master crafters - add weapons to Barracks and faster building
procedure MasterCrafter(const PL: Integer);
var i, j: Integer;
aiHouses: array of Integer;
begin
   if (Length(PLAYER[PL].Buildings.Barracks) > 0) then
      for i := 0 to High(PLAYER[PL].WeaponsCount) do
         if (PLAYER[PL].WeaponsCount[i] - States.StatResourceProducedCount(PL, i+16)) <= 0 then begin
            PLAYER[PL].WeaponsCount[i] := PLAYER[PL].WeaponsCount[i] + 3;
            Actions.GiveWeapons(PL,i+16,1);
         end;
   aiHouses := States.PlayerGetAllHouses(PL);
   for i := 0 to High(aiHouses) do
      if not States.HouseIsComplete(aiHouses[i]) then
         if (States.HouseBuildingProgress(aiHouses[i]) mod 50 > 0) AND (States.HouseBuildingProgress(aiHouses[i]) mod 50 <= 50) then
            for j := 0 to 10 do Actions.HouseAddBuildingProgress(aiHouses[i]);
end;
// Tough People - switch militia -> barbarian
procedure SwitchToBarbarian(const UnitID, GroupID: Integer);
var aBarracks: Integer;
begin
   if States.UnitType(UnitID) = 14 then begin
      aBarracks := States.ClosestHouse(States.UnitOwner(UnitID), States.UnitPositionX(UnitID), States.UnitPositionY(UnitID), 21);
      if aBarracks > -1 then begin
         if States.PlayerIsAI(States.UnitOwner(UnitID)) = True then Actions.HouseAddWaresTo(aBarracks, 20, 1); // Help to AI
         if (States.HouseResourceAmount(aBarracks, 20) >= 1) then begin
            Actions.HouseTakeWaresFrom(aBarracks, 20, 1);
            Actions.GroupOrderLink(Actions.GiveGroup(States.UnitOwner(UnitID), 23, States.UnitPositionX(UnitID), States.UnitPositionY(UnitID), States.UnitDirection(UnitID),1,1), GroupID);
         end else begin
            Actions.HouseBarracksGiveRecruit(aBarracks);
            Actions.HouseAddWaresTo(aBarracks, 20, 1);
         end;
         Actions.UnitKill(UnitID, True);
      end;
   end;
end;
// Tough People - rewards
procedure Reward(const UnitID, KillerOwner: Integer);
begin
   case States.UnitType(UnitID) of
      15: GiveWeapons([16,18],KillerOwner);         //Axe Fighter
      16: GiveWeapons([17,19,21],KillerOwner);      //Sword Fighter
      17: GiveWeapons([18,24],KillerOwner);         //Bowman
      18: GiveWeapons([19,25],KillerOwner);         //Crossbowman
      19: GiveWeapons([18,22],KillerOwner);         //Lance carrier
      20: GiveWeapons([19,23],KillerOwner);         //Pikeman
      21: GiveWeapons([16,18,26],KillerOwner);      //Scout
      22: GiveWeapons([17,19,21,26],KillerOwner);      //Knight
   end;
end;
// Snipers - kill defender if attacker is long range unit
procedure Sniper(const UnitID, AttackerID: Integer);
begin
   if (States.UnitType(AttackerID) = 17 ) OR (States.UnitType(AttackerID) = 18 ) then if States.KamRandom() < 0.04 then begin
      if (States.UnitType(UnitID) > 13) AND (States.UnitType(UnitID) < 28) then PLAYER[States.UnitOwner(AttackerID)].SpecialKills := PLAYER[States.UnitOwner(AttackerID)].SpecialKills + 1;
      Actions.UnitKill(UnitID, False);
   end;
end;

// Retribution - kill attacker
procedure Retribute(const UnitID, AttackerID: Integer);
begin
   if (States.KamRandom() < 0.04) and (States.UnitType(UnitID) > 13) and (States.UnitType(UnitID) < 28) then begin
      PLAYER[States.UnitOwner(UnitID)].SpecialKills := PLAYER[States.UnitOwner(UnitID)].SpecialKills + 1;
      Actions.UnitKill(AttackerID, False);
   end;
end;

// Building order procedures:
procedure CheckBuildOrder(const aHouse: Integer);
var i: Integer;
begin
   if Length(aBuildOrder[States.HouseType(aHouse)]) > 0 then
      for i := 0 to High(aBuildOrder[States.HouseType(aHouse)]) do
         if not (States.HouseUnlocked(States.HouseOwner(aHouse), aBuildOrder[States.HouseType(aHouse)][i])) then begin
            Actions.HouseAllow(States.HouseOwner(aHouse), aBuildOrder[States.HouseType(aHouse)][i], True);
            Actions.HouseUnlock(States.HouseOwner(aHouse), aBuildOrder[States.HouseType(aHouse)][i]);
         end;
end;

procedure unlockAllBuildings(const PL: Integer);
var i: Integer;
var aiHouses: array of Integer;
begin
   aiHouses := States.PlayerGetAllHouses(PL);
   for i := 0 to 29 do if (i <> 26) and (i <> 27) then begin
      Actions.HouseUnlock(PL, i);
      Actions.HouseAllow(PL, i, True);
   end;
   if (PLAYER[PL].Bonus <> btSurvivalists) then begin Actions.HouseAllow(PL, 27, True); Actions.HouseUnlock(PL, 27); end;
end;

procedure SetBuildOrder(const PL: Integer);
var i: Integer;
var aiHouses: array of Integer;
begin
   for i := 0 to 29 do if (i <> 26) then Actions.HouseAllow(PL, i, False);
   if (PLAYER[PL].Bonus = btSurvivalists) or (PLAYER[PL].Bonus = btMasterCrafters) then
      unlockAllBuildings(PL)
   else begin
      aiHouses := States.PlayerGetAllHouses(PL);
      for i := 0 to High(aiHouses) do CheckBuildOrder(aiHouses[i]);
   end;
end;

//Choose a bonus
procedure ChoseMasterCrafters(const PL: Integer);
var i: Integer;
begin
   PLAYER[PL].Bonus := btMasterCrafters;
   Actions.ShowMSG(PL, '<$1>');
   SetLength(PLAYER[PL].WeaponsCount, 11);
   for i := 0 to High(PLAYER[PL].WeaponsCount) do PLAYER[PL].WeaponsCount[i] := 1;
end;
procedure ChoseRich(const PL: Integer);
var i: Integer;
aiHouses: array of Integer;
begin
   PLAYER[PL].Bonus := btRich;
   Actions.ShowMSG(PL, '<$3>');
   for i := 0 to High(PLAYER[PL].Buildings.Schools) do
      CloseSchool(PLAYER[PL].Buildings.Schools[i]);
   aiHouses := States.PlayerGetAllHouses(PL);
end;
procedure ChoseSurvivalists(const PL: Integer);   begin PLAYER[PL].Bonus := btSurvivalists;      Actions.ShowMSG(PL, '<$7>'); PLAYER[PL].Feeding := 1; end;
procedure ChoseToughPeople(const PL: Integer);   begin PLAYER[PL].Bonus := btToughPeople;      Actions.ShowMSG(PL, '<$2>'); end;
procedure ChoseSnipers(const PL: Integer);       begin PLAYER[PL].Bonus := btSnipers;          Actions.ShowMSG(PL, '<$4>'); PLAYER[PL].SpecialKills := 0; end;
procedure ChoseRetribution(const PL: Integer);    begin PLAYER[PL].Bonus := btRetribution;       Actions.ShowMSG(PL, '<$5>'); PLAYER[PL].SpecialKills := 0; end;

procedure ChooseBonus(const PL, aType, aX, aY: Integer);
begin
   case aType of
      BonusChooseByHouses[0]: ChoseSurvivalists(PL);
      BonusChooseByHouses[1]: ChoseRich(PL);
      BonusChooseByHouses[2]: ChoseMasterCrafters(PL);
      BonusChooseByHouses[3]: ChoseToughPeople(PL);
      BonusChooseByHouses[4]: ChoseSnipers(PL);
      BonusChooseByHouses[5]: ChoseRetribution(PL);
   end;
   SetBuildOrder(PL);
   Actions.PlanRemove(PL, aX, aY);
end;

function CountEnemiesToPlayer(Player: integer) : integer;
begin

   j:=0;
   
   For i:=0 to 7 do begin
   
      if (Alliance[Player][i] = false) and (States.PlayerEnabled(i) = true) then inc(j);
         
   end;
   
   Result:= j;
   
end;


//Update Overlay
procedure UpdateOverlay();
var i, j, PL, recipient, aFrom, aTo, count, value: Integer;
str: String;
begin
   Actions.OverlayTextSet(-1, '');
   
   // PLAYERS
   for PL := 0 to 7 do
   if States.PlayerEnabled(PL) then begin
   
      // BONUSES
      case PLAYER[PL].Bonus of
         btSurvivalists:      str := '<$8>';
         btRich:            str := '<$9>';
         btMasterCrafters:   str := '<$10>';
         btToughPeople:      str := '<$11>';
         btSnipers:         str := '<$12>'+IntToStr(PLAYER[PL].SpecialKills)+'[].';
         btRetribution:      str := '<$13>'+IntToStr(PLAYER[PL].SpecialKills)+'[].';
         else str := '';
      end;
         
      // STATISTICS
      value := 0;
      for i := 14 to 26 do value := value + States.StatUnitKilledCount(PL,i);

      for j := 0 to 7 do
      if States.PlayerEnabled(j) then begin
          if ((PL = j) OR States.PlayerAllianceCheck(PL, j)) and (PL < 9) then begin
            // BONUSES & STATISTICS
            Actions.OverlayTextAppendFormatted(j, '<$14>'+str+'<$15>', [States.PlayerColorText(PL), States.PlayerName(PL), value, States.StatArmyCount(PL)]);
            end;
            
      end;
         
      if (States.GameTime - ExtraStart < 600) And (ExtraStart > 2) then begin
      
         case RandomQuestType of
            0: Actions.OverlayTextAppendFormatted(PL, '|<$27>|', [States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient)]);
            1: Actions.OverlayTextAppendFormatted(PL, '|<$28>|', [States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient)]);
            2: Actions.OverlayTextAppendFormatted(PL, '|<$29>|', [States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient), States.UnitTypeName(UnitTypeToDie[0]), States.UnitTypeName(UnitTypeToDie[1])]);
            3: Actions.OverlayTextAppendFormatted(PL, '|<$30>|', [States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient), PortionToDestroy]);
            4: Actions.OverlayTextAppendFormatted(PL, '|<$31>|', [States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient)]);
            5: Actions.OverlayTextAppendFormatted(PL, '|<$32>|', [RebelNumber, States.PlayerColorText(ExtraRecipient), States.PlayerName(ExtraRecipient)]);
         end;
         
      end;
      
      if NextExtra - States.GameTime > 0 then begin
      
         Actions.OverlayTextAppendFormatted(PL, '|<$26>|', [((NextExtra - States.GameTime) div 600), (((NextExtra - States.GameTime) mod 600) div 100), (((NextExtra - States.GameTime) mod 600) mod 100) div 10]);
         
      end;
      
      
      
      
      // MARKETS
      value := 0;
      for i := 0 to High(PLAYER[PL].Buildings.Markets) do if States.HouseRepair(PLAYER[PL].Buildings.Markets[i]) then begin
         count := States.MarketOrderAmount(PLAYER[PL].Buildings.Markets[i]);
         if count > 0 then begin
            aFrom := States.MarketFromWare(PLAYER[PL].Buildings.Markets[i]);
            aTo := States.MarketToWare(PLAYER[PL].Buildings.Markets[i]);
            // MERCENARY - check peace time
            if (aFrom = 7) AND ( (aTo = 20) OR (aTo = 22) OR (aTo = 24) OR (aTo = 26) ) then begin
               if States.GameTime < States.PeaceTime then begin
                  Actions.MarketSetTrade(PLAYER[PL].Buildings.Markets[i], aFrom, aTo, 0);
                  Actions.ShowMsg(PL, '<$18>');
                  Actions.PlayWAV(PL, 'wCant', 1);
               end;
            end
            // CARAVAN
            else begin
               if TradeSide[aFrom][aTo] then count := count*TradeRatio[aFrom][aTo];
               if aFrom < aTo then recipient := aTo-1
               else recipient := aTo;
               value := value+1;
               if (recipient < 8) AND (States.PlayerEnabled(recipient)) AND (States.PlayerAllianceCheck(PL, recipient)) then begin
                  for j := 0 to 7 do if States.PlayerEnabled(j) AND States.PlayerAllianceCheck(j, PL) then begin//AND States.PlayerAllianceCheck(j, recipient)
                     if (value = 1) then Actions.OverlayTextAppend(j,'|');
                     Actions.OverlayTextAppendFormatted(j, '<$16>', [count,States.WareTypeName(aFrom),States.PlayerColorText(recipient),States.PlayerName(recipient)])
                  end;
               end else begin
                  Actions.MarketSetTrade(PLAYER[PL].Buildings.Markets[i], aFrom, aTo, 0);
                  Actions.PlayWAV(PL, 'wCant', 1);
               end;
            end;
         end;
      end;
   end;
end;



procedure MultiplyWares(WareType, player: integer);
begin
   
   if (t[player][WareType].Amount - States.StatResourceProducedCount(player,t[player][WareType].ID)) <= 0 then begin
      
      t[player][WareType].Amount := t[player][WareType].Amount + t[player][WareType].TrackIncrease;
      Actions.GiveWares(player, t[player][WareType].ID, t[player][WareType].AmountAdd);
      MultiplyWares(WareType, player);
      
   end;
   
end;


function Max(integy, intketto: integer) : integer;
begin

   if integy > intketto then Result:= integy else Result:= intketto;

end;


procedure GiveWeaponFair(Barrack: integer);
begin

   For i:=16 to 26 do begin
      
      Actions.HouseAddWaresTo(Barrack, i, Max(20, States.HouseResourceAmount(Barrack, i) * 40 div 100));
   
   end;

end;


procedure AddInn(out Inns: array of Integer; const Inn: Integer);
begin

   SetLength(Inns, Length(Inns) + 1);
   Inns[High(Inns)]:= Inn;

end;


procedure RemoveInn(out Inns: array of Integer; const Inn: Integer);
begin

   m:= 0;
      
      while (Inns[m] <> Inn) do begin
      
         Inc(m);
      
      end;
      
      for n:= (m + 1) to High(Inns) do begin
      
         Inns[n-1]:= Inns[n];
      
      end;
      
      SetLength(Inns, Length(Inns) - 1)

end;

procedure FillEveryInn(const Inns: array of Integer);
begin
   
   for m:=0 to High(Inns) do begin
   
      t[States.HouseOwner(Inns[m])][3].Amount:= t[States.HouseOwner(Inns[m])][3].Amount + 5 - States.HouseResourceAmount(Inns[m], 0);
      t[States.HouseOwner(Inns[m])][5].Amount:= t[States.HouseOwner(Inns[m])][5].Amount + 5 - States.HouseResourceAmount(Inns[m], 27);
   
      for n:=0 to High(FoodTypes) do begin
         
         Actions.HouseAddWaresTo(Inns[m], FoodTypes[n], 5 - States.HouseResourceAmount(Inns[m], n));
         
      end;
      
   end;

end;

procedure FeedEveryUnit(Units: array of integer);
begin

   for n:=0 to High(Units) do begin

      Actions.UnitHungerSet(Units[n], States.UnitMaxHunger);

   end;
   
end;

procedure AddExtraFoodToStore(Player: Integer);
begin
   
      Actions.GiveWares(Player, FoodTypes[0], 100);
      Actions.GiveWares(Player, FoodTypes[1], 75);
      Actions.GiveWares(Player, FoodTypes[2], 50);
      Actions.GiveWares(Player, FoodTypes[3], 100);

      
      t[Player][3].Amount:= t[Player][3].Amount + 100;
      t[Player][5].Amount:= t[Player][5].Amount + 100;

end;

procedure Extra_RandomDeath(Beneficiary, Attacked: Integer);
begin

   //IF PLAYER IS ENEMY, IT WILL LOSE A RANDOM UNIT THIS TICK
   
   if Alliance[Beneficiary][Attacked] = false and
      (States.PlayerEnabled(Attacked) = true) and
      (States.PlayerDefeated(Attacked) = false) then begin
      
      UnitTracker[Attacked]:= States.PlayerGetAllUnits(Attacked);
      
      if High(UnitTracker[Attacked]) = -1 then exit;
      
      Extra_RandomDeath_Unit:= States.KamRandomI(High(UnitTracker[Attacked])+1);

      Actions.UnitKill(UnitTracker[Attacked][Extra_RandomDeath_Unit], false);
   
   end;
end;


procedure KillRandomType(UnitType, Player: integer);
var Units: array of integer;
var NumberOfUnitTypeUnits, UnitsAlreadyKilled: integer;
begin

   Units:= States.PlayerGetAllUnits(Player);
   NumberOfUnitTypeUnits:= 0;
   UnitsAlreadyKilled:= 0;
   
   For m:=0 to High(Units) do begin
   
      if States.UnitType(Units[m]) = UnitType then inc(NumberOfUnitTypeUnits);

   end;
   
   For m:=0 to High(Units) do begin
      
      case UnitType of
      
         0, 13, 14, 17, 18, 23: begin
                           if (States.UnitType(Units[m]) = UnitType) and
                              (UnitsAlreadyKilled < ((NumberOfUnitTypeUnits * PortionToKill) div 100)) then begin
                              
                              Actions.UnitKill(Units[m], false);
                              inc(UnitsAlreadyKilled);
                              
                           end;
                           end;
                              
         else begin
               if (States.UnitType(Units[m]) = UnitType) then begin
               
                  Actions.UnitKill(Units[m], false);
                  inc(UnitsAlreadyKilled);
                  
               end;
                  
            end;
      
      end;
      
      

   end;
   
end;

procedure RemoveCheaterField(aPlayer, aX, aY: integer);
begin
   
      for i:=0 to High(RandomQuestPlace) do begin
         case i of
            0, 2: begin
                  if (Abs(aX - RandomQuestPlace[i][0]) <= 20) And (Abs(aY - RandomQuestPlace[i][1]) <= 20) then begin
                     Actions.PlanRemove(aPlayer, aX, aY);
                     if States.PlayerIsAI(aPlayer) then Actions.AIAutoBuild(aPlayer, false);
                  end;
                 end;
            1: begin
               if (((aX - RandomQuestPlace[i][0]) >= -27) And ((aX - RandomQuestPlace[i][0]) <= 25)) And
                  (((aY - RandomQuestPlace[i][1] >= -13) And ((aY - RandomQuestPlace[i][1]) <= 12))) then begin
                  Actions.PlanRemove(aPlayer, aX, aY);
                  if States.PlayerIsAI(aPlayer) then Actions.AIAutoBuild(aPlayer, false);
               end;
               end;
         end;
      end;

end;

// EVENTS
procedure OnPlanFieldPlaced(aPlayer, aX, aY: Integer);
begin

   RemoveCheaterField(aPlayer, aX, aY);

end;

procedure OnPlanRoadPlaced(aPlayer, aX, aY: Integer);
begin
   
   RemoveCheaterField(aPlayer, aX, aY);
   
end;

procedure OnPlanWinefieldPlaced(aPlayer, aX, aY: Integer);
begin

   RemoveCheaterField(aPlayer, aX, aY);
   
end;

procedure OnHousePlanPlaced(PL, aX, aY, THouseType: Integer);
begin

   if PLAYER[PL].Bonus = btNone then ChooseBonus(PL, THouseType, aX, aY)
// if ((States.StatHouseTypeCount(PL, 17)+States.StatHouseTypePlansCount(PL, 17)) > TOWERS_LIMIT) then
// - THIS DOES NOT WORK!!! StatHouseTypeCount takes ONLY COMPLETED HOUSES and StatHouseTypePlansCount takes ONLY UNTOUCHED PLANS so there still are plans with uncompleted houses
   else if THouseType = 17 then
      if PLAYER[PL].TowersCount < TOWERS_LIMIT then
         Inc(PLAYER[PL].TowersCount)
      else begin
         Actions.ShowMSG(PL, '<$17> '+IntToStr(TOWERS_LIMIT)+'!');
         Actions.PlanRemove(PL, aX, aY);
      end;
   
   RemoveCheaterField(PL, aX, aY);
   
end;

procedure OnHousePlanRemoved(PL: Integer; aX: Integer; aY: Integer; THouseType: Integer);
begin
   if THouseType = 17 then Dec(PLAYER[PL].TowersCount);
end;

procedure OnHouseBuilt(aHouse: Integer);
begin
   if not States.PlayerIsAI(States.HouseOwner(aHouse)) and not (PLAYER[States.HouseOwner(aHouse)].Bonus = btSurvivalists) and not (PLAYER[States.HouseOwner(aHouse)].Bonus = btMasterCrafters) then CheckBuildOrder(aHouse);
   if (States.HouseType(aHouse) = 13) then begin
      AddHouse(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Schools);
      CloseSchool(aHouse);
   end else if (States.HouseType(aHouse) = 21) then AddHouse(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Barracks)
   else if (States.HouseType(aHouse) = 29) then begin
      AddHouse(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Markets);
      AddHouse(0, PLAYER[States.HouseOwner(aHouse)].Buildings.Merc);
   end;
   
   if (States.HouseType(aHouse) = 27) and (States.HouseOwner(aHouse) < 8) then begin
         
      AddInn(PlayerInns[States.HouseOwner(aHouse)], aHouse);
   
   end;
   
end;

procedure OnHouseDestroyed(aHouse, aDestroyerIndex: Integer);
begin
   if States.HouseIsComplete(aHouse) and (States.HouseOwner(aHouse) < 8) then begin
      if (States.HouseType(aHouse) = 13) then RemoveHouse(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Schools)
      else if (States.HouseType(aHouse) = 21) then begin
         RemoveHouse(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Barracks);
         if States.HouseOwner(aHouse) <> aDestroyerIndex then GiveBarracksWeapons(aHouse, aDestroyerIndex);
      end else if (States.HouseType(aHouse) = 29) then RemoveMarket(aHouse, PLAYER[States.HouseOwner(aHouse)].Buildings.Markets, PLAYER[States.HouseOwner(aHouse)].Buildings.Merc);
   end;
   if (States.HouseType(aHouse) = 17) then Dec(PLAYER[States.HouseOwner(aHouse)].TowersCount);
   
   if (States.HouseOwner(aHouse) = 8) then begin
   
         ExtraRecipient:= aDestroyerIndex;
         ExtraStart:= States.GameTime;   
         ExtraEnd:= ExtraStart + ExtraDuration;
         RebelNumber:= CountEnemiesToPlayer(ExtraRecipient) * RebelsToSpawn;
         
         for m:= 0 to High(RandomQuestHouses) do begin
         
            if States.HouseType(aHouse) = RandomQuestHouses[m] then begin
            
               ExtraActive[m]:= true;
               AlreadyRemovedUnits:=false;
               
            end;
            
         end;
         
   end;
   
   if (States.HouseType(aHouse) = 27) and (States.HouseOwner(aHouse) < 8) then begin
      
      if States.HouseIsComplete(aHouse) then RemoveInn(PlayerInns[States.HouseOwner(aHouse)], aHouse);
      
   end;
   
end;

procedure OnMarketTrade(aMarket, aFrom, TWareType: Integer);
begin
   if States.HouseRepair(aMarket) then Caravan(aMarket, aFrom, TWareType)
   else if PLAYER[States.HouseOwner(aMarket)].Bonus = btRich then DoubleTrade(aMarket, aFrom, TWareType);
end;

procedure OnUnitTrained(UnitID: Integer);
var PL: Integer;
begin
   PL := States.UnitOwner(UnitID);
   if (PL <> -1) then GiveGold(PL);
end;

procedure OnWarriorEquipped(UnitID, aGroupID: Integer);
begin
   if PLAYER[States.UnitOwner(UnitID)].Bonus = btToughPeople then SwitchToBarbarian(UnitID, aGroupID);
end;

procedure OnUnitAttacked(UnitID, AttackerID: Integer);
begin
   if (AttackerID > -1) AND (UnitID > -1) then
      if (not States.UnitDead(AttackerID)) AND (not States.UnitDead(UnitID)) then begin
         if States.UnitOwner(AttackerID) > -1 then if PLAYER[States.UnitOwner(AttackerID)].Bonus = btSnipers then Sniper(UnitID, AttackerID);
         if States.UnitOwner(UnitID) > -1 then if PLAYER[States.UnitOwner(UnitID)].Bonus = btRetribution then Retribute(UnitID, AttackerID);
      end;
end;

{
procedure OnUnitWounded(aUnit, aAttacker: Integer);
begin
end;
}

procedure OnUnitDied(UnitID, aKillerOwner: Integer);
begin
   if (aKillerOwner > -1) AND (aKillerOwner < 8) AND (PLAYER[aKillerOwner].Bonus = btToughPeople) AND (States.KaMRandom()<0.3) then Reward(UnitID, aKillerOwner);
end;

procedure OnMissionStart;
var PL, i: Integer;
aiHouses: array of Integer;
text: String;
begin
   
   Initialize();
   Actions.ShowMSG(-1, '<$25>');
   
   aBuildOrder[0] := [3, 4, 5, 6, 8, 19, 21, 28, 29];aBuildOrder[1] := [2, 10, 23];aBuildOrder[2] := [];aBuildOrder[3] := [];aBuildOrder[4] := [1];aBuildOrder[5] := [15];aBuildOrder[6] := [];aBuildOrder[7] := [];aBuildOrder[8] := [12, 16, 22];aBuildOrder[9] := [0];aBuildOrder[10] := [];aBuildOrder[11] := [11, 13];aBuildOrder[12] := [];aBuildOrder[13] := [14, 27];aBuildOrder[14] := [9, 17];aBuildOrder[15] := [18];aBuildOrder[16] := [24, 25];aBuildOrder[17] := [];aBuildOrder[18] := [];aBuildOrder[19] := [];aBuildOrder[20] := [];aBuildOrder[21] := [];aBuildOrder[22] := [7];aBuildOrder[23] := [];aBuildOrder[24] := [];aBuildOrder[25] := [20];aBuildOrder[26] := [];aBuildOrder[27] := [14];aBuildOrder[28] := [];aBuildOrder[29] := [];
   BonusChooseByHouses := [27, 15, 1, 21, 17, 29]; //[Inn, Metallurgist's, Iron Smithy, Barracks, Watchtower, Market]
   
   TradeCalculation();
   SetLength(PLAYER, 10);
   for PL := 0 to 7 do if States.PlayerEnabled(PL) then begin
      // Schoolhouse + Castle + Towers count
      aiHouses := States.PlayerGetAllHouses(PL);
      PLAYER[PL].TowersCount := 0;
      for i := 0 to High(aiHouses) do begin
         if (States.HouseType(aiHouses[i]) = 13) and States.HouseIsComplete(aiHouses[i]) then AddHouse(aiHouses[i], PLAYER[PL].Buildings.Schools)
         else if (States.HouseType(aiHouses[i]) = 21) and States.HouseIsComplete(aiHouses[i]) then AddHouse(aiHouses[i], PLAYER[PL].Buildings.Barracks)
         else if (States.HouseType(aiHouses[i]) = 29) and States.HouseIsComplete(aiHouses[i]) then begin
            AddHouse(aiHouses[i], PLAYER[PL].Buildings.Markets);
            AddHouse(0, PLAYER[PL].Buildings.Merc);
         end else if (States.HouseType(aiHouses[i]) = 17) then PLAYER[PL].TowersCount := PLAYER[PL].TowersCount + 1;
      end;
      // Building order (Bonuses)
      for i := 0 to 29 do if (i <> 26) then Actions.HouseAllow(PL, i, False);
      for i := Low(BonusChooseByHouses) to High(BonusChooseByHouses) do begin
         Actions.HouseAllow(PL, BonusChooseByHouses[i], True);
         Actions.HouseUnlock(PL, BonusChooseByHouses[i]);
      end;
      // AI Bonus
      if States.PlayerIsAI(PL) then begin
         case States.KamRandomI(4) of
            0: ChoseRetribution(PL);
            1: ChoseSnipers(PL);
            2: ChoseMasterCrafters(PL);
            3: ChoseToughPeople(PL);
         end;
         // AI configuration
         unlockAllBuildings(PL); // AI works only with specific unlock order (or with everything on)
         Actions.AIEquipRate(PL,0,0);
         Actions.AIEquipRate(PL,1,0);
         Actions.AIWorkerLimit(PL, 35);
         //Actions.AISerfsPerHouse(PL, 1.5);// KaM does not recognize this action :(
         Actions.AISoldiersLimit(PL,-1);
      end else PLAYER[PL].Bonus := btNone;
      text := text + '[$'+ States.PlayerColorText(PL)+']'+States.PlayerName(PL)+'[] = [$B9B9FF]'+States.WareTypeName(PL)+'|';
   end;
   
   ChoseRetribution(9);
   
   Actions.ShowMSG(-1, '<$0>');   // Welcome message
   Actions.ShowMSG(-1, text);      // Players representation
end;

procedure OnTick;
var PL: Integer;
begin

   
   //PRODUCTION ENHANCEMENT
   
   i:= States.GameTime mod 640
   
   if (i mod 10 = 0) and ((i mod 80) div 10 < 6) then MultiplyWares((i mod 80) div 10, i div 80);
               
   
   //EXTRA BUILDING PLACEMENT
   
   if States.GameTime = NextExtra then begin
      
      {for h:=0 to 2 do begin
         for i:=0 to 4 do begin
            for j:=0 to 3 do begin
               
               if States.UnitAt(ExtraBuildingTiles[h][i][j].X, ExtraBuildingTiles[h][i][j].Y) <> -1 then begin
                     Actions.GroupKillAll(States.UnitsGroup(States.UnitAt(ExtraBuildingTiles[h][i][j].X, ExtraBuildingTiles[h][i][j].Y)), false);
                     UnitsMustBeRemovedFirst:= true;
                  
               end;
               
            end;
         end;
      end;}
      
      if AlreadyRemovedUnits = false then begin
      
         for h:=0 to 2 do begin
         
            for i:= -5 to 5 do begin
            
               for j:= -7 to 7 do begin
                  
                  m:= RandomQuestPlace[h][0] + i;
                  n:= RandomQuestPlace[h][1] + j;
                  
                  if States.UnitAt(m, n) <> -1 then begin
                  
                     Actions.UnitKill(States.UnitAt(m, n), false);
                     UnitsMustBeRemovedFirst:= true;
                     
                  end;
                  
               end;
            
            end;
         
         end;
      
      end;
      
      if UnitsMustBeRemovedFirst then begin
         NextExtra:= NextExtra + 20;         
         UnitsMustBeRemovedFirst:= false;
         AlreadyRemovedUnits:=true;
         Exit;
      end;
      
      RandomQuestLoc:= States.KaMRandomI(High(RandomQuestPlace)+1);
      RandomQuestType:= States.KaMRandomI(High(RandomQuestHouses)+1);
      
      j:= Actions.GiveHouse(8, RandomQuestHouses[RandomQuestType], RandomQuestPlace[RandomQuestLoc][0],  RandomQuestPlace[RandomQuestLoc][1]);
      
      
   end;
   
   // Statistics
   if (States.GameTime mod UPDATE_OVERLAY_DELAY) = 0 then UpdateOverlay();
   
   i:= States.GameTime mod 10
   
      if (i < 8) then begin
         
         if States.PlayerEnabled(i) then begin
            // Schoolhouse
            if not States.PlayerIsAI(i) then Recruits(i);
            // Bonuses
            if PLAYER[i].Bonus = btSurvivalists then Survivalist(i)
            else if PLAYER[i].Bonus = btMasterCrafters then MasterCrafter(i);
            
            for j:=0 to 5 do MultiplyWares(j, i);
      
      end;
         
         //EXTRAS
         
         if ExtraActive[0] = true then begin //0 is Plague!
      
            Extra_RandomDeath(ExtraRecipient, i);
            
            if States.GameTime > ExtraEnd then begin
            
               ExtraActive[0]:= false;
               NextExtra:= States.GameTime + ExtraInterval - ExtraDuration;
            end;
         
         end;
         
         if ExtraActive[1] = true then begin //1 is Abundance!
         
            for j:=0 to 7 do begin
            
               if Alliance[ExtraRecipient][j] = true then begin
               
                  FeedEveryUnit(States.PlayerGetAllUnits(j));
                  FillEveryInn(PlayerInns[j]);
                  AddExtraFoodToStore(j);
               
               end;
               
            end;
            
            ExtraActive[1]:= false;
            NextExtra:= States.GameTime + ExtraInterval;
            
         end;
         
         if ExtraActive[2] = true then begin //2 is God's Hand!
            
            UnitTypeToDie[0]:= States.KamRandomI(24);
            repeat
               UnitTypeToDie[1]:= States.KamRandomI(24);
            until UnitTypeToDie[0] <> UnitTypeToDie[1];
            
            for j:=0 to 7 do begin
            
               if Alliance[ExtraRecipient][j] = false then begin
               
                  KillRandomType(UnitTypeToDie[0], j);
                  KillRandomType(UnitTypeToDie[1], j);
               
               end;
               
            end;
            
            ExtraActive[2]:= false;
            NextExtra:= ExtraStart + ExtraInterval;
               
         
         end;
         
         if ExtraActive[3] = true then begin //3 is Conflagration!
            
            
            for j:= 0 to 7 do begin
            
               if Alliance[ExtraRecipient][j] = false then begin
               
                  HousesToDestroy:= States.PlayerGetAllHouses(j);
                  
                  for k:=1 to (((High(HousesToDestroy)) * PortionToDestroy) div 100) do begin
                     
                     BuildingToDestroy:= States.KaMRandomI(High(HousesToDestroy)+1);
                     
                     case States.HouseType(HousesToDestroy[BuildingToDestroy]) of
                        
                        11, 13, 18, 21, 27, 29: Dec(k);
                        else Actions.HouseDestroy(HousesToDestroy[BuildingToDestroy], false);
                        
                     end;
                  
                  end;
               
               end;
               
            end;
            
            ExtraActive[3]:= false;
            NextExtra:= States.GameTime + ExtraInterval;            
         
         end;
         
         if ExtraActive[4] = true then begin //4 is Weapon Fair in Town!
         
            for j:=0 to 7 do begin
            
               if Alliance[ExtraRecipient][j] = true then begin
               
                  for k:=0 to High(PLAYER[j].Buildings.Barracks) do begin
                  
                     GiveWeaponFair(PLAYER[j].Buildings.Barracks[k]);
               
                  end;
                  
               end;
               
            end;

            ExtraActive[4]:= false;
            NextExtra:= States.GameTime + ExtraInterval;
         
         end;
         
         if ExtraActive[5] = true then begin //5 is REAL Peasants' Rebellion!
         
            for j:=0 to 7 do begin
            
               if Alliance[ExtraRecipient][j] = false then Actions.PlayerAllianceChange(9, j, true, false) else Actions.PlayerAllianceChange(9, j, true, true);
               
            end;
            
            Actions.PlayerAllianceChange(8, 9, true, true);
            
            for j:=0 to High(RebelSpawnLoc) do begin
            
               Actions.GiveGroup(9, 24, RebelSpawnLoc[j][0], RebelSpawnLoc[j][1], 4, (RebelNumber div Length(RebelSpawnLoc)), 8);
            
            end;
            
            ExtraActive[5]:= false;
            NextExtra:= States.GameTime + ExtraInterval;
            
         end;
         
      end;
      
end;

<<

grayter

Rogue

Posts: 58

Joined: 18 Aug 2014, 12:06

Location: Poland

KaM Skill Level: Skilled

Post 08 Nov 2017, 07:09

Re: Error in script, please help!

I think this message appears, because you try to do something with group which is dead. I suggest you to check States.GroupDead in each case. You need to reproduce the problem, because code analysis is hard indeed. Try to find exact moment when problem appears. Maybe you will be able to localize problem in some function/procedure. It will be easier that way.
<<

Rey

Pikeman

Posts: 160

Joined: 12 Oct 2016, 07:41

Location: Moscow

KaM Skill Level: Skilled

Post 08 Nov 2017, 07:48

Re: Error in script, please help!

I checked source code - error happens in next block:
https://github.com/Kromster80/kam_remak ... s.pas#L862

It happens, when "//Enemy was killed, but target Group still exists", then we try to find another enemy from the same group, but there are no valid (not dead or dying) group members. Sounds like a bug for me. That should never happen.
If you could track the situation, when it happens, then I will be able to fix it.
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 08 Nov 2017, 12:44

Re: Error in script, please help!

Thank you!

I believe this issue is probably caused by a fact that I have a code which kills random enemy unit every second during a set period.

What I think happens is that there are 2 units in the group: one that was just killed by a unit, ordered to attack it. So group still exists, meaning next target will be selected. But - in the meantime - my script kills this unit, causing a crash. No?
What do you think?

If you want to find this script part search for the term “plague” under ontick.

I will have access to the crash replay this evening, so I will try to reproduce or observe the moment it happens.

Appreciate your help guys!
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 09 Nov 2017, 18:40

Re: Error in script, please help!

Sadly this was not the case. Noone was tough people that game.

I was able to get my hands on the replay.

Although, I cannot reproduce the error, nor can i get the replay to display what's happening. I get a warning message instead of the regular bug window.

But now I fully understand the source code: A unit had an attack order an another unit. This unit died while being targeted. Then code was looking for another foe, and this is where I get the error.
<<

Rey

Pikeman

Posts: 160

Joined: 12 Oct 2016, 07:41

Location: Moscow

KaM Skill Level: Skilled

Post 09 Nov 2017, 20:03

Re: Error in script, please help!

So it seems to be a quite rare error.
I can simply fix it removing Assert clause and just ignore that situation.
Ofc its better to figure it out why it happens exactly, but considering that it appears very rary I think this solution is good enought
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 09 Nov 2017, 21:15

Re: Error in script, please help!

If you want the replay, here you go.

By the way how do you modify source code?
You do not have the required permissions to view the files attached to this post.
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 10 Nov 2017, 21:39

Re: Error in script, please help!

The error happened again. So tomorrow I will try to look into it again.

I have a suggestion.

You should not remove the assert part. I think if the assert condition returns false, it should restart from the 'if'. So it checks again:

------------------------------------------------------------------------------

if (OrderTargetUnit = nil) and (OrderTargetGroup <> nil) then
begin
//Old enemy has died, change target to his comrades
U := OrderTargetGroup.GetNearestMember(Members[0].GetPosition);
Assert(U <> nil, 'We checked that Group is not dead, hence we should have a valid Unit'); (here, it goes back to the start)
OrderAttackUnit(U, False);
end;

------------------------------------------------------------------------------


What do you think?
<<

Erdesz Balazs

Serf

Posts: 7

Joined: 07 Nov 2017, 19:43

KaM Skill Level: Skilled

Post 11 Nov 2017, 17:20

Re: Error in script, please help!

I had time to investigate. I have the EXACT reason for the error.

My script kills random units every second. And, during this, it can kill units that haven't yet spawned to the playground. (For example, it can kill units that have already been trained but haven't left the barrack yet.)

Then when this group's units would vanish completely (e.g. last unit is killed), I get the error.





So the issue is: When these units die inside the barrack, they are most probably not removed from the group they were supposed to be part of. And then the game tries to look for a new target in the group, but the group is nonexistent anymore.
<<

Rey

Pikeman

Posts: 160

Joined: 12 Oct 2016, 07:41

Location: Moscow

KaM Skill Level: Skilled

Post 12 Nov 2017, 07:43

Re: Error in script, please help!

Thank you very much for your investigation!

I will look into it and try to fix it then ;)
<<

Rey

Pikeman

Posts: 160

Joined: 12 Oct 2016, 07:41

Location: Moscow

KaM Skill Level: Skilled

Post 17 Nov 2017, 19:58

Re: Error in script, please help!

Erdesz Balazs wrote:So the issue is: When these units die inside the barrack, they are most probably not removed from the group they were supposed to be part of. And then the game tries to look for a new target in the group, but the group is nonexistent anymore.


I checked the source code - and it looks like your theory is not correct, because soldier is not assigned to any group when he is inside Barracks. Group assign happens only when soldier went outside from Barracks.
I'll fix this error in a very simple way, as I said earlier.

Return to Dynamic Scripting

Who is online

Users browsing this forum: No registered users and 2 guests

cron