//
// player.script
//

function _player_GetTPlayerArgs(const plHnd : Integer) : Pointer;
begin
   Result := StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
   if (gProfile.bDbgLogPointerNil) then
   _init_DebugPointerNil(Result);
end;

// _player_ClearNationStatistics
//
procedure _player_ClearPlayerStatistics(var playerstat : TPlayerStatistics);
begin
   // очистка игровой статистики игрока
   var i, j : Integer;
   for i:=0 to gc_ResCount-1 do
   begin
      playerstat.restotal[i] := 0;
      playerstat.resonupgrade[i] := 0;
      playerstat.resonmines[i] := 0;
      playerstat.resonunits[i] := 0;
      playerstat.resonbuildings[i] := 0;
      playerstat.resonlife[i] := 0;
      playerstat.resbuy[i] := 0;
      playerstat.ressell[i] := 0;
   end;
   for i:=0 to gc_MaxCountryCount-1 do
   for j:=0 to gc_country_maxmembers-1 do
   begin
      playerstat.killed[i][j] := 0;
      playerstat.produced[i][j] := 0;
   end;
   playerstat.population.Clear;
   playerstat.population.SetCapacity(playerstat.population.GetGrowthDelta);
   playerstat.scores.Clear;
   playerstat.scores.SetCapacity(playerstat.population.GetGrowthDelta);
end;

// _player_ClearUpgradeState
//
procedure _player_ClearUpgradeState(var upgstate : TPlayerUpgradeState);
begin
   // обнуления состояния выполнения апгрейда игрока
   upgstate.sid := '';
   upgstate.done := False;
   upgstate.enabled := False;
   upgstate.inprogress := False;
   upgstate.grey := False;
   upgstate.timestart := 0;
   upgstate.timedone := 0;
   upgstate.bscenariodisabled := False;
end;

// _player_Init
//
procedure _player_Init(var player : TPlayer; id : Integer);
begin
   var i, j : Integer;
   player.id := id;
   player.counter.total := 0;
   player.counter.farmused := 0;
   player.counter.scores := 0;
   for i:=0 to gc_MaxCountryCount-1 do
   for j:=0 to gc_country_maxmembers-1 do
   begin
      _unit_InitBase(gPlayer[id].objbase[i][j], gObjProp[i][j], i, j);
      player.counter.all[i][j] := 0;
      player.counter.built[i][j] := 0;
   end;
   player.cid := 0;
   //player.csid := '';
   _player_ClearPlayerStatistics(player.stat);
   for i:=0 to gc_MaxArtilleryType-1 do
   begin
      player.artlimit[i] := 0;
      player.artcount[i] := 0;
   end;
   for i:=0 to gc_MaxCountryCount-1 do
   for j:=0 to gc_country_maxupgradecount-1 do
   begin
      _player_ClearUpgradeState(player.upgstate[i][j]);
      player.upgstate[i][j].sid := gCountry[i].upgrade[j].id;
      player.upgstate[i][j].enabled := gCountry[i].upgrade[j].enabled;
   end;
   for i:=0 to gc_ResCount-1 do
   begin
      if (id=gc_player_mercenaryind) then
      begin
         case i of
            gc_resource_type_food : _res_SetResToPlayerByIndex(id, i, 10000);
            gc_resource_type_gold : _res_SetResToPlayerByIndex(id, i, 20000);
            gc_resource_type_iron : _res_SetResToPlayerByIndex(id, i, 10000);
            gc_resource_type_coal : _res_SetResToPlayerByIndex(id, i, 10000);
            else
            _res_SetResToPlayerByIndex(id, i, 10000);
         end;
      end
      else
      _res_SetResToPlayerByIndex(id, i, 5000000);
      player.counter.resconsume[i] := 0;
      player.counter.resconsumeremains[i] := 0;
      player.counter.resincome[i] := 0;
      player.counter.resincomeremains[i] := 0;
   end;
   for i:=0 to gc_MaxCountryCount-1 do
   for j:=0 to gc_ResCount-1 do
   player.resefficiency[i][j] := 100;
   player.fieldlife := 0;
   player.victorystate := gc_player_victorystate_none;
   player.victorystategametime := 0;
   player.unitcount := 0;
   player.farm := 0;
   player.enemyplmask := 255 xor (1 shl id);
   player.myplmask := (1 shl id);
   player.team := 0;
   player.difficulty := gc_player_difficulty_easy;
   player.bexists := False;
   player.bgeology := False;
   player.bballoon := False;
   player.bfamine := False;
   player.brebellion := False;
   player.bai := False;
   player.lists.buildings.Clear;
   player.lists.storehouses.Clear;
   player.lists.ports.Clear;
   
   //for alarm
   player.lastattacktime := 0;
   player.lastprogresstime := 0;
   player.progresstick := id*2;
   player.searchenemyprev := 0;
   player.searchenemylast := 0;
   player.searchenemycur := 0;
   player.searchenemylasttime := 0;
   
   player.lastcheckexists := id*0.25+gc_global_TimeCheckExists;
   player.lastcalcsquadsmovecounttime := id*0.25+gc_global_TimeCalcSquadsMoveCount;
   player.lastprogressaitime := id*0.25+gc_global_TimeProgressAI;
   player.lastseedwheattime := id*0.25+gc_global_TimeSeedWheat;
   
   player.squads.Clear;
   player.aiData.Reset;
end;

// _player_ReInitAllPlayersBaseObj
//
procedure _player_ReInitAllPlayersBaseObj();
begin
   var clearobjbase : TObjBase;
   var clearobjprop : TObjProp;
   var i, j, k : Integer;
   for i:=0 to gc_MaxPlayerCount-1 do
   begin
      for j:=0 to gc_MaxCountryCount-1 do
      for k:=0 to gc_country_maxmembers-1 do
      begin
         gPlayer[i].objbase[j][k] := clearobjbase;
         gObjProp[j][k] := clearobjprop;
         _unit_InitBase(gPlayer[i].objbase[j][k], gObjProp[j][k], j, k);
      end;
   end;
end;

// _player_InitAllPlayers
//
procedure _player_InitAllPlayers();
begin
   var i : Integer;
   for i:=0 to gc_MaxPlayerCount-1 do
   begin
      _player_Init(gPlayer[i], i);
      var def : String = 'pol';
      gPlayer[i].cid := _country_GetIDBySID(def);
      //gPlayer[i].csid := cdef;
   end;
   gWallSystem.Reset;
end;

// _player_ResetAllPlayers
//
procedure _player_ResetAllPlayers();
begin
   var clearobjbase : TObjBase;
   var clearobjprop : TObjProp;
   var i, j, k : Integer;
   for i:=0 to gc_MaxPlayerCount-1 do
   begin
      for j:=0 to gc_MaxCountryCount-1 do
      for k:=0 to gc_country_maxmembers-1 do
      begin
         gPlayer[i].objbase[j][k] := clearobjbase;
         gObjProp[j][k] := clearobjprop;
      end;
   end;
   _player_InitAllPlayers;
end;

// _player_CreateConstructionDummyBySID
//
function _player_CreateConstructionDummyBySID(const plHnd, cid : Integer; var sid : String; const px, pz : Float) : Integer;
begin
   //_misc_ProfilerBegin('_player_CreateConstructionDummyBySID');
   // Создание заглушки здания, которая двигается за курсором мышки для выбора места, куда построить здание.
   // Функцию реализовано кустарно, используя тот функционал, который есть. Этот механизм возможно нужно переписать. Пока он больше напоминает хак и костыль.
   var goHnd : Integer = CreatePlayerGameObjectHandleByHandle(plHnd, gc_racename_buildings, sid, px, RayCastHeight(px, pz), pz);
   if (GetGameObjectCountChildByHandle(goHnd)>0) then
   begin
      var child : Integer = GetGameObjectGOHandleChildByHandle(goHnd, 0);
      SetGameObjectVisibleByHandle(child, False);
   end;
   SetGameObjectColIntMaskOptionsByHandle(goHnd, True, False);
   var id : Integer = _unit_ConvertObjSIDToID(cid, sid);
   SetGameObjectTagFloatByHandle(goHnd, cid*256+id);
   Result := goHnd;
   //_misc_ProfilerEnd('_player_CreateConstructionDummyBySID');
end;

// _player_CreateConstructionDummy
//
function _player_CreateConstructionDummy(const plHnd, cid, id : Integer; const px, pz : Float) : Integer;
begin
   // Создание заглушки здания, которая двигается за курсором мышки для выбора места, куда построить здание.
   // Функцию реализовано кустарно, используя тот функционал, который есть. Этот механизм возможно нужно переписать. Пока он больше напоминает хак и костыль.
   var sid : String;
   _unit_ConvertObjIDToSID(cid, id, sid);
   Result := _player_CreateConstructionDummyBySID(plHnd, cid, sid, px, pz);
end;

// _player_GoldIncomeLessThanConsume
//
function _player_GoldIncomeLessThanConsume(const plInd : Integer) : Boolean;
begin
   var rawincome : Float = gPlayer[plind].counter.resincome[gc_resource_type_gold]/10;
   var rawconsume : Float = (gPlayer[plind].counter.resconsume[gc_resource_type_gold]*32/100)/250;
   Result := (rawincome<rawconsume);
end;

// _player_ProcessResourceIncome
//
procedure _player_ProcessResourceIncome(const plInd : Integer; const deltatime : Float);
begin
   var i : Integer;
   for i:=0 to gc_ResCount-1 do
   begin
      var resincome : Integer = gPlayer[plInd].counter.resincome[i];
      if (resincome>0) then
      begin
         const mult = 100;
         const speed = 256/1.024;
         var resincomerem : Float = gPlayer[plInd].counter.resincomeremains[i]/mult;
         resincome := resincome*gc_time_to_frames;
         var bank : Float = resincomerem+resincome*deltatime;
         var realbank : Float = bank/speed;
         var value : Integer = floor(realbank);
         if (value<>0) then
         begin
            _res_AddResToPlayerByIndex(plInd, i, value);
            gPlayer[plInd].stat.restotal[i] := gPlayer[plInd].stat.restotal[i]+value
         end;
         bank := (realbank-value)*speed;
         gPlayer[plInd].counter.resincomeremains[i] := floor(bank*mult);
      end;
   end;
end;

// _player_ProcessResourceConsume
//
procedure _player_ProcessResourceConsume(const plInd : Integer; const deltatime : Float);
begin
   // потребление еды/золота/дерева/камня на содержание юнитов
   // сбор экономической статистики по количеству добытых и затраченных ресурсов.
   // включение/выключение флагов голод, восстание.
   var i : Integer;
   for i:=0 to gc_ResCount-1 do
   begin
      var resconsume : Integer = gPlayer[plInd].counter.resconsume[i];
      if (resconsume>0) then
      begin
         const mult = 100;
         const speed = 20000;
         var resconsumerem : Float = gPlayer[plInd].counter.resconsumeremains[i]/mult;
         resconsume := resconsume*gc_time_to_frames;
         var bank : Float = resconsumerem+resconsume*deltatime;
         var realbank : Float = bank/speed;
         var value : Integer = floor(realbank);
         if (value<>0) then
         begin
            if (gPlayer[plInd].res[i]>=value) then
            begin
               gPlayer[plInd].stat.resonlife[i] := gPlayer[plInd].stat.resonlife[i]+value;
               _res_AddResToPlayerByIndex(plInd, i, -value);
               case i of
                  gc_resource_type_food : gPlayer[plInd].bfamine := False;
                  gc_resource_type_gold : gPlayer[plInd].brebellion := False;
               end;
            end
            else
            begin
               gPlayer[plInd].stat.resonlife[i] := gPlayer[plInd].stat.resonlife[i]+gPlayer[plInd].res[i];
               _res_AddResToPlayerByIndex(plInd, i, -gPlayer[plInd].res[i]);
               case i of
                  gc_resource_type_food : gPlayer[plInd].bfamine := True;
                  gc_resource_type_gold : begin
                     if (gPlayer[plInd].counter.resconsume[i]>gPlayer[plInd].counter.resincome[i]) then
                     gPlayer[plInd].brebellion := True
                     else
                     gPlayer[plInd].brebellion := False;
                  end;
               end;
            end;
         end;
         bank := (realbank-value)*speed;
         gPlayer[plInd].counter.resconsumeremains[i] := floor(bank*mult);
      end;
   end;
   if (gPlayer[plInd].brebellion) and ((gPlayer[plInd].res[gc_resource_type_gold]>=2) or (gPlayer[plInd].counter.resconsume[gc_resource_type_gold]<=0)) then
   gPlayer[plInd].brebellion := False;
   if (gPlayer[plInd].bfamine) and ((gPlayer[plInd].res[gc_resource_type_food]>0) or (gPlayer[plInd].counter.resconsume[gc_resource_type_food]<=0)) then
   gPlayer[plInd].bfamine := False;
end;

// _player_CheckAccessControlRequirements
//
function _player_CheckAccessControlRequirements(const plHnd, cid : Integer; var sid : String) : Integer;
begin
   // Проверка, country (национальных) запретов на производство юнита или совершение апгрейда
   Result := gc_result_checkaccesscontrolreq_ok;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   
   // Определяаем, является ли запрашиваемый объект юнитом или апгрейдом
   var bMember : Boolean = _country_IsCountryMember(cid, sid);
   var bUpgrade : Boolean;
   if (bMember) then
   bUpgrade := False
   else
   bUpgrade := _country_IsCountryUpgrade(cid, sid);
   if (not bMember) and (not bUpgrade) then
   ErrorLog('_player_CheckAccessControlRequirements : Unknown sid = '+sid);
   
   // Разрешен ли данный юнит для производства. Не выполняется ли в данный момент этот апгрейд (одновременное выполнение одного и того же апгрейда запрещено).
   var id : Integer;
   var upgind : Integer;
   var bEnabled : Boolean;
   if (bMember) then
   begin
      id := _unit_ConvertObjSIDToID(cid, sid);
      bEnabled := (_country_GetEnabledIndexBySID(cid, sid)>-1) and (gPlayer[plInd].objbase[cid][id].bproduceenabled);
   end
   else
   if (bUpgrade) then
   begin
      upgind := _country_GetUpgradeIndexByUpgradeID(cid, sid, False);
      bEnabled := (gPlayer[plInd].upgstate[cid][upgind].enabled) and (not (gPlayer[plInd].upgstate[cid][upgind].done) and (not gPlayer[plInd].upgstate[cid][upgind].bscenariodisabled));
   end;
   
   if (bEnabled) then
   begin
      if (bMember) then
      begin
         // Не заблокировано ли производство механизмом LockCount (ограничение на количество юнитов/построек одного типа у игрока).
         var ind : Integer = _country_GetUnitLockIndexBySID(cid, sid, False);
         if (ind>=0) then
         begin
            var locksid : String = gCountry[cid].unitlock[ind].lockid;
            var lockid : Integer = _unit_ConvertObjSIDToID(cid, locksid);
            var count : Integer = gPlayer[plInd].counter.all[cid][lockid];
            var lockcount : Integer = gCountry[cid].unitlock[ind].count;
            if (count>=lockcount) then
            Result := gc_result_checkaccesscontrolreq_lockcount;
         end;
         if (Result=gc_result_checkaccesscontrolreq_ok) then
         begin
            if (gObjProp[cid][id].bArtillery) and (gPlayer[plInd].artcount[gObjProp[cid][id].artind]>=gPlayer[plInd].artlimit[gObjProp[cid][id].artind]) then
            Result := gc_result_checkaccesscontrolreq_artlimit;
         end;
      end;
      if (Result=gc_result_checkaccesscontrolreq_ok) then
      begin
         // Сверка списка необходимых построек/апгрейдов для данной постройки или апгрейда, с имеющимися у игрока постройками и выполненными апгрейдами
         var ind : Integer = _country_GetAccessControlIndexBySID(cid, sid, False);
         if (ind>=0) then
         begin
            var i : Integer;
            for i:=0 to gc_country_maxaccesscontrolreqs-1 do
            begin
               var reqsid : String = gCountry[cid].accesscontrol[ind].req[i];
               if (reqsid<>'') then
               begin
                  if (_country_IsCountryMember(cid, reqsid)) then
                  begin
                     var reqid : Integer = _unit_ConvertObjSIDToID(cid, reqsid);
                     if (reqid>0) and (reqsid<>'') then
                     begin
                        if ((gScenario.bactive) or (gPlayer[plInd].objbase[cid, reqid].bproduceenabled)) and (gPlayer[plInd].counter.built[cid][reqid]<=0) then
                        begin
                           Result := gc_result_checkaccesscontrolreq_noprerequisit;
                           //break;
                        end;
                     end
                     else
                     break;
                  end
                  else
                  begin
                     var upgind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, reqsid, False);
                     if (upgind>0) then
                     begin
                        var requpgind : Integer;
                        if (gPlayer[plInd].upgstate[cid][upgind].sid=reqsid) then
                        requpgind := upgind
                        else
                        begin
                           var j : Integer;
                           for j:=0 to gc_MaxCountryCount-1 do
                           begin
                              if (gPlayer[plInd].upgstate[cid][j].sid=reqsid) then
                              begin
                                 requpgind := j;
                                 break;
                              end;
                           end;
                        end;
                        if (requpgind>0) then
                        begin
                           if (not gPlayer[plInd].upgstate[cid][requpgind].done) then
                           begin
                              if ((gScenario.bactive) and (gPlayer[plInd].upgstate[cid][requpgind].bscenariodisabled)) then
                              Result := gc_result_checkaccesscontrolreq_hide
                              else
                              Result := gc_result_checkaccesscontrolreq_noprerequisit;
                              break;
                           end;
                        end
                        else
                        ErrorLog('_player_CheckAccessControlRequirements : Unknown requpgsid = '+reqsid)
                     end
                     else
                     ErrorLog('_player_CheckAccessControlRequirements : Unknown reqsid = '+reqsid);
                  end;
               end;
            end;
         end;
      end;
   end
   else
   Result := gc_result_checkaccesscontrolreq_notenabled;
