Page 1 of 1
					
				Error in script, please help!
				PostPosted: 07 Nov 2017, 20:05
				by Erdesz Balazs
				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...
			 
			
					
				Re: Error in script, please help!
				PostPosted: 07 Nov 2017, 21:18
				by grayter
				Please attach your script file, I'll check if there are some commands which cause the problem. Are you using r6720?
			 
			
					
				Re: Error in script, please help!
				PostPosted: 07 Nov 2017, 22:07
				by Erdesz Balazs
				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;
 
			 
			
					
				Re: Error in script, please help!
				PostPosted: 08 Nov 2017, 07:09
				by grayter
				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.
			 
			
					
				Re: Error in script, please help!
				PostPosted: 08 Nov 2017, 07:48
				by Rey
				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.
 
			 
			
					
				Re: Error in script, please help!
				PostPosted: 08 Nov 2017, 12:44
				by Erdesz Balazs
				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!
			 
			
					
				Re: Error in script, please help!
				PostPosted: 09 Nov 2017, 18:40
				by Erdesz Balazs
				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.
			 
			
					
				Re: Error in script, please help!
				PostPosted: 09 Nov 2017, 20:03
				by Rey
				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
			 
			
					
				Re: Error in script, please help!
				PostPosted: 09 Nov 2017, 21:15
				by Erdesz Balazs
				If you want the replay, here you go.
By the way how do you modify source code?
			 
			
					
				Re: Error in script, please help!
				PostPosted: 10 Nov 2017, 21:39
				by Erdesz Balazs
				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?
			 
			
					
				Re: Error in script, please help!
				PostPosted: 11 Nov 2017, 17:20
				by Erdesz Balazs
				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.
			 
			
					
				Re: Error in script, please help!
				PostPosted: 12 Nov 2017, 07:43
				by Rey
				Thank you very much for your investigation! 
I will look into it and try to fix it then 

 
			 
			
					
				Re: Error in script, please help!
				PostPosted: 17 Nov 2017, 19:58
				by Rey
				
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.