end;

// _player_UpdateFOW
//
procedure _player_UpdateFOW(plInd : Integer);
begin
   var bFOW : Boolean = True;
   if (gPlayer[plInd].bballoon) or (gMap.settings.additional.balloon=gc_mapsettings_balloon_with) or (gPlayer[plInd].victorystate<>gc_player_victorystate_none) then
   bFOW := False;
   if (not gbool_editor_fogofwar) then
   bFOW := False;
   ClearFOWPlayers;
   if (bFOW) then
   begin
      AddFOWPlayers(GetPlayerHandleByIndex(plInd));
      var team : Integer = gPlayer[plInd].team;
      var i : Integer;
      for i:=0 to gc_MaxPlayerCount-1 do
      begin
         if (i<>plInd) and (gPlayer[i].team=team) then
         begin
            //if ((not gScenario.bactive) or (plInd<>0)) or ((plInd=0) and (not gPlayer[i].bneutral)) then
            if (plInd<>0) or (not gPlayer[i].bneutral) then
            AddFOWPlayers(GetPlayerHandleByIndex(i));
         end;
      end;
      SetFOWDovFunc('_unit_GetVision');
   end;
   SetFOWEnable(bFOW);
end;

// _player_StopSquad
//
procedure _player_StopSquad(plInd, squad : Integer);
begin
   var pSquad : Pointer = _misc_GetTSquad(plInd, squad);
   var i : Integer;
   for i := TSquad(pSquad).GetCount-1 downto 0 do
   begin
      var goHnd : Integer = TSquad(pSquad).Get(i);
      _unit_Stop(goHnd);
   end;
end;

// _player_SetGroupPosition
//
procedure _player_SetGroupPosition(plHnd, grHnd, squadInd : Integer; posX, posZ, visPosX, visPosZ, dirX, dirZ : Float; bAddOrder, bDoFirst, bRebuild, bCenter : Boolean; mode : Integer);
begin
   var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
   if parg <> nil then
   begin
      if GetGroupCountGameObjectsByHandle(grHnd) > 0 then
      begin
         var goHnd : Integer = GetGroupGOHandleByGOIndexByHandle(grHnd, 0);
         if _unit_IsWaterUnit(goHnd) then
         begin
            var pObjProp : Pointer = _unit_GetObjProp(goHnd);
            if TObjProp(pObjProp).transport > 0 then
            begin
               // TODO : FAUST : move to write/readmove states to work on client side
               SetGameObjectRayCastIntersectEnabledByHandle(goHnd, False);
               SetGameObjectCIMovableByHandle(goHnd, True);
               var childHnd : Integer = GetGameObjectGOHandleChildByCustomName(goHnd, 'ferry_ladder');
               GameObjectSetFrameAnimationByHandle(childHnd, 'close', false);
               SetGameObjectAnimationModeByHandle(childHnd, 'aamPlayOnce');
            end;
         end;
      end;
      
      TPlayerArgs(parg).fposx := posX;
      TPlayerArgs(parg).fposz := posZ;
      TPlayerArgs(parg).fvisposx := visPosX;
      TPlayerArgs(parg).fvisposz := visPosZ;
      TPlayerArgs(parg).fdirx := dirX;
      TPlayerArgs(parg).fdirz := dirZ;
      TPlayerArgs(parg).faddord := bAddOrder;
      TPlayerArgs(parg).fdofirst := bDoFirst;
      TPlayerArgs(parg).frebuild := bRebuild;
      TPlayerArgs(parg).fcenter := bCenter;
      TPlayerArgs(parg).fmode := mode;
      TPlayerArgs(parg).fgroup := grHnd;
      TPlayerArgs(parg).fsquad := squadInd;
      PlayerExecuteStateByHandle(plHnd, 'WriteMove');
   end;
end;

// _player_SetSquadPosition
//
procedure _player_SetSquadPosition(plHnd, squadInd : Integer; posX, posZ, dirX, dirZ : Float; bAddOrder, bCenter : Boolean; mode : Integer; blan : Boolean);
begin
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var pSquad : Pointer = gPlayer[plInd].squads.Get(squadInd);
   if pSquad <> nil then
   begin
      var grName : String = TSquad(pSquad).fGroupName;
      var grHnd : Integer = GetPlayerGrHandleByHandle(plHnd, grName);
      if (grHnd=0) then
      ErrorLog('_player_SetSquadPosition grHnd=0');
      
      if (TSquad(pSquad).fFormation < 0) and (TSquad(pSquad).fWidth * TSquad(pSquad).fHeight < TSquad(pSquad).GetCount) then
      _squad_SetCustomFormation(pSquad, true);
      
      _group_AddUnitsFromSquad(grHnd, pSquad);
      _group_Initialize(grHnd, pSquad);
      _player_SetGroupPosition(plHnd, grHnd, squadInd, posX, posZ, posX, posZ, dirX, dirZ, bAddOrder, false, false, bCenter, mode);
   end;
end;

// _player_StopSelectedUnits
//
procedure _player_StopSelectedUnits(plInd : Integer; var units : TIntegerList; var squads : TPtrList; blan : Boolean);
begin
   //if list.GetCount > 0 then
   begin
      if _net_IsOffline or _net_IsServer then
      begin
         var i : Integer;
         for i := units.GetCount-1 downto 0 do
         begin
            var goHnd : Integer = units.Get(i);
            if (not _unit_IsBuilding(goHnd)) or (_unit_GetUsage(goHnd)=gc_obj_usage_tower) then
            _unit_Stop(goHnd);
         end;
         
         for i:=squads.GetCount-1 downto 0 do
         begin
            var psquad : Pointer = squads.GetPointer(i);
            if (psquad<>nil) then
            begin
               var posx, posz, dirx, dirz : Float;
               var plHnd : Integer = GetPlayerHandleByIndex(plInd);
               var movemode : Integer = _misc_GetSquadMoveMode(psquad);
               _squad_GetCentralPosAndDir(psquad, posx, posz, dirx, dirz);
               _player_SetSquadPosition(plHnd, TSquad(psquad).fIndex, posx, posz, dirx, dirz, False, True, movemode, True);
               _player_StopSquad(plInd, TSquad(psquad).fIndex);
            end;
         end;
      end;
      
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         var plHnd : Integer = GetPlayerHandleByIndex(plInd);
         var pArg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
         if pArg<>nil then begin
            // STOP UNITS
            if (units.GetCount>0) then
            begin
               TPlayerArgs(pArg).fintlst := units;
               PlayerExecuteStateByHandle(plHnd, 'WriteStop');
            end;
            // STOP SQUADS
            if (squads.GetCount>0) then
            begin
               TPlayerArgs(parg).fpointer0 := squads;
               TPlayerArgs(parg).fordtyp := gc_playersquad_action_stop;
               TPlayerArgs(parg).fbstate := True;
               TPlayerArgs(parg).fform := 0;
               TPlayerArgs(parg).fintlst := gIntListWriteSquadAction;
               PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
            end;
         end;
      end;
   end;
end;

// _player_ResetSaveLoadStates
//
procedure _player_ResetSaveLoadStates(plhnd: Integer);
begin
   SetPlayerGlobalBeforeSaveStateName(plhnd, '');
   SetPlayerGlobalAfterLoadStateName(plhnd, '');
   SetPlayerGroupsBeforeSaveStateName(plhnd, '');
   SetPlayerGroupsAfterLoadStateName(plhnd, '');
end;

// _player_AddGamePlayer
//
function _player_AddGamePlayer(): Integer;
begin
   if GetCountOfPlayers<gc_MaxPlayerCount then begin
      result:=CreatePlayer('player'+IntToStr(GetCountOfPlayers), gc_racename_units, 'cmPlayer');
      PlayerExecuteStateByHandle(result, 'Initial');
      _player_ResetSaveLoadStates(result);
   end else result:=0;
end;

// _player_AddServicePlayers
//
procedure _player_AddServicePlayers();
begin
   if(GetPlayerHandleByName(gc_racename_env)=0) then begin
      var hnd: Integer=CreatePlayerInsert(gc_racename_env, gc_racename_env, 'cmPC', gc_playerind_env);
      _player_ResetSaveLoadStates(hnd);
   end;
   
   if (GetPlayerHandleByName(gc_racename_misc)=0) then begin
      var hnd: Integer=CreatePlayerInsert(gc_racename_misc, gc_racename_misc, 'cmPC', gc_playerind_misc);
      _player_ResetSaveLoadStates(hnd);
      var i : Integer;
      for i:=0 to gc_MaxPlayerCount-1 do
      AddPlayerEnemyPlayerByHandle(hnd, GetPlayerHandleByIndex(i));
   end;
   
   if (GetPlayerHandleByName(gc_racename_progress)=0) then
   begin
      var hnd: Integer=CreatePlayerInsert(gc_racename_progress, gc_racename_progress, 'cmPC', gc_playerind_progress);
      SetPlayerProgressOnLanByHandle(hnd, True);
      _player_ResetSaveLoadStates(hnd);
      SetPlayerProgressOnEditByHandle(hnd, true);
   end;
   
   if (GetPlayerHandleByName(gc_plname_pool)=0) then begin
      var hnd: Integer=CreatePlayerInsert(gc_plname_pool, gc_racename_misc, 'cmPC', gc_playerind_pool);
      _player_ResetSaveLoadStates(hnd);
      var i : Integer;
      for i:=0 to gc_MaxPlayerCount-1 do
      AddPlayerEnemyPlayerByHandle(hnd, GetPlayerHandleByIndex(i));
   end;
end;

// _player_GetOrCreateGamePlayerByIndex
//
function _player_GetOrCreateGamePlayerByIndex(index: Integer): Integer;
begin
   var plnam: String = 'player'+IntToStr(index);
   var plhnd: Integer = GetPlayerHandleByName(plnam);
   if plhnd=0 then begin
      plhnd:=CreatePlayerInsert(plnam, gc_racename_units, 'cmPlayer', index);
      PlayerExecuteStateByHandle(plhnd, 'Initial');
      _player_ResetSaveLoadStates(plhnd);
      end else begin
      if GetPlayerRaceNameByHandle(plhnd)=gc_racename_units then begin
         if GetPlayerIndexByHandle(plhnd)<>index then
         ExchangePlayerIndex(GetPlayerIndexByHandle(plhnd), index);
         //TXControlMode = (cmNone, cmPC, cmPlayer);
         if GetPlayerControlModeIntByHandle(plhnd)=0 then
         SetPlayerControlModeIntByHandle(plhnd, 2);
         if StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr)=nil then
         PlayerExecuteStateByHandle(plhnd, 'Initial');
         _player_ResetSaveLoadStates(plhnd);
         end else begin
         plhnd:=CreatePlayerInsert(plnam, gc_racename_units, 'cmPlayer', index);
         PlayerExecuteStateByHandle(plhnd, 'Initial');
         _player_ResetSaveLoadStates(plhnd);
      end;
   end;
   result:=plhnd;
end;

// _player_ClearGroup
//
procedure _player_ClearGroup(grHnd : Integer);
begin
   if grHnd <> 0 then
   begin
      GroupClearGameObjectsByHandle(grHnd);
      GroupSetPickedByHandle(grHnd, False);
   end;
end;

// _player_ClearGroups
//
procedure _player_ClearGroups(plInd : Integer);
begin
   var plHnd : Integer = GetPlayerHandleByIndex(plInd);
   var grSelHnd : Integer = GetPlayerGrHandleByHandle(plHnd, gc_group_selected);
   _player_ClearGroup(grSelHnd);
   
   var i : Integer;
   for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
   begin
      var pSquad : Pointer = _misc_GetTSquad(plInd, i);
      var grHnd : Integer = GetPlayerGrHandleByHandle(plHnd, TSquad(pSquad).fGroupName);
      _player_ClearGroup(grHnd);
   end;
end;

// _player_GetFirstSelectedSquad
//
function _player_GetFirstSelectedSquad(plInd : Integer) : Pointer;
begin
   var i : Integer;
   Result := nil;
   for i := 0 to gPlayer[plInd].squads.GetCount-1 do
   if TSquad(gPlayer[plInd].squads.Get(i)).fSelected then
   begin
      Result := gPlayer[plInd].squads.Get(i);
      break;
   end;
end;

// _player_CreateGroupsFromSelected
//
procedure _player_CreateGroupsFromSelected();
begin
   var bBuildingExists, bUnitExists : Boolean;
   if (gSelectedObjects.GetCount>0) then
   begin
      if _unit_IsBuilding(gSelectedObjects.Get(0)) then
      bBuildingExists := True
      else
      bUnitExists := True;
   end;
   var plHnd : Integer = GetPlayerHandleInterfaceIO;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var selGrHnd : Integer = _misc_GetOrCreateGroupByName(plHnd, gc_group_selected);
   GroupClearGameObjectsByHandle(selGrHnd);
   var i, j : Integer;
   for i:=gSelectedObjects.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = gSelectedObjects.Get(i);
      if GetGameObjectPlayableObjectByHandle(goHnd) then
      begin
         if (not bBuildingExists)
         or ((bBuildingExists) and (bUnitExists) and (not _unit_IsBuilding(goHnd)))
         or ((bBuildingExists) and (not bUnitExists) and _unit_IsBuilding(goHnd)) then
         GroupAddGameObjectByHandle(selGrHnd, goHnd)
         else
         begin
            SetGameObjectPickedByHandle(goHnd, False);
            _misc_UnitUpdateMiniMapPrimitive(goHnd, False);
         end;
      end;
   end;
   if not bBuildingExists then
   begin
      if GetGroupCountGameObjectsByHandle(selGrHnd) > 0 then
      _group_Initialize(selGrHnd, nil);
      
      for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
      if TSquad(gPlayer[plInd].squads.Get(i)).fSelected then
      begin
         var pSquad : Pointer = gPlayer[plInd].squads.Get(i);
         var grHnd : Integer = GetPlayerGrHandleByHandle(plHnd, TSquad(pSquad).fGroupName);
         
         _group_AddUnitsFromSquad(grHnd, pSquad);
         _group_Initialize(grHnd, pSquad);
      end;
   end;
end;

// _player_SetSquadFormation
//
procedure _player_SetSquadFormation(plHnd, squadInd, formInd : Integer; bSetPosition, blan : Boolean);
begin
   var i, j, k : Integer;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var pSquad : Pointer = gPlayer[plInd].squads.Get(squadInd);
   if (pSquad <> nil) and (formInd >= 0) then
   begin
      if _net_IsOffline or ({blan and} _net_IsServer) or ((not blan) and _net_IsClient) then
      begin
         TSquad(pSquad).fFormation := formInd;
         var goCount : Integer = TSquad(pSquad).GetCount;
         
         var cols : Integer = gFormation[formInd].width;
         var rows : Integer = gFormation[formInd].height;
         
         TSquad(pSquad).fWidth := cols;
         TSquad(pSquad).fHeight := rows;
         TSquad(pSquad).fAddDamage := gFormation[formInd].bonusdamage;
         TSquad(pSquad).fAddShield := gFormation[formInd].bonusshield;
         TSquad(pSquad).fAddDamageHold := gFormation[formInd].bonusdamagehold;
         TSquad(pSquad).fAddShieldHold := gFormation[formInd].bonusshieldhold;
         
         var offHnd : Integer = _squad_GetOfficer(pSquad);
         var drumHnd : Integer = _squad_GetDrummer(pSquad);
         var bOfficer : Boolean = (offHnd <> 0);
         var bDrummer : Boolean = (drumHnd <> 0);
         
         for i := 0 to rows-1 do
         for j := 0 to cols-1 do
         TSquad(pSquad).arGrid[i, j] := 0;
         
         var ind : Integer;
         for [MAIN]i := rows-1 downto 0 do
         for j := 0 to cols-1 do
         begin
            if (ind >= goCount) and (not bOfficer) and (not bDrummer) then
            break(MAIN);
            
            if (ind < goCount) and gFormation[formInd].mask[i, j] then
            begin
               var goHnd : Integer = TSquad(pSquad).Get(ind);
               while (ind < goCount) and (_unit_IsOfficer(goHnd) or _unit_IsDrummer(goHnd)) do
               begin
                  ind := ind+1;
                  if ind < goCount then
                  goHnd := TSquad(pSquad).Get(ind)
                  else
                  goHnd := 0;
               end;
               TSquad(pSquad).arGrid[i, j] := goHnd;
               ind := ind+1;
            end;
            
            if gFormation[formInd].maskOfficers[i, j] then
            begin
               if bOfficer then
               begin
                  TSquad(pSquad).arGrid[i, j] := offHnd;
                  bOfficer := false;
               end
               else
               if bDrummer then
               begin
                  TSquad(pSquad).arGrid[i, j] := drumHnd;
                  bDrummer := false;
               end
            end;
         end;
         if (bSetPosition) then
         begin
            var posx, posz, dirx, dirz : Float;
            _squad_GetCentralPosAndDir(psquad, posx, posz, dirx, dirz);
            {_squad_GetSquadPosition(plHnd, pSquad, posX, posZ);
            var dirX : Float = TSquad(pSquad).fDirX;
            var dirZ : Float = TSquad(pSquad).fDirZ;}
            _player_SetSquadPosition(plHnd, squadInd, posx, posz, dirx, dirz, false, false, gc_obj_order_move_mode_default, false);
         end;
      end;
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
         if parg<>nil then begin
            gWriteSquadPtrList.Clear;
            gWriteSquadPtrList.AddPointer(psquad);
            gIntListWriteSquadAction.Clear;
            TPlayerArgs(parg).fpointer0 := gWriteSquadPtrList;
            TPlayerArgs(parg).fordtyp := gc_playersquad_action_form;
            TPlayerArgs(parg).fbstate := bSetPosition;
            TPlayerArgs(parg).fform := formind;
            TPlayerArgs(parg).fintlst := gIntListWriteSquadAction;
            PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
         end else _misc_ErrorLog('_player_SetSquadFormation: parg = nil');
      end;
   end;
end;

// _player_WriteSquadNew
//
function _player_WriteSquadNew(plhnd, formind, offhnd, drmhnd : Integer; var list : TIntegerList; bSetPosition, bInSquad : Boolean) : Pointer;
begin
   Result := nil;
   var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
   if parg<>nil then begin
      TPlayerArgs(parg).fid := GetPlayerIndexByHandle(plHnd);
      TPlayerArgs(parg).fsid := '';
      TPlayerArgs(parg).find := formind;
      TPlayerArgs(parg).fofficer := offhnd;
      TPlayerArgs(parg).fdrummer := drmhnd;
      TPlayerArgs(parg).fposition := bSetPosition;
      TPlayerArgs(parg).fbstate := bInSquad;
      TPlayerArgs(parg).fintlst := list;
      PlayerExecuteStateByHandle(plhnd, 'WriteSquadNew');
      Result := TPlayerArgs(parg).fpointer0;
   end else ErrorLog('_player_WriteSquadNew : parg = nil');
end;

// _player_CreateSquad
//
function _player_CreateSquad(plHnd, formInd, officerHnd, drummerHnd : Integer; var list : TIntegerList; bSetPosition, bInSquad : Boolean) : Pointer;
begin
   Result := nil;
   var goCount : Integer = list.GetCount;
   if {(goCount > 0) and} ((formInd < 0) or ((officerHnd=0) or (_unit_IsOfficer(officerHnd)) and ((drummerHnd=0) or (_unit_IsDrummer(drummerHnd))))) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      
      var count, id : Integer;
      if formInd >= 0 then
      begin
         var tmpHnd : Integer;
         if (officerHnd<>0) then
         tmpHnd := officerHnd
         else
         tmpHnd := list.Get(0);
         var pObj : Pointer = _unit_GetTObj(tmpHnd);
         if (pObj<>nil) then
         id := TObj(pObj).id;
         count := gFormation[formInd].countUnits;
      end
      else
      begin
         id := 0;
         count := 0;
      end;
      
      var pSquad : Pointer = gPlayer[plInd].squads.AddSetup(count, id, false, true);
      var squadInd : Integer = TSquad(pSquad).fIndex;
      
      if formInd >= 0 then
      begin
         if (officerHnd<>0) then
         _squad_AddUnit(pSquad, officerHnd, bInSquad);
         if (drummerHnd<>0) then
         _squad_AddUnit(pSquad, drummerHnd, bInSquad);
      end;
      
      var i : Integer;
      for i := 0 to goCount-1 do
      begin
         var goHnd : Integer = list.Get(i);
         _squad_AddUnit(pSquad, goHnd, bInSquad);
      end;
      
      TSquad(pSquad).fPlIndex := plInd;
      var grHnd : Integer = _squad_SetupGroup(pSquad);
      _squad_SetGroupCollisionOptions(pSquad);
      
      var x, z, dirX, dirZ : Float;
      if formInd >= 0 then
      _misc_GetUnitsListCentralPosAndDir(list, x, z, dirX, dirZ)
      else
      _squad_GetSquadCentralPosAndDir(pSquad, x, z, dirX, dirZ);
      
      SetGroupStateTargetPositionByHandle(grHnd, x, 0, z);
      GroupSetDirectPosAndDirByHandle(grHnd, x, 0, z, dirX, 0, dirZ);
      
      Result := pSquad;
   end
   else
   begin
      Result := nil;
      ErrorLog('Squad not created');
   end;
end;

// _player_AddUnitsToSquad
//
procedure _player_AddUnitsToSquad(plhnd : Integer; psquad : Pointer; bsetposition : Boolean; var list : TIntegerList);
begin
   var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
   if parg<>nil then begin
      gWriteSquadPtrList.Clear;
      gWriteSquadPtrList.AddPointer(psquad);
      TPlayerArgs(parg).fpointer0 := gWriteSquadPtrList;
      TPlayerArgs(parg).fordtyp := gc_playersquad_action_fill;
      TPlayerArgs(parg).fbstate := bsetposition; // _net_IsClient; //not bsetposition
      TPlayerArgs(parg).fform := 0;
      TPlayerArgs(parg).fintlst := list;
      PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
   end else _misc_ErrorLog('_player_AddUnitsToSquad: parg = nil');
end;

// _player_FillSquad
//
procedure _player_FillSquad(plHnd, squadInd : Integer; bSetPosition, blan : Boolean);
begin
   gTmpIntList.Clear;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var pSquad : Pointer = gPlayer[plInd].squads.Get(squadInd);
   if pSquad <> nil then
   begin
      if _net_IsOffline or ({blan and} _net_IsServer) or ((not blan) and _net_IsClient) then
      begin
         var goSquadHnd : Integer = _unit_GetSquadUnit(pSquad);
         if goSquadHnd <> 0 then
         begin
            var pObj : Pointer = _unit_GetTObj(goSquadHnd);
            var pObjBase : Pointer = gPlayer[plInd].objBase[TObj(pObj).cid][TObj(pObj).id];
            
            var needCount : Integer = TSquad(pSquad).fBaseCount - TSquad(pSquad).GetCount;
            if _squad_GetOfficer(pSquad) <> 0 then
            needCount := needCount+1;
            if _squad_GetDrummer(pSquad) <> 0 then
            needCount := needCount+1;
            
            if needCount > 0 then
            begin
               var posX, posZ, dirX, dirZ : Float;
               _squad_GetCentralPosAndDir(pSquad, posX, posZ, dirX, dirZ);
               gTmpIntListFillSquad.Clear;
               var addCount : Integer = _unit_FindUnitsAndFillList(posX, posZ, gc_gameplay_officersearchradius, TObj(pObj).cid, TObj(pObj).id, plHnd, needCount, -1, True, False, False, 1, gTmpIntListFillSquad);
               if addCount > 0 then
               begin
                  var i : Integer;
                  for i := 0 to addCount-1 do
                  begin
                     var goHnd : Integer = gTmpIntListFillSquad.Get(i);
                     _squad_AddUnit(pSquad, goHnd, false);
                     if (_net_IsOffline) or (_net_IsServer) then
                     gTmpIntList.Add(goHnd);
                  end;
                  _player_SetSquadFormation(plHnd, squadInd, TSquad(pSquad).fFormation, False, blan and ((not _net_IsClient) and (not _net_IsReplay)));
                  
                  if (not _net_IsClient) and (not _net_IsReplay) then
                  if bSetPosition then
                  _player_SetSquadPosition(plHnd, squadInd, posX, posZ, dirX, dirZ, false, false, gc_obj_order_move_mode_default, blan);
               end;
               if (GetPlayerHandleInterfaceIO=plHnd) and (gProfileUserStruct.achs.ach[gc_ach_learn_fillsquadhalfdead10].cur<10) and (addCount>(TSquad(pSquad).fBaseCount div 2)) then
               gProfileUserStruct.achs.ach[gc_ach_learn_fillsquadhalfdead10].cur := Min(gProfileUserStruct.achs.ach[gc_ach_learn_fillsquadhalfdead10].cur+1, 10);
            end;
         end;
      end;
      gIntListWriteSquadAction := gTmpIntList;
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         _player_AddUnitsToSquad(plhnd, psquad, bSetPosition, gIntListWriteSquadAction);
      end;
   end;
end;

// _player_GetSquadByGroup
//
function _player_GetSquadByGroup(plInd, grHnd : Integer) : Pointer;
begin
   Result := nil;
   if grHnd <> 0 then
   begin
      var i : Integer;
      var grName : String = GetGroupNameByHandle(grHnd);
      
      for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = _misc_GetTSquad(plInd, i);
         if TSquad(pSquad).fGroupName = grName then
         begin
            Result := pSquad;
            break;
         end;
      end;
   end;
end;

// _player_WriteOrderObjects
//
function _player_WriteOrderObjects(var list : TIntegerList; const plhnd, ordertype, trghnd: Integer; const clearorders: Boolean): Boolean;
begin
   // network
   if (list.GetCount>0) and (_net_IsOnline or _net_IsRecord) then begin
      var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
      if parg<>nil then begin
         TPlayerArgs(parg).fordtyp:=ordertype;
         TPlayerArgs(parg).ftarget:=trghnd;
         TPlayerArgs(parg).fclrord:=clearorders;
         TPlayerArgs(parg).fintlst:=list;
         PlayerExecuteStateByHandle(plhnd, 'WriteOrder');
         Result:=True;
         end else begin
         Result:=False;
         _misc_ErrorLog('WriteOrderObjects: parg = nil');
      end;
   end
   else
   Result:=False;
end;

// _player_OrderUnitsToBuild
//
procedure _player_OrderUnitsToBuild(var list : TIntegerList; const trgHnd : Integer; const bClearOrders, bRepair, bNetwork : Boolean);
begin
   if (trgHnd<>0) then
   begin
      var count : Integer;
      var i, j, k : Integer;
      
      var trgX, trgZ : Float;
      _unit_GetAlignedPosition(trgHnd, trgX, trgZ);
      
      for i := list.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         begin
            var ordcount : Integer;
            for j:=0 to gc_obj_MaxOrderCount-1 do
            begin
               if (TObj(pobj).orders[j].itype<>gc_obj_order_type_none) then
               ordcount := ordcount+1
               else
               break;
            end;
            var goX : Float = GetGameObjectPositionXByHandle(goHnd);
            var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
            var dist : Float = VectorDistance(goX, 0, goZ, trgX, 0, trgZ);
            dist := dist+(ordcount*100000); // units with less build orders are first prior
            SetGameObjectTagFloatByHandle(goHnd, dist);
         end;
      end;
      
      _misc_SortListByTagFloat(list, 0, list.GetCount-1);
      
      var pTrgObj : Pointer = _unit_GetTObj(trgHnd);
      var cid : Integer = TObj(pTrgObj).cid;
      var id : Integer = TObj(pTrgObj).id;
      
      var bwall : Boolean = (gObjProp[cid][id].bwall) or (gObjProp[cid][id].bgate);
      var variation : Integer = TObj(pTrgObj).wallvariation;
      if (bwall) and (variation=0) then
      bwall := False;
      if (not bwall) then
      begin
         if gCustomObjPoints[cid, id].builderCount = 0 then
         _unit_CalcBuilderPoints(trgHnd, gc_BuilderDist);
      end;
      var bCount : Integer;
      if (bwall) then
      bCount := gCustomBuildPointsWall[variation].builderCount
      else
      bCount := gCustomObjPoints[cid, id].builderCount;
      
      gOrderUnitsList.Clear;
      gOrderUnitsListTmp.Clear;
      gOrderUnitsListTmp := list;
      
      procedure GetBuildPoints(var list : TIntegerList; const bldHnd : Integer);
      begin
         gTmpIntListGetBuildPoints.Clear;
         gTmpIntListGetBuildPoints.AddNulls(bCount);
         
         for i := 0 to list.GetCount-1 do
         begin
            var goHnd : Integer = list.Get(i);
            if (_unit_GetUsage(goHnd)=gc_obj_usage_peasant) then
            begin
               var stX : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
               var stZ : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
               
               var needOrder : Boolean = true;
               for j := 0 to bCount-1 do
               begin
                  var x, z : Float;
                  if (bwall) then
                  begin
                     x := gCustomBuildPointsWall[variation].builderPoints[j].x;
                     z := gCustomBuildPointsWall[variation].builderPoints[j].y;
                  end
                  else
                  begin
                     x := gCustomObjPoints[cid][id].builderPoints[j].x;
                     z := gCustomObjPoints[cid][id].builderPoints[j].y;
                  end;
                  if VectorDistance(stX, 0, stZ, trgX+x, 0, trgZ+z) < GetGameObjectEpsilonDistanceByHandle(goHnd) then
                  begin
                     var pobj : Pointer = _unit_GetTObj(goHnd);
                     if (pobj<>nil) and (TObj(pobj).orders[0].info.trg=bldHnd) then
                     begin
                        if gTmpIntListGetBuildPoints.Get(j) = 0 then
                        gTmpIntListGetBuildPoints.Set(goHnd, j)
                        else
                        begin
                           for k := 0 to bCount-1 do
                           if gTmpIntListGetBuildPoints.Get(k) = 0 then
                           begin
                              gTmpIntListGetBuildPoints.Set(goHnd, k);
                              break;
                           end;
                        end;
                        
                        needOrder := false;
                        break;
                     end;
                  end;
               end;
               
               if (needOrder) and (not _unit_OrderBuild(gTmpIntListGetBuildPoints, goHnd, bldHnd, bRepair, bClearOrders)) and (gObjProp[cid][id].bwall) then
               gOrderUnitsList.Add(goHnd);
            end;
         end;
      end;
      
      GetBuildPoints(gOrderUnitsListTmp, trgHnd);
      
      if (gOrderUnitsList.GetCount>0) and (gObjProp[cid][id].bwall) then
      begin
         var tryCount : Integer;
         var nextWall : Integer = trgHnd;
         while true do
         begin
            tryCount := tryCount+1;
            nextWall := _unit_GetNextWallForWork(gOrderUnitsList, nextWall);
            if nextWall<>0 then
            begin
               gOrderUnitsListTmp := gOrderUnitsList;
               gOrderUnitsList.Clear;
               GetBuildPoints(gOrderUnitsListTmp, nextWall);
            end;
            if (nextWall=0) or (gOrderUnitsList.GetCount=0) or (tryCount>50) then
            break;
         end;
      end;
      
      // network
      if bNetwork then
      begin
         var ordtype : Integer;
         if (not bRepair) then
         ordtype := gc_obj_order_type_build
         else
         ordtype := gc_obj_order_type_repair;
         _player_WriteOrderObjects(list, GetPlayerHandleInterfaceIO, ordtype, trgHnd, bClearOrders);
      end;
   end;
end;

// _player_OrderUnitsToGoInside
//
procedure _player_OrderUnitsToGoInside(plInd : Integer; var list : TIntegerList; var squads : TPtrList; const trgHnd : Integer; const bClearOrders, blan : Boolean);
begin
   var plHnd : Integer = GetPlayerHandleByIndex(plInd);
   var pObjTrg : Pointer = _unit_GetTObj(trgHnd);
   var pObjPropTrg : Pointer = gObjProp[TObj(pObjTrg).cid][TObj(pObjTrg).id];
   var pObjTrgInside : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_inside);
   var inCount : Integer;
   if (pObjTrgInside<>nil) then
   inCount := TIntegerList(pObjTrgInside).GetCount;
   var inside : Integer = inCount+TObj(pObjTrg).insidereserved;
   var maxinside : Integer = TObjProp(pObjPropTrg).peasantabsorber + TObj(pObjTrg).individual.addpeasantabsorber;
   maxInside := maxInside + TObjProp(pObjPropTrg).transport + TObj(pObjTrg).individual.addTransport;
   
   if (maxinside>inside) then
   begin
      procedure ProcessUnit(goHnd, trgHnd : Integer; var aList : TIntegerList);
      begin
         if (goHnd<>0) and (trgHnd<>0) then
         begin
            aList.Add(goHnd);
            var trgX : Float = GetGameObjectPositionXByHandle(trgHnd);
            var trgZ : Float = GetGameObjectPositionZByHandle(trgHnd);
            
            var pObj : Pointer = _unit_GetTObj(goHnd);
            var pObjProp : Pointer = _unit_GetObjProp(goHnd);
            var ordcount : Integer;
            if TObjProp(pObjProp).usage = gc_obj_usage_peasant then
            begin
               if TObj(pobj).orders[0].itype <> gc_obj_order_type_none then
               begin
                  var j : Integer;
                  for j:=0 to gc_obj_MaxOrderCount-1 do
                  begin
                     if (TObj(pobj).orders[j].itype = gc_obj_order_type_gotomine) or (TObj(pobj).orders[j].itype = gc_obj_order_type_gototransport) then
                     ordcount := ordcount+1
                     else
                     break;
                  end;
               end
               else
               ordcount := -1;
            end;
            var goX : Float = GetGameObjectPositionXByHandle(goHnd);
            var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
            var dist : Float = _unit_GetPathDistanceByCoord(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), trgX, trgZ, False);
            dist := dist+(ordcount*100000);
            SetGameObjectTagFloatByHandle(goHnd, dist);
         end
         else
         ErrorLog('_player_OrderUnitsToGoInside : ProcessUnit : goHnd or trgHnd = 0');
      end;
      
      gOrderUnitsList.Clear;
      var i, j : Integer;
      for i := list.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         if _unit_CanGoInside(goHnd, trgHnd) then
         ProcessUnit(goHnd, trgHnd, gOrderUnitsList);
      end;
      
      for i := squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = squads.GetPointer(i);
         for j := TSquad(pSquad).GetCount-1 downto 0 do
         begin
            var goHnd : Integer = TSquad(pSquad).Get(j);
            if _unit_CanGoInside(goHnd, trgHnd) then
            ProcessUnit(goHnd, trgHnd, gOrderUnitsList);
         end;
      end;
      
      _misc_SortListByTagFloat(gOrderUnitsList, 0, gOrderUnitsList.GetCount-1);
      var maxcount : Integer = Min(maxinside-inside, gOrderUnitsList.GetCount);
      if (not _net_IsClient) and (not _net_IsReplay) then
      begin
         for i:=0 to maxcount-1 do
         begin
            var goHnd : Integer = gOrderUnitsList.Get(i);
            var pObj : Pointer = _unit_GetTObj(goHnd);
            if TObj(pObj).squad >= 0 then
            begin
               var psquad : Pointer = gPlayer[TObj(pobj).pl].squads.Get(TObj(pobj).squad);
               if (psquad<>nil) then
               begin
                  gIntegerList.Clear;
                  for i:=TSquad(psquad).GetCount-1 downto 0 do
                  gIntegerList.Add(TSquad(psquad).Get(i));
                  _misc_AddUnitsToSelection(gIntegerList);
               end;
               _misc_DisbandSquad(plHnd, TObj(pObj).squad, true);
            end;
            
            if (bClearOrders) then
            _unit_ClearOrders(goHnd);
            _unit_OrderGoInside(goHnd, trgHnd);
         end;
      end
      else
      gOrderUnitsList.SetCount(maxcount);
      
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         if gOrderUnitsList.GetCount>0 then
         _player_WriteOrderObjects(gOrderUnitsList, plHnd, gc_obj_order_type_gotomine, trgHnd, bClearOrders);
      end;
   end;
end;

// _player_OrderUnitsListToGainRes
//
procedure _player_OrderUnitsListToGainRes(var list : TIntegerList; x, y : Float; restype : Integer; bClearOrders : Boolean);
begin
   if (list.GetCount>0) then
   begin
      var pobj : Pointer = _unit_GetTObj(list.Get(0));
      if (pobj<>nil) then
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(TObj(pobj).pl);
         //if (_misc_FindResourceToExtract(TObj(pobj).pl, x, y, resHnd, resX, resZ, restype)) then
         var resHnd : Integer;
         var i : Integer;
         for i:=1 to 3 do
         begin
            resHnd := _unit_SearchResourceInRadius(0, x, y, gc_obj_res_searchradius*i*2, 1, restype, True);
            if (resHnd<>0) then
            break;
         end;
         if (resHnd<>0) then
         begin
            var resX : Float = GetGameObjectPositionXByHandle(resHnd);
            var resZ : Float = GetGameObjectPositionZByHandle(resHnd);
            //_order_OrderUnitsToGainResource(list, plHnd, resHnd, true);
            for i:=list.GetCount-1 downto 0 do
            begin
               var goHnd : Integer = list.Get(i);
               if (bClearOrders) then
               _unit_ClearOrders(goHnd);
               _unit_OrderGainResource(goHnd, resHnd, resX, resZ);
            end;
            // network
            _player_WriteOrderObjects(list, plHnd, gc_obj_order_type_gainres, resHnd, bClearOrders);
         end;
      end;
   end;
end;

// _player_OrderUnitsToAttack
//
procedure _player_OrderUnitsToAttack(plInd, trgHnd : Integer; var list : TIntegerList; var squads : TPtrList; bClearOrders, bLan : Boolean);
begin
   var i, j : Integer;
   var plHnd : Integer = GetPlayerHandleByIndex(plInd);
   if list.GetCount > 0 then
   begin
      _unit_ListSetStandGround(plHnd, list, false, true);
      _unit_ListSetSearchEnemy(plHnd, list, true, true);
      for i := list.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         var pObj : Pointer = _unit_GetTObj(goHnd);
         if pObj <> nil then
         begin
            TObj(pObj).bstandground := false;
            TObj(pObj).bsearchenemy := true;
            if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) then
            _unit_OrderAttack(goHnd, trgHnd, true, false, true)
            else
            _unit_OrderAttack(goHnd, trgHnd, false, bClearOrders, true);
         end;
      end;
   end;
   
   // TODO: squad logic with network
   if squads.GetCount > 0 then
   begin
      gTmpIntList.Clear;
      for i := squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = squads.GetPointer(i);
         var ind : Integer = TSquad(pSquad).fIndex;
         gTmpIntList.Add(ind);
      end;
      
      _unit_SetSquadsStandGround(plHnd, gTmpIntList, false, true);
      _unit_SetSquadsSearchEnemy(plHnd, gTmpIntList, true, true);
      for i := squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = squads.GetPointer(i);
         if pSquad <> nil then
         begin
            for j := TSquad(pSquad).GetCount-1 downto 0 do
            begin
               var goHnd : Integer = TSquad(pSquad).Get(j);
               var pObj : Pointer = _unit_GetTObj(goHnd);
               if pObj <> nil then
               begin
                  TObj(pObj).bStandGround := false;
                  if gPlayer[plInd].bai and _unit_IsArtillery(goHnd) then
                  _unit_OrderAttack(goHnd, trgHnd, false, false, true)
                  else
                  _unit_OrderAttack(goHnd, trgHnd, false, bClearOrders, true);
               end;
            end;
            TSquad(pSquad).fStandGround := false;
            if (not TSquad(psquad).fStandGround) and TSquad(psquad).fHoldMode then
            TSquad(psquad).fHoldMode := false;
         end;
      end;
   end;
   // network
   if blan and (_net_IsOnline or _net_IsRecord) then begin
      _player_WriteOrderObjects(list, plHnd, gc_obj_order_type_attackobj, trgHnd, bClearOrders);
      var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
      if parg<>nil then begin
         gIntListWriteSquadAction.Clear;
         gIntListWriteSquadAction.Add(trgHnd);
         TPlayerArgs(parg).fpointer0 := squads;
         TPlayerArgs(parg).fordtyp := gc_playersquad_action_attack;
         TPlayerArgs(parg).fbstate := bClearOrders;
         TPlayerArgs(parg).fform := 0;
         TPlayerArgs(parg).fintlst := gIntListWriteSquadAction;
         PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
      end else _misc_ErrorLog('_player_FillSquad: parg = nil');
   end;
end;

// _player_ConstructBuildingList
//
function _player_ConstructBuildingList(const plHnd, cid : Integer; var sid : String; const px, pz : Float; var list : TIntegerList; const bClearOrders, blan : Boolean) : Integer;
begin
   //_misc_ProfilerBegin('_player_ConstructBuildingList');
   Result := 0;
   var id: Integer = _unit_ConvertObjSIDToID(cid, sid);
   var plind: Integer = GetPlayerIndexByHandle(plHnd);
   if (_unit_CanApplyCostByID(cid, id, plind) or (_net_IsClient or _net_IsReplay)) then
   begin
      var trghnd: Integer;
      
      if ((blan    ) and (_net_IsServer))
      or ((not blan) and (_net_IsClient)) or (_net_IsOffline) then begin
         _unit_ApplyCostByID(cid, id, plind);
         gint_unit_parentcid := cid;
         trghnd := CreatePlayerGameObjectHandleByHandle(plHnd, gc_racename_buildings, sid, px, 0, pz);
         if trghnd<>0 then begin
            var pobj: Pointer = _unit_GetTObj(trghnd);
            if pobj<>nil then begin
               TObj(pobj).bbuilt := False;
               TObj(pobj).buildprogress := 0;
               TObj(pobj).hp := 10;
               _unit_ControlBuildProgress(trghnd);
            end else _misc_ErrorLog('_player_ConstructBuildingList: pobj = nil');
            // remove objects placed on object's collision mask
            if (GetCountOfPlayers>=gc_playerind_env) and (gObjProp[cid][id].usage<>gc_obj_usage_mine) then
            begin
               var mapW : Integer = GetMapWidth;
               var mapH : Integer = GetMapHeight;
               var w : Integer = GetGameObjectCollisionMaskWidthByHandle(trghnd);
               var h : Integer = GetGameObjectCollisionMaskHeightByHandle(trghnd);
               var i, j, k : Integer;
               var envPlHnd : Integer = GetPlayerHandleByIndex(gc_playerind_env);
               
               for i := 0 to h-1 do
               for j := 0 to w-1 do
               if GetGameObjectCollisionMaskValueByHandle(trghnd, i, j) then
               begin
                  var testX : Float;
                  if w mod 2 = 1 then
                  testX := px + (j-(w-1)/2)/2
                  else
                  testX := px + (j+1-w/2)/2;
                  
                  var testZ : Float;
                  if h mod 2 = 1 then
                  testZ := pz + (i-(h-1)/2)/2
                  else
                  testZ := pz + (i-h/2)/2;
                  
                  if (testX >= -mapW/2) and (testX <= mapW/2) and (testZ >= -mapH/2) and (testZ <= mapH/2) then
                  begin
                     testX := Floor(2*testX)/2 + 0.25;
                     testZ := Floor(2*testZ)/2 + 0.25;
                     GetGameObjectsInRadius(testX, testZ, 1, false, true, envPlHnd, -1, 0, false, false, false, false, false, false);
                     // _misc_MakeListEnvironmentInRadius not ok, cause we need to clear decor/bushes that dont have TRes struct and not in grid list
                     
                     for k:=GetGameObjectListCount-1 downto 0 do
                     begin
                        var goHnd : Integer = GetGameObjectListByIndex(k);
                        var baseid : Integer = _misc_GetBaseObjID(goHnd);
                        if (baseid=gc_baseid_nil) or (baseid=gc_baseid_res) then
                        begin
                           if (baseid=gc_baseid_res) then
                           begin
                              var pres : Pointer = _misc_GetObjectArgData(goHnd, gc_argenv_res);
                              if (not ((TRes(pres).itype=gc_resource_type_wood) or (TRes(pres).itype=gc_resource_type_none))) then
                              continue;
                           end;
                           var posX : Float = GetGameObjectPositionXByHandle(goHnd);
                           var posZ : Float = GetGameObjectPositionZByHandle(goHnd);
                           
                           if (Abs(posX-testX) < 0.25*3) and (Abs(posZ-testZ) < 0.25*3) then
                           GameObjectDestroyByHandle(goHnd);
                        end;
                     end;
                  end;
               end;
            end;
         end
         else _misc_ErrorLog('_player_ConstructBuildingList: trghnd = 0');
         
         Result := trghnd;
         
         if (not _net_IsClient) and (not _net_IsReplay) then
         _player_OrderUnitsToBuild(list, trgHnd, bClearOrders, false, false);
      end;
      
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
         if parg<>nil then begin
            //TPlayerArgs(parg).fsquad:=sqind;
            TPlayerArgs(parg).fcid:=cid;
            TPlayerArgs(parg).fsid:=sid;
            TPlayerArgs(parg).fposx:=px;
            TPlayerArgs(parg).fposz:=pz;
            TPlayerArgs(parg).fintlst:=list;
            TPlayerArgs(parg).fclrord:=bClearOrders;
            PlayerExecuteStateByHandle(plhnd, 'WriteConstruct');
         end else _misc_ErrorLog('_player_ConstructBuildingList: parg = nil');
      end;
   end;
   //_misc_ProfilerEnd('_player_ConstructBuildingList');
end;

//_player_ConstructGates
//
procedure _player_ConstructGates(const goHnd : Integer);
begin
   gbool_gui_gatefinished := True;
   var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var newSprite : Integer = _misc_GetGateBaseSprite(goHnd);
   if (newSprite<>-1) then
   begin
      var pCluster : Pointer;
      var index : Integer = gWallSystem.GetWallClusterByHandle(plInd, goHnd, pCluster);
      if (pCluster<>nil) then
      begin
         if (TWallCluster(pCluster).Cells.GetCount>index+1) then
         begin
            var p1 : Pointer = TWallCluster(pCluster).Cells.GetPointer(index-1);
            var p2 : Pointer = TWallCluster(pCluster).Cells.GetPointer(index);
            var p3 : Pointer = TWallCluster(pCluster).Cells.GetPointer(index+1);
            if (p1<>nil) and (p2<>nil) and (p3<>nil) then
            begin
               var goX : Float = GetGameObjectPositionXByHandle(goHnd);
               var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
               var sid : String;
               _misc_GetSIDBySprite(TWallCluster(pCluster).wallType, newSprite, TWallCluster(pCluster).cid, sid);
               TWallCell(p1).SetSprite(-1, False);
               TWallCell(p2).SetSprite(newSprite, False);
               TWallCell(p3).SetSprite(-1, False);
               gIntegerList.Clear;
               var trgHnd : Integer = _player_ConstructBuildingList(plHnd, TWallCluster(pCluster).cid, sid, goX, goZ, gIntegerList, true, True);
               var pobj : Pointer = _unit_GetTObj(trgHnd);
               if (pobj<>nil) then
               TObj(pobj).individual.upglevel := TObj(pobj).individual.upglevel+1;
               TWallCell(p2).goHnd := trgHnd;
               if (trgHnd<>0) then
               begin
                  if (plHnd=GetPlayerHandleInterfaceIO) then
                  begin
                     gSelectedObjects.Remove(goHnd);
                     gGuiUpdateHighlights.Remove(goHnd);
                     if (gSelectedObjects.GetCount=0) and (gSelectedSquads.GetCount=0) then
                     begin
                        GameManagerBeginSelection;
                        GameManagerStartSelection('spmRunTime');
                        SetGameObjectPickedByHandle(trgHnd, True);
                        gSelectedObjects.Add(trgHnd);
                        gGuiUpdateHighlights.Add(trgHnd);
                        GameManagerCancelSelection;
                        _misc_UpdateSelection;
                     end;
                  end;
               end;
            end
            else
            ErrorLog('_player_ConstructGates some p=nil');
         end
         else
         ErrorLog('_player_ConstructGates some c<=index');
      end
      else
      ErrorLog('pCluster=nil');
   end;
end;

// _player_ApplyUpgrade
//
procedure _player_ApplyUpgrade(const plHnd, goHnd, cid, upgind : Integer; const blan : Boolean);
begin
   // выполнение апгрейда
   var upgrade : Pointer = gCountry[cid].upgrade[upgind];
   if (upgrade<>nil) and (TCountryUpgrade(upgrade).itype<>gc_upg_type_none) then
   begin
      var i, j, k, id : Integer;
      if (goHnd<>0) then
      begin
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         for i:=gc_obj_MaxOrderCount-1 downto 0 do
         begin
            if (TObj(pobj).orders[i].itype=gc_obj_order_type_performupgrade) and (TObj(pobj).orders[j].info.upgradeid=upgind) then
            _unit_RemoveOrder(goHnd, i);
         end;
         //_unit_RemoveOrder(goHnd, 0);
         // Show log if player InterfaceIO
         if (plHnd=GetPlayerHandleInterfaceIO) then
         begin
            var upgsid : String;
            _country_GetUpgradeSIDByUpgradeID(cid, upgind, upgsid);
            if (not gObjProp[cid][TObj(pobj).id].bwall) then
            begin
               var s1, s2 : String;
               _country_GetUpgradeNameBySID(cid, upgsid, s1);
               _misc_ShowLogMessage(gc_logmessagesid_upgradedone, s1, s2, True);
            end;
            var sid, sndname : String;
            if (gObjProp[cid][TObj(pobj).id].usage=gc_obj_usage_mine) then
            sndname := 'upgmine'
            else
            begin
               sid := gObjProp[cid][TObj(pobj).id].sid;
               sndname := 'upg'+SubStr(sid, StrLength(sid)-2, 3);
            end;
            _misc_PlaySound(gc_snd_lib_interface, sndname);
            gbool_gui_update_unitcontrol := True;
         end;
      end;
      var iarr2 : array [0..gc_upgrade_maxarrparam2count-1] of Integer; // Integer version of String arr2, to store converted sid to id
      for i:=0 to gc_upgrade_maxarrparam2count-1 do
      iarr2[i] := 0;
      var sid : String;
      if (plHnd=0) then
      ErrorLog('_player_ApplyUpgrade plHnd=0');
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var gocount : Integer = GetPlayerGameObjectsCountByHandle(plHnd);
      var value : Float = TCountryUpgrade(upgrade).value;
      
      if (not TCountryUpgrade(upgrade).bindividual) then
      begin
         gPlayer[plInd].upgstate[cid][upgind].done := True;
         gPlayer[plInd].upgstate[cid][upgind].timedone := GetGameTime();
      end;
      gPlayer[plInd].upgstate[cid][upgind].inprogress := False;
      gPlayer[plInd].upgstate[cid][upgind].timestart := 0;
      
      case TCountryUpgrade(upgrade).itype of
         gc_upg_type_lifeperc,
         gc_upg_type_damage,
         gc_upg_type_damageperc,
         gc_upg_type_protection,
         gc_upg_type_shield,
         gc_upg_type_enableunit,
         gc_upg_type_priceperc,
         gc_upg_type_buildtimeperc,
         gc_upg_type_attpauseperc,
         gc_upg_type_attrangeperc,
         gc_upg_type_attdispertionperc,
         gc_upg_type_healing,
         gc_upg_type_fishingperc,
         gc_upg_type_speedperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (TCountryUpgrade(upgrade).sarrparam2[i]<>'') then
            begin
               var ind : Integer = _unit_ConvertObjSIDToID(cid, TCountryUpgrade(upgrade).sarrparam2[i]);
               if ind>0 then
               iarr2[i] := ind;
            end;
         end;
      end;
      
      case TCountryUpgrade(upgrade).itype of
         gc_upg_type_none : begin
         end;
         gc_upg_type_lifeperc : begin
            for i:=0 to gocount-1 do
            begin
               var bSave : Boolean;
               var trgHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
               var pobj2 : Pointer = _unit_GetTObj(trgHnd);
               if (pobj2<>nil) then
               for j:=0 to gc_upgrade_maxarrparam2count-1 do
               begin
                  if (iarr2[j]<>0) and (TObj(pobj2).id=iarr2[j]) and (TObj(pobj2).cid=cid) then
                  TObj(pobj2).hp := Round((TObj(pobj2).hp/gPlayer[TObj(pobj2).pl].objbase[cid][TObj(pobj2).id].maxhp)*(1+value/100)*gPlayer[TObj(pobj2).pl].objbase[cid][TObj(pobj2).id].maxhp);
               end;
            end;
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            gPlayer[plInd].objbase[cid][iarr2[i]].maxhp := Round(gPlayer[plInd].objbase[cid][iarr2[i]].maxhp*(1+value/100));
         end;
         gc_upg_type_damage : begin
            for i:=0 to gc_upgrade_maxarrparam1count-1 do
            begin
               var weaponkind : Integer = TCountryUpgrade(upgrade).iarrparam1[i];
               if (weaponkind<>gc_obj_weapon_kind_none) then
               begin
                  for j:=0 to gc_upgrade_maxarrparam2count-1 do
                  if (iarr2[j]<>0) then
                  begin
                     for k:=0 to gc_obj_MaxWeapon-1 do
                     if (gObjProp[cid][iarr2[j]].weapon[k].kind=weaponkind) then
                     begin
                        gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagestatic := gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagestatic + round(value);
                        gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damage := floor((gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damageinit + gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagestatic) * (1 + gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagepercent / 100));
                     end;
                  end;
               end;
            end;
         end;
         gc_upg_type_damageperc : begin
            for i:=0 to gc_upgrade_maxarrparam1count-1 do
            begin
               var weaponkind : Integer = TCountryUpgrade(upgrade).iarrparam1[i];
               if (weaponkind<>gc_obj_weapon_kind_none) then
               begin
                  for j:=0 to gc_upgrade_maxarrparam2count-1 do
                  if (iarr2[j]<>0) then
                  begin
                     for k:=0 to gc_obj_MaxWeapon-1 do
                     if (gObjProp[cid][iarr2[j]].weapon[k].kind=weaponkind) then
                     begin
                        gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagepercent := gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagepercent+round(value);
                        gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damage := floor((gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damageinit+gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagestatic)*(1+gPlayer[plInd].objbase[cid][iarr2[j]].weapon[k].damagepercent/100));
                     end;
                  end;
               end;
            end;
         end;
         gc_upg_type_protection : begin
            for i:=0 to gc_upgrade_maxarrparam1count-1 do
            begin
               var kind : Integer = TCountryUpgrade(upgrade).iarrparam1[i];
               if (kind<>gc_obj_weapon_kind_none) then
               begin
                  for j:=0 to gc_upgrade_maxarrparam2count-1 do
                  if (iarr2[j]<>0) then
                  gPlayer[plInd].objbase[cid][iarr2[j]].protection[kind] := gPlayer[plInd].objbase[cid][iarr2[j]].protection[kind]+round(value);
               end;
            end;
         end;
         gc_upg_type_shield : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            gPlayer[plInd].objbase[cid][iarr2[i]].shield := gPlayer[plInd].objbase[cid][iarr2[i]].shield+round(value);
         end;
         gc_upg_type_enableunit : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (TCountryUpgrade(upgrade).sarrparam2[i]<>'') then
            _country_AddEnabled(gCountry[cid], TCountryUpgrade(upgrade).sarrparam2[i])
         end;
         gc_upg_type_effectfood : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_food] := gPlayer[plInd].resefficiency[cid][gc_resource_type_food]+round(value);
         end;
         gc_upg_type_effectfoodperc : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_food] := Round(gPlayer[plInd].resefficiency[cid][gc_resource_type_food]+value);
         end;
         gc_upg_type_effectwood : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_wood] := gPlayer[plInd].resefficiency[cid][gc_resource_type_wood]+round(value);
         end;
         gc_upg_type_effectwoodperc : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_wood] := Round(gPlayer[plInd].resefficiency[cid][gc_resource_type_wood]+value);
         end;
         gc_upg_type_effectstone : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_stone] := gPlayer[plInd].resefficiency[cid][gc_resource_type_stone]+round(value);
         end;
         gc_upg_type_effectstoneperc : begin
            gPlayer[plInd].resefficiency[cid][gc_resource_type_stone] := Round(gPlayer[plInd].resefficiency[cid][gc_resource_type_stone]+value);
         end;
         gc_upg_type_fieldlifeperc : begin
            gPlayer[plInd].fieldlife := Round(gPlayer[plInd].fieldlife+value);
         end;
         gc_upg_type_priceperc : begin // first values in sarr2 used to store unit sid. last string values is StrToInt that stores price change value for each resource starting from food (wood, stone, gold, iron, coal)
            // unique upgrade, when we store in one arr sids and effect values
            for i:=0 to (gc_upgrade_maxarrparam2count-gc_ResCount)-1 do
            if (iarr2[i]<>0) then
            begin
               for j:=1 to gc_ResCount-1 do
               begin
                  var id : Integer = iarr2[i];
                  gPlayer[plInd].objbase[cid][id].price[j] := Round(gPlayer[plInd].objbase[cid][id].price[j]*(1+StrToInt(TCountryUpgrade(upgrade).sarrparam2[gc_upgrade_maxarrparam2count-gc_ResCount-1+j])/100));
               end;
            end;
         end;
         gc_upg_type_buildtimeperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            gPlayer[plInd].objbase[cid][iarr2[i]].buildtime := gPlayer[plInd].objbase[cid][iarr2[i]].buildtime*(1+value/(100*100000));
         end;
         gc_upg_type_attpauseperc : begin
            var weaponkind : Integer = TCountryUpgrade(upgrade).iarrparam1[0];
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            begin
               for j:=0 to gc_obj_MaxWeapon-1 do
               if (gObjProp[cid][iarr2[i]].weapon[j].kind=weaponkind) then
               gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].pause := Round(gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].pause*(1+value/100));
            end;
         end;
         gc_upg_type_attrangeperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            begin
               for j:=0 to gc_obj_MaxWeapon-1 do
               if (gObjProp[cid][iarr2[i]].weapon[j].enabled) then
               gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].radiusmax := gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].radiusmax*(1+value/100);
            end;
         end;
         gc_upg_type_attdispertionperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            begin
               for j:=0 to gc_obj_MaxWeapon-1 do
               if (gObjProp[cid][iarr2[i]].weapon[j].enabled) then
               gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].dispertion := gPlayer[plInd].objbase[cid][iarr2[i]].weapon[j].dispertion*(1+value/100);
            end;
         end;
         gc_upg_type_healing : begin
            for i:=0 to gocount-1 do
            begin
               var bSave : Boolean;
               var trgHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
               var pobj2 : Pointer = _unit_GetTObj(trgHnd);
               if (pobj2<>nil) then
               for j:=0 to gc_upgrade_maxarrparam2count-1 do
               begin
                  var maxhp : Integer = gPlayer[TObj(pobj2).pl].objbase[cid][TObj(pobj2).id].maxhp;
                  if (iarr2[j]<>0) and (TObj(pobj2).cid=cid) and (TObj(pobj2).id=iarr2[j]) and (TObj(pobj2).hp<maxhp) then
                  begin
                     TObj(pobj2).hp := Round(TObj(pobj2).hp+maxhp*(value/100));
                     if (TObj(pobj2).hp>maxhp) then
                     TObj(pobj2).hp := maxhp;
                  end;
               end;
            end;
         end;
         gc_upg_type_fishingperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            begin
               gPlayer[plInd].objbase[cid][iarr2[i]].fishingmax := Round(gPlayer[plInd].objbase[cid][iarr2[i]].fishingmax*(1+value/100));
               gPlayer[plInd].objbase[cid][iarr2[i]].fishingspeed := Round(gPlayer[plInd].objbase[cid][iarr2[i]].fishingspeed*(1+value/100));
            end;
         end;
         gc_upg_type_geology : begin
            gPlayer[plInd].bgeology := True;
            if (plHnd=GetPlayerHandleInterfaceIO) then
            _misc_UpdateMiniMapPrimitives(True);
         end;
         gc_upg_type_balloon : begin
            gPlayer[plInd].bballoon := True;
            if (plHnd=GetPlayerHandleInterfaceIO) then
            _player_UpdateFOW(plInd)
            else
            if (GetPlayerHandleInterfaceIO<>0) then // on game generation, interfaceio is not set, and GetPlayerIndexInterfaceIO lead to crash
            begin
               var myPlInd : Integer = GetPlayerIndexInterfaceIO;
               if ((gPlayer[plInd].myplmask and gPlayer[myPlInd].enemyplmask)=0) then
               begin
                  gPlayer[myPlInd].bballoon := True;
                  _player_UpdateFOW(myPlInd);
               end;
            end;
            if (goHnd<>0) and (not _net_IsReplay) and (not _net_IsClient) then
            _misc_CreateBalloon(goHnd);
         end;
         gc_upg_type_speedperc : begin
            for i:=0 to gc_upgrade_maxarrparam2count-1 do
            if (iarr2[i]<>0) then
            gPlayer[plInd].objbase[cid][iarr2[i]].speed := gPlayer[plInd].objbase[cid][iarr2[i]].speed*(1+value/100);
         end;
         gc_upg_type_single_inside : begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) then
            begin
               TObj(pobj).individual.benabled := True;
               TObj(pobj).individual.upglevel := TObj(pobj).individual.upglevel+1;
               TObj(pobj).individual.addtransport := TObj(pobj).individual.addtransport+round(value);
            end;
         end;
         gc_upg_type_single_inside_mine : begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) then
            begin
               TObj(pobj).individual.benabled := True;
               TObj(pobj).individual.upglevel := TObj(pobj).individual.upglevel+1;
               TObj(pobj).individual.addpeasantabsorber := TObj(pobj).individual.addpeasantabsorber+round(value);
            end;
         end;
         gc_upg_type_single_attpauseperc : begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) then
            begin
               TObj(pobj).individual.benabled := True;
               TObj(pobj).individual.upglevel := TObj(pobj).individual.upglevel+1;
               TObj(pobj).individual.attackrate := TObj(pobj).individual.attackrate+(value/100);
            end;
         end;
         gc_upg_type_single_buildgate : begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
            begin
               TObj(pobj).individual.benabled := True;
               TObj(pobj).individual.upglevel := TObj(pobj).individual.upglevel+1;
               _player_ConstructGates(goHnd);
            end;
         end;
      end;
      
      // Process links when upgrade enables other upgrade
      for i:=0 to gc_country_maxupgradecount-1 do
      begin
         var upglink1 : String = gCountry[cid].upgradelinks[i][0];
         if (upglink1='') then
         break
         else
         if (upglink1=TCountryUpgrade(upgrade).id) then
         begin
            var upglink2 : String = gCountry[cid].upgradelinks[i][1];
            var upglinkind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, upglink2, False);
            gPlayer[plInd].upgstate[cid][upglinkind].enabled := True;
            gPlayer[plInd].upgstate[cid][upglinkind].done := False;
            gPlayer[plInd].upgstate[cid][upglinkind].timedone := 0;
         end;
      end;
      
      if (plHnd=GetPlayerHandleInterfaceIO) then
      begin
         var packedupg : Integer = cid*gc_country_maxupgradecount+upgind;
         gAchLastUpgrades.Add(packedupg);
      end;
      
      // network
      if blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr); if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(parg);
         if parg<>nil then begin
            TPlayerArgs(parg).fplayer:=plhnd;
            TPlayerArgs(parg).fhandle:=gohnd;
            TPlayerArgs(parg).fcid:=cid;
            TPlayerArgs(parg).find:=upgind;
            PlayerExecuteStateByHandle(plhnd, 'WriteApply');
         end else _misc_ErrorLog('_player_ApplyUpgrade: parg = nil');
      end;
   end
   else
   ErrorLog('_player_ApplyUpgrade : upgrade.itype=gc_upg_type_none');
end;

// _player_GetTopZoneTrackPointsDanger
//
function _player_GetTopZoneTrackPointsDanger(plInd, zone : Integer) : Float;
begin
   //_misc_ProfilerBegin('_player_GetTopZoneTrackPointsDanger');
   Result := 0;
   var i, j : Integer;
   var tpCount : Integer = TopologyGetZoneTrackPointsCountByIndex(zone);
   
   for i := 0 to tpCount-1 do
   begin
      var x, z : Float;
      TopologyGetZoneTrackPointCoordsByIndex(zone, i, x, z);
      var zInd : Integer = TopologyGetZoneIndex(x, z);
      
      if zInd >= 0 then
      begin
         var pBuffer : Pointer = TopologyGetZoneBufferByIndex(zInd);
         for j := 0 to gc_MaxPlayerCount-1 do
         if gPlayer[j].myPlMask and gPlayer[plInd].enemyPlMask <> 0 then
         Result := Result + TTopZone(pBuffer).danger[j];
      end;
   end;
   //_misc_ProfilerEnd('_player_GetTopZoneTrackPointsDanger');
end;

// _player_GetTopologyDangerPathFromZone
//
function _player_GetTopologyDangerPathFromZone(const plHnd, stZone : Integer; const onlyNearest : Boolean; var pathDanger : Float) : Integer;
begin
   pathDanger := 0;
   Result := -1;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   
   if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
   begin
      var i, j : Integer;
      var myTeam : Integer = gPlayer[plInd].team;
      var plIndices : array [0..gc_MaxPlayerCount-1] of Integer;
      var plCount : Integer;
      for i := 0 to gc_MaxPlayerCount-1 do
      if (i <> plInd) and gPlayer[i].bExists and (gPlayer[i].team <> myTeam) then
      begin
         plIndices[plCount] := i;
         plCount := plCount+1;
      end;
      
      if plCount > 0 then
      begin
         for i := TopologyGetZonesCount-1 downto 0 do
         begin
            var pBuffer : Pointer = TopologyGetZoneBufferByIndex(i);
            var weight : Float = gc_top_ZoneWeight;
            
            for j := 0 to plCount-1 do
            weight := weight + TTopZone(pBuffer).danger[plIndices[j]];
            
            TopologySetZoneWeightByIndex(i, weight);
         end;
         
         Result := TopologyGetWeightPathFromZone(stZone, onlyNearest);
         if Result >= 0 then
         pathDanger := _player_GetTopZoneTrackPointsDanger(plInd, Result);
      end;
   end;
end;

// _player_GetTopologyDangerPath
//
function _player_GetTopologyDangerPath(const plHnd, stZone, endZone : Integer; var pathDanger : Float) : Boolean;
begin
   pathDanger := 0;
   Result := false;
   
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
   begin
      var i : Integer;
      for i := TopologyGetZonesCount-1 downto 0 do
      TopologySetZoneNeedPathByIndex(i, (i=endZone));
      
      _player_GetTopologyDangerPathFromZone(plHnd, stZone, true, pathDanger);
      var tpCount : Integer = TopologyGetZoneTrackPointsCountByIndex(endZone);
      Result := (tpCount > 0);
   end;
end;

// _player_GetOrCreateEnemyInfo
//
function _player_GetOrCreateEnemyInfo(plInd : Integer) : Pointer;
begin
   Result := nil;
   if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
   begin
      Result := _misc_GetPlayerEnemyInfo(plInd);
      if Result = nil then
      begin
         Result := gEnemyInfoList.Add;
         TEnemyInfo(Result).team := gPlayer[plInd].team;
         TEnemyInfo(Result).enemyMask := gPlayer[plInd].enemyPlMask;
      end;
   end;
end;

// _player_GetTeamPlayersCount
//
function _player_GetTeamPlayersCount(team : Integer) : Integer;
begin
   var i : Integer;
   Result := 0;
   for i := 0 to gc_MaxPlayerCount-1 do
   if gPlayer[i].team = team then
   Result := Result+1;
end;

// _player_SetupTeams
//
procedure _player_SetupTeams(mapInfo : Boolean);
begin
   function GetTeam(ind : Integer) : Integer;
   begin
      if mapInfo then
      Result := gMap.players[ind].team
      else
      Result := gPlayer[ind].team;
   end;
   
   gEnemyInfoList.Clear;
   var curTeam : Integer = 1;
   var i, j : Integer;
   
   for i := 0 to gc_MaxPlayerCount-1 do
   if gPlayer[i].team = 0 then
   begin
      gPlayer[i].team := curTeam;
      var pEnemyInfo : Pointer = gEnemyInfoList.Add;
      TEnemyInfo(pEnemyInfo).team := curTeam;
      TEnemyInfo(pEnemyInfo).enemyMask := gPlayer[i].enemyPlMask;
      
      var myTeam : Integer = GetTeam(i);
      if myTeam > 0 then
      begin
         for j := i+1 to gc_MaxPlayerCount-1 do
         if GetTeam(j) = myTeam then
         gPlayer[j].team := curTeam;
      end;
      
      curTeam := curTeam+1;
   end;
end;

// _player_MakeAlly
//
procedure _player_MakeAlly(plInd1, plInd2 : Integer);
begin
   if (plInd1 <> plInd2) and (plInd1 >= 0) and (plInd1 < gc_MaxPlayerCount) and (plInd2 >= 0) and (plInd2 < gc_MaxPlayerCount) then
   begin
      var team1 : Integer = gPlayer[plInd1].team;
      var team2 : Integer = gPlayer[plInd2].team;
      if team1 <> team2 then
      begin
         var i : Integer;
         var enMask : Integer;
         for i := 0 to gc_MaxPlayerCount-1 do
         begin
            if (gPlayer[i].team = team1) or (gPlayer[i].team = team2) then
            gPlayer[i].team := team1
            else
            enMask := enMask or (1 shl i);
         end;
         
         for i := 0 to gc_MaxPlayerCount-1 do
         if gPlayer[i].team = team1 then
         gPlayer[i].enemyPlMask := enMask;
         
         var pEnemyInfo : Pointer = _misc_GetTeamEnemyInfo(team2);
         if pEnemyInfo <> nil then
         TEnemyInfo(pEnemyInfo).Reset;
         
         pEnemyInfo := _misc_GetTeamEnemyInfo(team1);
         if pEnemyInfo <> nil then
         begin
            TEnemyInfo(pEnemyInfo).enemyMask := enMask;
            TEnemyInfo(pEnemyInfo).changed := true;
         end;
      end;
   end;
end;

// _player_MakeEnemyForAll
//
procedure _player_MakeEnemyForAll(plInd : Integer);
begin
   var myTeam : Integer = gPlayer[plInd].team;
   if _player_GetTeamPlayersCount(myTeam) > 1 then
   begin
      var pEnInfo : Pointer = _misc_GetPlayerEnemyInfo(plInd);
      var team : Integer;
      var moved : Boolean;
      var newEnMask : Integer = 255 xor (1 shl plInd);
      
      for team := 1 to gc_MaxPlayerCount do
      if _player_GetTeamPlayersCount(team) = 0 then
      begin
         gPlayer[plInd].team := team;
         var pNewEnInfo : Pointer = _player_GetOrCreateEnemyInfo(plInd);
         if pNewEnInfo <> nil then
         begin
            TEnemyInfo(pNewEnInfo).enemyMask := newEnMask;
            TEnemyInfo(pNewEnInfo).changed := true;
         end;
         
         moved := true;
         break;
      end;
      
      if moved then
      begin
         var enMask : Integer = gPlayer[plInd].enemyPlMask or (1 shl plInd);
         gPlayer[plInd].enemyPlMask := newEnMask;
         
         if pEnInfo <> nil then
         begin
            TEnemyInfo(pEnInfo).enemyMask := enMask;
            TEnemyInfo(pEnInfo).changed := true;
         end;
         
         var i : Integer;
         for i := 0 to gc_MaxPlayerCount-1 do
         if gPlayer[i].team = myTeam then
         gPlayer[i].enemyPlMask := enMask;
      end
      else
      ErrorLog('Can''t remove player from team! No free teams found!');
   end;
end;

// _player_MakeEnemy
//
procedure _player_MakeEnemy(plInd1, plInd2 : Integer);
begin
   if (plInd1 <> plInd2) and (plInd1 >= 0) and (plInd1 < gc_MaxPlayerCount) and (plInd2 >= 0) and (plInd2 < gc_MaxPlayerCount) and
   (gPlayer[plInd1].team = gPlayer[plInd2].team) then
   begin
      _player_MakeEnemyForAll(plInd1);
      _player_MakeEnemyForAll(plInd2);
   end;
end;

// _player_GetTopologyZoneDanger
//
function _player_GetTopologyZoneDanger(const plHnd, tpZone : Integer) : Float;
begin
   //_misc_ProfilerBegin('_player_GetTopologyZoneDanger');
   Result := 0;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var pBuffer : Pointer = TopologyGetZoneBufferByIndex(tpZone);
   var weight : Float = gc_top_ZoneWeight;
   var i : Integer;
   if pBuffer <> nil then
   for i := 0 to gc_MaxPlayerCount-1 do
   if gPlayer[i].myPlMask and gPlayer[plInd].enemyPlMask <> 0 then
   Result := Result + TTopZone(pBuffer).danger[i];
   //_misc_ProfilerEnd('_player_GetTopologyZoneDanger');
end;

// _player_SetUnitsPosition
//
procedure _player_SetUnitsPosition(plInd, mode : Integer; var list : TIntegerList; x, z, dirX, dirZ : Float);
begin
   if list.GetCount > 0 then
   begin
      var plHnd : Integer = GetPlayerHandleByIndex(plInd);
      ClearPlayerStretchBrushGroups(plHnd);
      var i, j : Integer;
      for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = gPlayer[plInd].squads.Get(i);
         TSquad(pSquad).fTag := 0;
      end;
      
      for i := GetPlayerCountOfGroupsByHandle(plHnd)-1 downto 0 do
      begin
         var grHnd : Integer = GetGRHandleByPlayerHandleByIndex(plHnd, i);
         _player_ClearGroup(grHnd);
      end;
      
      //var grSelHnd : Integer = GetPlayerGrHandleByHandle(plHnd, gc_group_selected); // ai player can have no selection group
      var grSelHnd : Integer = _misc_GetOrCreateGroupByName(plHnd, gc_group_selected);
      
      for i := list.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         var pObj : Pointer = _unit_GetTObj(goHnd);
         if (pObj<>nil) and ((GetGameObjectStatesTagByHandle(goHnd) and gc_statetag_essential_none)<>0) then
         begin
            var squad : Integer = TObj(pObj).squad;
            if squad<0 then
            GroupAddGameObjectByHandle(grSelHnd, goHnd)
            else
            begin
               var pSquad : Pointer = gPlayer[plInd].squads.Get(squad);
               if TSquad(pSquad).fTag = 0 then
               begin
                  var grName : String = TSquad(pSquad).fGroupName;
                  var grHnd : Integer = GetPlayerGrHandleByHandle(plHnd, grName);
                  AddPlayerStretchBrushGroupHandle(grHnd);
                  
                  for j := TSquad(pSquad).GetCount-1 downto 0 do
                  begin
                     var goHnd1 : Integer = TSquad(pSquad).Get(j);
                     GroupAddGameObjectByHandle(grHnd, goHnd1);
                  end;
                  
                  TSquad(pSquad).fTag := 1;
               end;
            end;
         end;
      end;
      
      if GetGroupCountGameObjectsByHandle(grSelHnd) > 0 then
      begin
         _group_Initialize(grSelHnd, nil);
         AddPlayerStretchBrushGroupHandle(grSelHnd);
      end;
      
      var brushCount : Integer = GetPlayerStretchBrushGroupsCount(plHnd);
      if brushCount > 0 then
      begin
         SLogicFrmRotateSetStaticDir(False);
         ProcessPlayerSLogicFrmRotate(plHnd, x, z, dirX, dirZ, false);
         if SLogicFrmRotateGetStretchResult > 0 then
         begin
            ApplyPlayerStretchBrush(plHnd);
            
            var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
            if parg <> nil then
            for i := 0 to brushCount-1 do
            begin
               var grHnd : Integer = GetPlayerStretchBrushGroupHandleByIndex(plHnd, i);
               var count : Integer = GetGroupCountGameObjectsByHandle(grHnd);
               var pSquad : Pointer = _player_GetSquadByGroup(plInd, grHnd);
               var sqInd : Integer = -1;
               if pSquad <> nil then
               sqInd := TSquad(pSquad).fIndex;
               
               var grX : Float = GetPlayerStretchBrushGroupTargetPositionX(grHnd);
               var grY : Float = GetPlayerStretchBrushGroupTargetPositionY(grHnd);
               var dirX, dirY, dirZ : Float;
               dirZ := -1;
               var angle : Float = GetPlayerStretchBrushGroupTargetAngle(grHnd);
               VectorRotateY(dirX, dirY, dirZ, angle);
               
               TPlayerArgs(parg).fposx := grX;
               TPlayerArgs(parg).fposz := grY;
               TPlayerArgs(parg).fvisposx := grX;
               TPlayerArgs(parg).fvisposz := grY;
               TPlayerArgs(parg).fdirx := dirX;
               TPlayerArgs(parg).fdirz := dirZ;
               TPlayerArgs(parg).faddord := false;
               TPlayerArgs(parg).fdofirst := false;
               TPlayerArgs(parg).frebuild := (grHnd = grSelHnd);
               TPlayerArgs(parg).fcenter := True; // gec set to true. if you need somewhere false, add bCenter as function parameter.
               TPlayerArgs(parg).fmode := mode;
               TPlayerArgs(parg).fgroup := grHnd;
               TPlayerArgs(parg).fsquad := sqInd;
               PlayerExecuteStateByHandle(plHnd, 'WriteMove');
            end;
         end;
      end;
   end;
end;

// _player_OrderUnitsToGuard
//
procedure _player_OrderUnitsToGuard(plhnd : Integer; var list : TIntegerList; trgHnd : Integer; bClearOrders : Boolean);
begin
   var trgX : Float = GetGameObjectPositionXByHandle(trgHnd);
   var trgZ : Float = GetGameObjectPositionZByHandle(trgHnd);
   
   gOrderUnitsList.Clear;
   var i, ind : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      var goX : Float = GetGameObjectPositionXByHandle(goHnd);
      var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
      var dist : Float = VectorDistance(goX, 0, goZ, trgX, 0, trgZ);
      SetGameObjectTagFloatByHandle(goHnd, dist);
   end;
   _misc_SortListByTagFloat(list, 0, list.GetCount-1); // take 3 nearest guards
   for i:=0 to list.GetCount-1 do
   begin
      var goHnd : Integer = list.Get(i);
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) {and (goHnd<>trgHnd)} then
      begin
         var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
         if (TObj(pobj).bbuilt) and (not TObjProp(pobjprop).bbuilding) and (not TObjProp(pobjprop).bcapture) then
         begin
            TObj(pobj).bstandground := False;
            if (bClearOrders) then
            _unit_ClearOrders(goHnd);
            _unit_OrderGuard(goHnd, trgHnd);
            gOrderUnitsList.Add(goHnd);
            ind := ind+1;
            if ind>=gc_MaxGuardOrderCount then
            break;
         end;
      end;
   end;
   for i:=gOrderUnitsList.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = gOrderUnitsList.Get(i);
      SetGameObjectPickedByHandle(goHnd, False);
      gSelectedObjects.Remove(goHnd);
      gGuiUpdateHighlights.Add(goHnd);
   end;
   //network
   _player_WriteOrderObjects(gOrderUnitsList, plHnd, gc_obj_order_type_guard, trgHnd, bClearOrders);
end;

//TODO : check this
procedure _player_OrderUnitsToAttackPoint(plhnd : Integer; var list : TIntegerList; trgx, trgz : Float; bClearOrders : Boolean);
begin
   var i : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) then
      begin
         if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bartprepare) then
         begin
            TObj(pobj).bstandground := False;
            TObj(pobj).bsearchenemy := True;
            if (bClearOrders) then
            _unit_ClearOrders(goHnd);
            _unit_OrderAttackPoint(goHnd, trgx, trgz, False, bClearOrders);
         end;
      end;
      // network
      if _net_IsOnline or _net_IsRecord then begin
         if list.GetCount>0 then begin
            var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
            if parg<>nil then begin
               TPlayerArgs(parg).fordtyp:=gc_obj_order_type_attackpoint;
               TPlayerArgs(parg).ftarget:=0;
               TPlayerArgs(parg).fclrord:=bClearOrders;
               TPlayerArgs(parg).fintlst:=list;
               TPlayerArgs(parg).fposx:=trgx;
               TPlayerArgs(parg).fposz:=trgz;
               PlayerExecuteStateByHandle(plhnd, 'WriteOrder');
            end else _misc_ErrorLog('parg = nil');
         end;
      end;
   end;
end;

// _player_StandGroupToPosition
//
procedure _player_StandGroupToPosition(plHnd, grHnd : Integer; pSquad : Pointer; x, z, dirX, dirZ : Float);
begin
   _group_Initialize(grHnd, pSquad);
   
   ClearPlayerStretchBrushGroups(plHnd);
   AddPlayerStretchBrushGroupHandle(grHnd);
   SLogicFrmRotateSetStaticDir(False);
   ProcessPlayerSLogicFrmRotate(plHnd, x, z, dirX, dirZ, true);
   var res : Integer = SLogicFrmRotateGetStretchResult;
   if res > 0 then
   begin
      var grX : Float = GetPlayerStretchBrushGroupTargetPositionX(grHnd);
      var grY : Float = GetPlayerStretchBrushGroupTargetPositionY(grHnd);
      var angle : Float = GetPlayerStretchBrushGroupTargetAngle(grHnd);
      var dirX, dirY, dirZ : Float;
      dirZ := -1;
      VectorRotateY(dirX, dirY, dirZ, angle);
      
      _player_SetGroupPosition(plHnd, grHnd, -1, grX, grY, grX, grY, dirX, dirZ, false, false, false, false, gc_obj_order_move_mode_default);
      var i : Integer;
      for i := GetGroupCountGameObjectsByHandle(grHnd)-1 downto 0 do
      begin
         var goHnd : Integer = GetGroupGOHandleByGOIndexByHandle(grHnd, i);
         var pObj : Pointer = _unit_GetTObj(goHnd);
         if TObj(pObj).orders[0].iType = gc_obj_order_type_move then
         begin
            var x : Float = TObj(pObj).orders[0].info.x;
            var z : Float = TObj(pObj).orders[0].info.y;
            var y : Float;
            if (not RayCastWater(x, z, y)) then // is water, then y is water height
            y := RayCastHeight(x, z);
            SetGameObjectPositionByHandle(goHnd, x, y, z);
            
            var goDirX : Float = GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
            var goDirZ : Float = GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
            var turnAngle : Float = VectorAngle(dirX, 0, dirZ, goDirX, 0, goDirZ);
            var x1, y1, z1 : Float;
            VectorCross(dirX, 0, dirZ, goDirX, 0, goDirZ, x1, y1, z1);
            if y1 < 0 then
            turnAngle := -turnAngle;
            
            GameObjectVirtualTurnByHandle(goHnd, turnAngle);
            
            _unit_ClearOrders(goHnd);
         end;
      end;
   end;
end;

// _player_StandSquadToPosition
//
procedure _player_StandSquadToPosition(plHnd : Integer; pSquad : Pointer; x, z, dirx, dirz : Float);
begin
   if (pSquad <> nil) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      if TSquad(pSquad).fPlIndex = plInd then
      begin
         var grName : String = TSquad(pSquad).fGroupName;
         var grHnd : Integer = GetPlayerGrHandleByHandle(plHnd, grName);
         
         var count : Integer = TSquad(pSquad).GetCount;
         var grX, grZ : Float;
         var i : Integer;
         for i := 0 to count-1 do
         begin
            var goHnd : Integer = TSquad(pSquad).Get(i);
            GroupAddGameObjectByHandle(grHnd, goHnd);
            grX := grX + GetGameObjectPositionXByHandle(goHnd);
            grZ := grZ + GetGameObjectPositionZByHandle(goHnd);
         end;
         
         grX := grX / count;
         grZ := grZ / count;
         
         _player_StandGroupToPosition(plHnd, grHnd, pSquad, x, z, dirX, dirZ);
      end;
   end;
end;

// _player_StandUnitsToPosition
//
procedure _player_StandUnitsToPosition(plHnd : Integer; var list : TIntegerList; x, z, dirx, dirz : Float);
begin
   if list.GetCount > 0 then
   begin
      var grHnd : Integer = _misc_GetOrCreateGroupByName(plHnd, gc_group_selected);
      GroupClearGameObjectsByHandle(grHnd);
      var count : Integer = list.GetCount;
      var grX, grZ : Float;
      var i : Integer;
      for i := 0 to count-1 do
      begin
         var goHnd : Integer = list.Get(i);
         GroupAddGameObjectByHandle(grHnd, goHnd);
         grX := grX + GetGameObjectPositionXByHandle(goHnd);
         grZ := grZ + GetGameObjectPositionZByHandle(goHnd);
      end;
      
      grX := grX / count;
      grZ := grZ / count;
      
      _player_StandGroupToPosition(plHnd, grHnd, nil, x, z, dirX, dirZ);
   end;
end;

// _player_CalcSquadsMoveCount
//
procedure _player_CalcSquadsMoveCount(plInd : Integer);
begin
   var i, j : Integer;
   for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
   begin
      var pSquad : Pointer = gPlayer[plInd].squads.Get(i);
      TSquad(pSquad).fMoveCount := 0;
      for j := TSquad(pSquad).GetCount-1 downto 0 do
      begin
         var goHnd : Integer = TSquad(pSquad).Get(j);
         if ((GetGameObjectStatesTagByHandle(goHnd) and gc_statetag_move_walk)<>0) then
         TSquad(pSquad).fMoveCount := TSquad(pSquad).fMoveCount + 1
      end;
   end;
end;

// _player_ApplyMapSettings
//
procedure _player_ApplyMapSettings();
begin
   // gMap settings Apply
   var pl, cid, id : Integer;
   var pobjbase, pobjprop : Pointer;
   for cid:=0 to gc_MaxCountryCount-1 do
   begin
      var csid : String;
      _country_GetSIDByID(cid, csid);
      if (gMap.settings.additional.century18<>gc_mapsettings_century18_default) then
      begin
         var century18 : String = csid+'cen'+'.1';
         var ind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, century18, False);
         if (ind>0) then
         case gMap.settings.additional.century18 of
            gc_mapsettings_century18_no : begin
               var pl : Integer;
               for pl:=0 to gc_MaxPlayerCount-1 do
               gPlayer[pl].upgstate[cid][ind].enabled := False;
            end;
            gc_mapsettings_century18_with : begin
               for pl:=0 to gc_MaxPlayerCount-1 do
               begin
                  var plHnd : Integer = GetPlayerHandleByIndex(pl);
                  _player_ApplyUpgrade(plHnd, 0, cid, ind, False);
               end;
            end;
         end;
      end;
      if (gMap.settings.additional.balloon<>gc_mapsettings_balloon_default) then
      begin
         var ind : Integer = _country_GetUpgradeIndexByUpgradeType(cid, gc_upg_type_balloon);
         if (ind>0) then
         begin
            case gMap.settings.additional.balloon of
               gc_mapsettings_balloon_no : begin
                  var pl : Integer;
                  for pl:=0 to gc_MaxPlayerCount-1 do
                  gPlayer[pl].upgstate[cid][ind].enabled := False;
               end;
               gc_mapsettings_balloon_with : begin
                  var pl : Integer;
                  for pl:=0 to gc_MaxPlayerCount-1 do
                  begin
                     var plHnd : Integer = GetPlayerHandleByIndex(pl);
                     _player_ApplyUpgrade(plHnd, 0, cid, ind, False);
                  end;
               end;
            end;
         end;
      end;
   end;
   if (gMap.settings.additional.capture<>gc_mapsettings_capture_default) then
   begin
      {
      // moved to nothing
      for pl:=0 to gc_MaxPlayerCount-1 do
      begin
         for cid:=0 to gc_MaxCountryCount-1 do
         for id:=0 to gc_country_maxmembers-1 do
         begin
            pobjprop := gObjProp[cid][id];
            if (TObjProp(pobjprop).bcapture) then
            begin
               case gMap.settings.additional.capture of
                  gc_mapsettings_capture_nopeasants : begin
                     if (TObjProp(pobjprop).usage=gc_obj_usage_peasant) then
                     TObjProp(pobjprop).bcapture := False;
                  end;
                  gc_mapsettings_capture_nocenterspeasants : begin
                     if (TObjProp(pobjprop).usage=gc_obj_usage_peasant) or (TObjProp(pobjprop).usage=gc_obj_usage_center) then
                     TObjProp(pobjprop).bcapture := False;
                  end;
                  gc_mapsettings_capture_onlyartillery : begin
                     if (not TObjProp(pobjprop).bArtillery) then
                     TObjProp(pobjprop).bcapture := False;
                  end;
               end;
            end;
         end;
      end;
      }
   end;
   if (gMap.settings.additional.cannons<>gc_mapsettings_cannons_default) then
   begin
      for pl:=0 to gc_MaxPlayerCount-1 do
      begin
         for cid:=0 to gc_MaxCountryCount-1 do
         for id:=0 to gc_country_maxmembers-1 do
         begin
            pobjbase := gPlayer[pl].objbase[cid][id];
            pobjprop := gObjProp[cid][id];
            case gMap.settings.additional.cannons of
               gc_mapsettings_cannons_nocannonstowerswalls : begin
                  if (TObjProp(pobjprop).usage=gc_obj_usage_mortar) or (TObjProp(pobjprop).usage=gc_obj_usage_supermortar) or (TObjProp(pobjprop).usage=gc_obj_usage_mcannon) or (TObjProp(pobjprop).usage=gc_obj_usage_cannon) or (TObjProp(pobjprop).usage=gc_obj_usage_hardwall) or (TObjProp(pobjprop).usage=gc_obj_usage_tower) then
                  begin
                     TObjBase(pobjbase).bproduceenabled := False;
                     var csid : String;
                     _country_GetSIDByID(cid, csid);
                     var upgind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, csid+'art.cannon.1.1', False);
                     if (upgind>-1) then
                     gPlayer[pl].upgstate[cid, upgind].enabled := False;
                     upgind := _country_GetUpgradeIndexByUpgradeID(cid, csid+'art.cannon.2.1', False);
                     if (upgind>-1) then
                     gPlayer[pl].upgstate[cid, upgind].enabled := False;
                     
                     upgind := _country_GetUpgradeIndexByUpgradeID(cid, csid+'art.howitzer.1.1', False);
                     if (upgind>-1) then
                     gPlayer[pl].upgstate[cid, upgind].enabled := False;
                     upgind := _country_GetUpgradeIndexByUpgradeID(cid, csid+'art.howitzer.2.1', False);
                     if (upgind>-1) then
                     gPlayer[pl].upgstate[cid, upgind].enabled := False;
                     
                     upgind := _country_GetUpgradeIndexByUpgradeID(cid, csid+'aca.19', False);
                     if (upgind>-1) then
                     gPlayer[pl].upgstate[cid, upgind].enabled := False;
                  end;
               end;
               gc_mapsettings_cannons_expensivecannons : begin
                  if (TObjProp(pobjprop).bartillery) then
                  begin
                     var res : Integer;
                     for res:=0 to gc_ResCount-1 do
                     TObjBase(pobjbase).price[res] := TObjBase(pobjbase).price[res]*gc_gameplay_expensivecannonskoef;
                  end;
               end;
            end;
         end;
      end;
   end;
   if (gMap.settings.additional.marketdip<>gc_mapsettings_marketdip_default) then
   begin
      for pl:=0 to gc_MaxPlayerCount-1 do
      begin
         for cid:=0 to gc_MaxCountryCount-1 do
         for id:=0 to gc_country_maxmembers-1 do
         begin
            pobjbase := gPlayer[pl].objbase[cid][id];
            pobjprop := gObjProp[cid][id];
            case gMap.settings.additional.marketdip of
               gc_mapsettings_marketdip_nodip : begin
                  if (TObjProp(pobjprop).usage=gc_obj_usage_dipcenter) then
                  TObjBase(pobjbase).bproduceenabled := False;
               end;
               gc_mapsettings_marketdip_nomarket : begin
                  if (TObjProp(pobjprop).bmarket) then
                  TObjBase(pobjbase).bproduceenabled := False;
               end;
               gc_mapsettings_marketdip_noboth : begin
                  if (TObjProp(pobjprop).usage=gc_obj_usage_dipcenter) or (TObjProp(pobjprop).bmarket) then
                  TObjBase(pobjbase).bproduceenabled := False;
               end;
            end;
         end;
      end;
   end;
end;

// _player_FixPlayersUpgStateBadIndexes
//
procedure _player_FixPlayersUpgStateBadIndexes;
begin
   // check if wrong indexes exists
   var bBadIndexExists : Boolean;
   var pl, cid, upgid : Integer;
   for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
   begin
      for cid:=0 to gc_MaxCountryCount-1 do
      for upgid:=0 to gc_country_maxupgradecount-1 do
      begin
         // bad id found
         if (gPlayer[pl].upgstate[cid][upgid].sid<>gCountry[cid].upgrade[upgid].id) then
         begin
            bBadIndexExists := True;
            break(MAIN);
         end;
      end;
   end;
   
   if (bBadIndexExists) then // fix it
   begin
      var slog : String = '_player_FixPlayersUpgStateBadIndexes : bBadIndexExists = True, but now FIXED';
      var i : Integer;
      type TTmp = class
         plUpgState : array [0..gc_MaxPlayerCount-1] of array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxupgradecount-1] of TPlayerUpgradeState;
      end;
      var p : Pointer;
      GetMem(p, SizeOf(TTmp));
      if (p<>nil) then
      begin
         FillMem(p, SizeOf(TTmp), 0);
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         begin
            for cid:=0 to gc_MaxCountryCount-1 do
            for upgid:=0 to gc_country_maxupgradecount-1 do
            begin
               _player_ClearUpgradeState(TTmp(p).plUpgState[pl][cid][upgid]);
               TTmp(p).plUpgState[pl][cid][upgid].sid := gCountry[cid].upgrade[upgid].id;
               TTmp(p).plUpgState[pl][cid][upgid].enabled := gCountry[cid].upgrade[upgid].enabled;
            end;
         end;
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         begin
            for cid:=0 to gc_MaxCountryCount-1 do
            for upgid:=0 to gc_country_maxupgradecount-1 do
            begin
               // bad id found
               //if (gPlayer[pl].upgstate[cid][upgid].sid<>TTmp(p).plUpgState[pl][cid][upgid].sid) then
               begin
                  var goodSID : String = TTmp(p).plUpgState[pl][cid][upgid].sid;
                  if (goodSID<>'') then
                  begin
                     var bfound : Boolean;
                     for i:=0 to gc_country_maxupgradecount-1 do
                     if (gPlayer[pl].upgstate[cid][i].sid=goodSID) then
                     begin
                        //Log('_player_FixPlayersUpgStateBadIndexes : fix upg goodSID = '+goodSID+' prev id = '+IntToStr(upgid)+' new id = '+IntToStr(i));
                        TTmp(p).plUpgState[pl][cid][upgid] := gPlayer[pl].upgstate[cid][i];
                        bfound := True;
                        break;
                     end;
                     if (not bfound) then
                     slog := '_player_FixPlayersUpgStateBadIndexes : NEW upgid, not found in map gPlayer['+IntToStr(pl)+'].upgstate['+IntToStr(cid)+'] array. goodSID = '+goodSID;
                  end;
               end;
            end;
         end;
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         for cid:=0 to gc_MaxCountryCount-1 do
         for upgid:=0 to gc_country_maxupgradecount-1 do
         gPlayer[pl].upgstate[cid][upgid] := TTmp(p).plUpgState[pl][cid][upgid];
         FreeMem(p);
         Log(slog);
      end;
   end;
end;

// _player_FixPlayersObjBaseBadIndexes
//
procedure _player_FixPlayersObjBaseBadIndexes;
begin
   // check if wrong indexes exists
   var bBadIndexExists : Boolean;
   var pl, cid, id : Integer;
   for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
   begin
      for cid:=0 to gc_MaxCountryCount-1 do
      for id:=0 to gc_country_maxmembers-1 do
      begin
         // bad id found
         if (gPlayer[pl].objbase[cid][id].sid<>gObjProp[cid][id].sid) or ((gPlayer[pl].objbase[cid][id].sid<>'') and (gPlayer[pl].objbase[cid][id].maxhp=0) and (id>0) and (gObjProp[cid][id].usage<>gc_obj_usage_field)) then
         begin
            bBadIndexExists := True;
            break(MAIN);
         end;
      end;
   end;
   
   if (bBadIndexExists) then // fix it
   begin
      var slog : String = '_player_FixPlayersObjBaseBadIndexes : bBadIndexExists = True, but now FIXED';
      type TTmp = class
         var plObjBase : array [0..gc_MaxPlayerCount-1] of array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of TObjBase;
      end;
      var p : Pointer;
      GetMem(p, SizeOf(TTmp));
      if (p<>nil) then
      begin
         FillMem(p, SizeOf(TTmp), 0);
         var i : Integer;
         var tempObjProp : TObjProp;
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         begin
            for cid:=0 to gc_MaxCountryCount-1 do
            for id:=0 to gc_country_maxmembers-1 do
            begin
               TTmp(p).plObjBase[pl][cid][id].Create;
               _unit_InitBase(TTmp(p).plObjBase[pl][cid][id], tempObjProp, cid, id);
            end;
         end;
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         begin
            for cid:=0 to gc_MaxCountryCount-1 do
            for id:=0 to gc_country_maxmembers-1 do
            begin
               // bad id found
               if ((gPlayer[pl].objbase[cid][id].sid<>TTmp(p).plObjBase[pl][cid][id].sid))
               or ((gPlayer[pl].objbase[cid][id].sid<>'') and (gPlayer[pl].objbase[cid][id].maxhp=0) and (id>0) and (gObjProp[cid][id].usage<>gc_obj_usage_field)) then
               begin
                  var goodSID : String = TTmp(p).plObjBase[pl][cid][id].sid;
                  if (goodSID<>'') then
                  begin
                     var bfound : Boolean;
                     for i:=0 to gc_country_maxmembers-1 do
                     if (gPlayer[pl].objbase[cid][i].sid=goodSID) then
                     begin
                        if (TTmp(p).plObjBase[pl][cid][id].maxhp=0) or (gPlayer[pl].objbase[cid][i].maxhp<>0) then
                        begin
                           TTmp(p).plObjBase[pl][cid][id] := gPlayer[pl].objbase[cid][i];
                           bfound := True;
                           break;
                        end;
                     end;
                     if (not bfound) then
                     slog := '_player_FixPlayersObjBaseBadIndexes : NEW objbase id, not found in map gPlayer['+IntToStr(pl)+'].objbase['+IntToStr(cid)+'] array. goodSID = '+goodSID;
                  end;
               end;
            end;
         end;
         for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
         for cid:=0 to gc_MaxCountryCount-1 do
         for id:=0 to gc_country_maxmembers-1 do
         begin
            if (gPlayer[pl].objbase[cid][id].sid<>TTmp(p).plObjBase[pl][cid][id].sid) or ((gPlayer[pl].objbase[cid][id].sid<>'') and (gPlayer[pl].objbase[cid][id].maxhp=0) and (id>0) and (gObjProp[cid][id].usage<>gc_obj_usage_field)) then
            begin
               if (TTmp(p).plObjBase[pl][cid][id].sid<>'') and ((TTmp(p).plObjBase[pl][cid][id].maxhp>0) or (gPlayer[pl].objbase[cid][id].maxhp=0)) then
               gPlayer[pl].objbase[cid][id] := TTmp(p).plObjBase[pl][cid][id];
            end;
         end;
         FreeMem(p);
         Log(slog);
      end;
   end;
end;

// _player_FixPortBuiChildren
//
procedure _player_FixPortBuiChildren;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) and (_unit_IsBuilding(goHnd)) and (_unit_GetUsage(goHnd)=gc_obj_usage_port) then
            begin
               var tmpx, tmpz : Float;
               if (_misc_GetPortResourcePoint(goHnd, tmpx, tmpz, True)) then
               begin
                  var buiHnd : Integer = GetGameObjectGOHandleChildByBaseName(goHnd, gc_racename_misc, gc_basename_bui);
                  if (buiHnd=0) then
                  begin
                     var miscplhnd : Integer = GetPlayerHandleByIndex(gc_playerind_misc);
                     buiHnd := CreatePlayerGameObjectHandleByHandle(miscplhnd, gc_racename_misc, gc_basename_bui, tmpx, 0, tmpz);
                     GameObjectSwitchToFrameAnimationByHandle(buiHnd, 'idle', True);
                     SetGameObjectAnimationModeByHandle(buiHnd, 'aamLoop');
                     GameObjectParentToChildAbsolute(buiHnd, goHnd);
                     _misc_ApplyKeyColorRecursiveChild(buiHnd, gMap.players[pl].color);
                  end
                  else
                  SetGameObjectAbsolutePositionByHandle(buiHnd, tmpx, 0, tmpz);
               end
               else
               ErrorLog('_player_FixPortBuiChildren : port resource point not found');
            end;
         end;
      end;
   end;
end;

// _player_FixPlayerLists
//
procedure _player_FixPlayerLists;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) and (_unit_IsBuilding(goHnd)) then
            begin
               _unit_RemoveObjFromPlayerLists(goHnd);
               _unit_AddObjToPlayerLists(goHnd);
            end;
         end;
      end;
   end;
end;

// _player_FixRallyPos
//
procedure _player_FixRallyPos;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) and (_unit_IsBuilding(goHnd)) then
            begin
               var pobj : Pointer = _unit_GetTObj(goHnd);
               if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].bcansetrally) then
               begin
                  var px : Float = GetGameObjectPositionXByHandle(goHnd);
                  var pz : Float = GetGameObjectPositionZByHandle(goHnd);
                  var exitCount : Integer = gCustomObjPoints[TObj(pobj).cid, TObj(pobj).id].exitCount;
                  if exitCount > 0 then
                  begin
                     TObj(pobj).rallyx := px + gCustomObjPoints[TObj(pobj).cid, TObj(pobj).id].exitPoints[exitCount-1].x;
                     TObj(pobj).rallyy := pz + gCustomObjPoints[TObj(pobj).cid, TObj(pobj).id].exitPoints[exitCount-1].z;
                  end
                  else
                  begin
                     var tempx, tempz : Float;
                     if (_unit_GetUsage(goHnd)=gc_obj_usage_port) and (_misc_GetPortResourcePoint(goHnd, tempx, tempz, True)) then
                     begin
                        TObj(pobj).rallyx := tempx;
                        TObj(pobj).rallyy := tempz;
                     end
                     else
                     begin
                        TObj(pobj).rallyx := px;
                        TObj(pobj).rallyy := pz;
                        ErrorLog('No start point for creating unit!');
                     end;
                  end;
                  TObj(pobj).rallytmpx := TObj(pobj).rallyx;
                  TObj(pobj).rallytmpy := TObj(pobj).rallyy;
               end;
            end;
         end;
      end;
   end;
end;

// _player_FixGameObjectsChildLODActorName
//
procedure _player_FixGameObjectsChildLODActorName;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) and (_unit_IsBuilding(goHnd)) then
            begin
               var lodactorparent : String = GetGameObjectLODActorNameByHandle(goHnd);
               var count : Integer = GetGameObjectCountChildByHandle(goHnd);
               if (count>0) then
               begin
                  for j:=0 to count-1 do
                  begin
                     var child : Integer = GetGameObjectGOHandleChildByHandle(goHnd, j);
                     var lodactor : String = GetGameObjectLODActorNameByHandle(child);
                     if (lodactor=lodactorparent) then
                     SetGameObjectLODActorNameByHandle(child, lodactorparent+'a');
                  end;
               end;
               var aname : String = lodactorparent+'anim';
               var child : Integer = GetGameObjectGOHandleChildByBaseName(goHnd, gc_racename_buildings, aname);
               if (child=0) then
               GameObjectAddNewChild(goHnd, gc_racename_buildings, aname);
               aname := lodactorparent+'anim2';
               child := GetGameObjectGOHandleChildByBaseName(goHnd, gc_racename_buildings, aname);
               if (child=0) then
               GameObjectAddNewChild(goHnd, gc_racename_buildings, aname);
            end;
         end;
      end;
   end;
end;

// _player_FixGameObjects
//
procedure _player_FixGameObjects(serialVer : Integer);
begin
   if (serialVer<42) then // bonus check if we need to have more serial check than one
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=gc_MaxPlayerCount-1 downto 0 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=GetPlayerGameObjectsCountByHandle(plHnd)-1 downto 0 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) then
            begin
               var pobj : Pointer = _unit_GetTObj(goHnd);
               var id : Integer = TObj(pobj).id;
               var cid : Integer = TObj(pobj).cid;
               if (serialVer<42) then
               begin
                  var usage : Integer =_unit_GetUsage(goHnd);
                  var sminterval : Integer = GetGameObjectStateMachineIntervalByHandle(goHnd);
                  case usage of
                     gc_obj_usage_peasant : begin
                        if (sminterval<>gc_statemachine_interval_peasants) then
                        SetGameObjectStateMachineIntervalByHandle(goHnd, gc_statemachine_interval_peasants);
                     end;
                     gc_obj_usage_mine : begin
                        if (sminterval<>gc_statemachine_interval_mines) then
                        SetGameObjectStateMachineIntervalByHandle(goHnd, gc_statemachine_interval_mines);
                     end;
                  end;
                  if _unit_IsBuilding(goHnd) and (usage<>gc_obj_usage_mine) and (sminterval<>gc_statemachine_interval_buildings) then
                  SetGameObjectStateMachineIntervalByHandle(goHnd, gc_statemachine_interval_buildings);
                  var bchanged : Boolean = GetGameObjectStateMachineIntervalByHandle(goHnd)<>sminterval;
                  if (bchanged) then
                  begin
                     var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
                     SetGameObjectPlayerHandleByHandle(goHnd, plHnd); // update progresssections
                  end;
               end;
            end;
         end;
      end;
   end;
end;

// _player_FixPlayersCounters
//
procedure _player_FixPlayersCounters();
begin
   var pl, cid, id, k : Integer;
   for pl:=0 to gc_MaxPlayerCount-1 do
   begin
      gPlayer[pl].lists.buildings.Clear;
      gPlayer[pl].lists.storehouses.Clear;
      gPlayer[pl].lists.ports.Clear;
      gPlayer[pl].farm := 0;
      gPlayer[pl].counter.farmused := 0;
      gPlayer[pl].counter.scores := 0;
      for k:=0 to gc_ResCount-1 do
      begin
         gPlayer[pl].counter.resincome[k] := 0;
         gPlayer[pl].counter.resconsume[k] := 0;
      end;
      for k:=0 to gc_MaxArtilleryType-1 do
      gPlayer[pl].artlimit[k] := 0;
      for k:=gc_obj_artind_cannon to gc_obj_artind_multicannon do
      gPlayer[pl].artcount[k] := 0;
      for cid:=0 to gc_MaxCountryCount-1 do
      for id:=0 to gc_country_maxmembers-1 do
      begin
         gPlayer[pl].counter.built[cid][id] := 0;
         gPlayer[pl].counter.all[cid][id] := 0;
         gPlayer[pl].counter.total := 0;
      end;
   end;
   
   for k:=1 to gc_MaxPlayerCount do
   begin
      var pEnemyInfo : Pointer = _misc_GetTeamEnemyInfo(k);
      if (pEnemyInfo <> nil) and TEnemyInfo(pEnemyInfo).enabled then
      TEnemyInfo(pEnemyInfo).towers.Clear;
   end;
   
   for pl:=0 to gc_MaxPlayerCount-1 do
   begin
      var plHnd : Integer = GetPlayerHandleByIndex(pl);
      var i : Integer;
      for i:=GetPlayerGameObjectsCountByHandle(plHnd)-1 downto 0 do
      begin
         var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         begin
            _unit_TopologyRemove(goHnd);
            _unit_ScanGridRemoveUnit(goHnd);
            if (not TObj(pobj).bdead) then
            begin
               _unit_AddObjToPlayerCounters(goHnd, TObj(pobj).bbuilt, False, True);
               
               if (TObj(pobj).insideofuid<>0) then
               begin
                  var trgHnd : Integer = GetGameObjectHandleByUniqueId(TObj(pobj).insideofuid);
                  if (trgHnd<>0) then
                  _unit_AddInside(goHnd, trgHnd);
               end;
            end;
         end;
      end;
   end;
end;

// _player_FixInvalidHandlesInLists
//
function _player_FixInvalidHandlesInLists(bFix : Boolean) : Boolean;
begin
   //TimeLog('_player_FixInvalidHandlesInLists : start time consuming function');
   var processed, compared : Integer;
   var pl : Integer;
   for pl:=0 to gc_MaxPlayerCount-1 do
   begin
      var plhnd : Integer = GetPlayerHandleByIndex(pl);
      var hnd, test : Integer;
      var bfound : Boolean;
      var i, j, iteration : Integer;
      for iteration:=0 to 3 do
      begin
         var list : Pointer;
         case iteration of
            0 : list := gPlayer[pl].aiData.agressors;
            1 : list := gPlayer[pl].aiData.freewarriors;
            2 : list := gPlayer[pl].aiData.defenders;
            3 : list := gPlayer[pl].aiData.guards;
         end;
         for i:=TIntegerList(list).GetCount-1 downto 0 do
         begin
            processed := processed+1;
            hnd := TIntegerList(list).Get(i);
            if (hnd=0) then
            begin
               if (bFix) then
               TIntegerList(list).Delete(i);
               ErrorLog('_player_FixInvalidHandlesInLists : pl = '+IntToStr(pl)+' : iteration = '+IntToStr(iteration)+' : ind = '+IntToStr(i)+' : hnd = '+IntToStr(hnd));
            end
            else
            begin
               bfound := False;
               for j:=GetPlayerGameObjectsCountByHandle(plhnd)-1 downto 0 do
               begin
                  compared := compared+1;
                  test := GetGameObjectHandleByIndex(j, plhnd);
                  if (test=hnd) then
                  begin
                     bfound := True;
                     break;
                  end;
               end;
               if (not bfound) then
               begin
                  if (bFix) then
                  TIntegerList(list).Delete(i);
                  ErrorLog('_player_FixInvalidHandlesInLists : pl = '+IntToStr(pl)+' : iteration = '+IntToStr(iteration)+' : ind = '+IntToStr(i)+' : hnd = '+IntToStr(hnd));
               end
               else
               begin
                  if (_unit_GetUsage(hnd)=gc_obj_usage_peasant) then
                  begin
                     if (bFix) then
                     TIntegerList(list).Delete(i);
                     ErrorLog('_player_FixInvalidHandlesInLists : pl = '+IntToStr(pl)+' : iteration = '+IntToStr(iteration)+' : ind = '+IntToStr(i)+' : hnd = '+IntToStr(hnd)+' : is a peasant');
                  end;
               end;
            end;
         end;
      end;
   end;
   //TimeLog('_player_FixInvalidHandlesInLists : processed unit count = '+IntToStr(processed)+' : compared = '+IntToStr(compared));
end;

// _player_FixGameObjectsUnitIDs
//
procedure _player_FixGameObjectsUnitIDs;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j, k : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) then
            begin
               var pobj : Pointer = _unit_GetTObj(goHnd); // pointer already <> nil
               if (TObj(pobj).bdead) and (TObj(pobj).ctrlgroupmask>0) then // hack that fix rare crash on load save.
               begin
                  for k:=0 to 9 do
                  begin
                     if (TObj(pobj).ctrlgroupmask and (1 shl k)<>0) then
                     gCtrlGroups[TObj(pobj).pl][k].Remove(goHnd);
                     ErrorLog('Something gone wrong and was fixed : second place');
                  end;
                  TObj(pobj).ctrlgroupmask := 0;
               end;
               var basename : String = GetGameObjectBaseNameByHandle(goHnd);
               var id : Integer = TObj(pobj).id;
               var cid : Integer = TObj(pobj).cid;
               if (gObjProp[cid][id].sid<>basename) then // wrong id
               begin
                  var bfound : Boolean;
                  for j:=0 to gc_country_maxmembers-1 do
                  begin
                     if (gObjProp[cid][j].sid=basename) then
                     begin
                        slog := '_player_FixGameObjectsUnitIDs : fix obj hnd = '+IntToStr(goHnd)+' sid='+basename+' prev id = '+IntToStr(id)+' new id = '+IntToStr(j);
                        TObj(pobj).id := j;
                        if (not TObj(pobj).bdead) and (TObj(pobj).hp>0) then
                        TObj(pobj).hp := gPlayer[pl].objbase[cid][j].maxhp;
                        bfound := True;
                        break;
                     end;
                  end;
                  if (not bfound) and (not gObjProp[cid][id].bwall) and (not gObjProp[cid][id].bgate) then
                  slog := '_player_FixGameObjectsUnitIDs : CAN NOT fix obj ID. hnd = '+IntToStr(goHnd)+' sid='+basename+' prev id = '+IntToStr(id)+' new id = ??? not exists in new country';
               end;
            end;
         end;
      end;
      if (slog<>'') then
      Log(slog);
   end
   else
   ErrorLog('_player_FixGameObjectsUnitIDs : not enough players count');
end;

// _player_FixGameObjectsWallVariations
//
procedure _player_FixGameObjectsWallVariations;
begin
   if GetCountOfPlayers>=gc_MaxPlayerCount then
   begin
      var slog : String;
      var pl, i, j : Integer;
      for [MAIN]pl:=0 to gc_MaxPlayerCount-1 do
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
         begin
            var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
            if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) then
            begin
               var pobj : Pointer = _unit_GetTObj(goHnd); // pointer already <> nil
               var id : Integer = TObj(pobj).id;
               var cid : Integer = TObj(pobj).cid;
               if gObjProp[cid][id].bwall then
               begin
                  var basename : String = GetGameObjectBaseNameByHandle(goHnd);
                  var variation : Integer = _misc_GetWallVariationByName(basename);
                  if (variation<>TObj(pobj).wallvariation) then
                  begin
                     TObj(pobj).wallvariation := variation;
                     slog := '_player_FixGameObjectsWallVariations : fixed';
                  end;
               end;
            end;
         end;
      end;
      if (slog<>'') then
      Log(slog);
   end
   else
   ErrorLog('_player_FixGameObjectsWallVariations : not enough players count');
end;

procedure _player_UpdateAIData(plInd : Integer; bClearLists : Boolean);
begin
   var plHnd : Integer = GetPlayerHandleByIndex(plInd);
   if (bClearLists) then
   gPlayer[plInd].aiData.Reset;
   var i : Integer;
   for i:=0 to GetPlayerGameObjectsCountByHandle(plHnd)-1 do
   begin
      var goHnd : Integer = GetGameObjectHandleByIndex(i, plHnd);
      var pobj : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_obj);
      if (pobj<>nil) and (not TObj(pobj).bdead) and (_unit_GetUsage(goHnd)<>gc_obj_usage_peasant) then
      begin
         if (bClearLists) and (_unit_IsBuilding(goHnd)) then
         begin
            var pDefBuilding : Pointer = _unit_AIAddDefendingBuilding(plInd, gohnd);
            if (pDefBuilding<>nil) then
            begin
               var pordlist : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_ordlist);
               if (pordlist<>nil) then
               begin
                  var j : Integer;
                  for j:=0 to TIntegerList(pordlist).GetCount-1 do
                  begin
                     var tmpHnd : Integer = TIntegerList(pordlist).Get(j);
                     if (_unit_IsGuard(tmpHnd)) then
                     TAiProtectedBuilding(pDefBuilding).defenders.Add(tmpHnd);
                  end;
               end;
            end;
            
            if (not TObj(pobj).bbuilt) then
            _unit_AIAddToBuildProjects(plInd, goHnd);
         end
         else
         if (bClearLists) or ((gPlayer[plInd].aiData.agressors.IndexOf(goHnd)<0) and (gPlayer[plInd].aiData.defenders.IndexOf(goHnd)<0) and (gPlayer[plInd].aiData.freewarriors.IndexOf(goHnd)<0)) then
         begin
            if (TObj(pobj).squad>=0) then
            begin
               var psquad : Pointer = TSquad(gPlayer[plInd].squads.Get(TObj(pobj).squad));
               if (psquad<>nil) and (TSquad(psquad).fArmy>=0) then
               continue;
            end;
            _unit_AddToAIData(goHnd);
         end;
      end;
   end;
end;

// _player_AddToWriteNewRequestList
//
function _player_AddToWriteNewRequestList(plind : Integer; racename, basename : String; cid, uid : Integer; posx, posz : Float; itype : Integer; bpush : Boolean) : Pointer;
begin
   GetMem(result, SizeOf(TWriteNewRequest));
   FillMem(result, SizeOf(TWriteNewRequest), 0);
   gWriteNewRequestList.AddPointer(result);
   TWriteNewRequest(result).plind := plind;
   TWriteNewRequest(result).racename := racename;
   TWriteNewRequest(result).basename := basename;
   TWriteNewRequest(result).cid := cid;
   TWriteNewRequest(result).uid := uid;
   TWriteNewRequest(result).posx := posx;
   TWriteNewRequest(result).posz := posz;
   TWriteNewRequest(result).itype := itype;
   TWriteNewRequest(result).bpush := bpush;
end;

// _player_ProgressWriteNewRequestList
//
procedure _player_ProgressWriteNewRequestList();
begin
   var count : Integer;
   if (gWriteNewRequestList.GetCount>=20) then
   count := 1+floor(random*(1+gWriteNewRequestList.GetCount div 10))
   else
   count := 1;
   var i : Integer;
   for i:=count-1 downto 0 do
   begin
      var pwritenew : Pointer = gWriteNewRequestList.GetPointer(i);
      if (pwritenew<>nil) then
      begin
         var plind : Integer = TWriteNewRequest(pwritenew).plind;
         var plhnd : Integer = GetPlayerHandleByIndex(plind);
         var cid : Integer;
         var owneruid : Integer = TWriteNewRequest(pwritenew).uid;
         var ownerhnd : Integer;
         var pownerobj : Pointer;
         if (owneruid>0) then
         begin
            ownerhnd := GetGameObjectHandleByUniqueId(owneruid);
            if (ownerhnd<>0) then
            begin
               pownerobj := _unit_GetTObj(ownerhnd);
               cid := TObj(pownerobj).cid;
            end;
         end
         else
         cid := TWriteNewRequest(pwritenew).cid;
         
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
         TPlayerArgs(parg).fposx:=TWriteNewRequest(pwritenew).posx;
         TPlayerArgs(parg).fposz:=TWriteNewRequest(pwritenew).posz;
         TPlayerArgs(parg).fracename:=TWriteNewRequest(pwritenew).racename;
         TPlayerArgs(parg).fbasename:=TWriteNewRequest(pwritenew).basename;
         TPlayerArgs(parg).fcid:=cid;
         PlayerExecuteStateByHandle(plhnd, 'WriteNew');
         var hnd : Integer = TPlayerArgs(parg).fresult;
         
         if (ownerhnd<>0) and (pownerobj<>nil)then
         begin
            var resHnd : Integer;
            var px, pz : Float;
            if (gObjProp[TObj(pownerobj).cid][TObj(pownerobj).id].usage=gc_obj_usage_port) then
            begin
               var resHnd : Integer;
               _unit_GetBuildingExitData(hnd, ownerhnd, False, resHnd, px, pz);
               _unit_RemoveShipDummy(ownerhnd);
               
               if _unit_GetUsage(hnd) <> gc_obj_usage_fisher then
               _unit_OrderLeave(hnd, ownerhnd, px, pz);
            end
            else
            begin
               _unit_SetSTO(hnd, ownerhnd);
               _unit_SetTagStates(hnd, gc_statetag_visual_hide or gc_statetag_essential_birth);
               var bCheckRes : Boolean = TObj(pownerobj).brally;
               _unit_GetBuildingExitData(hnd, ownerhnd, bCheckRes, resHnd, px, pz);
               _unit_OrderLeave(hnd, ownerhnd, px, pz);
               if (resHnd<>0) then
               _unit_OrderGainResource(hnd, resHnd, GetGameObjectPositionXByHandle(resHnd), GetGameObjectPositionZByHandle(resHnd));
            end;
         end;
         gWriteNewRequestList.Delete(i);
      end;
   end;
end;

_player_InitAllPlayers;