//
// unit.script
//

// _unit_GetTObj
//
function _unit_GetTObj(const hnd: Integer): Pointer;
begin
   result:=_misc_GetObjectArgData(hnd, gc_argunit_obj);
   if result=nil then begin
      if (gProfile.bDbgLogPointerNil) then
      _init_DebugPointerNil(result);
   end else if TBaseObj(result).baseid<>gc_baseid_obj then
   begin
      if (TBaseObj(result).baseid>gc_baseid_prj) then
      ErrorLog('_unit_GetTObj invalid handle, baseid is out of rangestate='+GetCurrentStateName+' filename='+GetStateMachineFileName(GetCurrentStateMachineHandle)+' hnd='+IntToStr(hnd))
      else
      ErrorLog('_unit_GetTObj try to get wrong baseid : state='+GetCurrentStateName+' filename='+GetStateMachineFileName(GetCurrentStateMachineHandle)+' hnd='+IntToStr(hnd));
      result:=nil;
   end;
end;

// _unit_GetTObjFast
//
function _unit_GetTObjFast(const goHnd : Integer) : Pointer;
begin
   var hnd : Integer = GetGameObjectStateMachineHandle(goHnd);
   if (hnd<>0) then
   Result := StateMachineGetArgDataByInd(hnd, gc_argunit_obj)
   else
   Result := nil;
end;

// _unit_GetObjProp
//
function _unit_GetObjProp(const hnd: Integer) : Pointer;
begin
   var pObj : Pointer = _unit_GetTObj(hnd);
   if pObj <> nil then
   Result := gObjProp[TObj(pObj).cid][TObj(pObj).id]
   else
   Result := nil;
end;

// _unit_GetObjBase
//
function _unit_GetObjBase(const hnd: Integer) : Pointer;
begin
   var pObj : Pointer = _unit_GetTObj(hnd);
   if pObj <> nil then
   Result := gPlayer[TObj(pObj).pl].objBase[TObj(pObj).cid][TObj(pObj).id]
   else
   Result := nil;
end;

// _unit_IsUnit
//
function _unit_IsUnit(const goHnd : Integer) : Boolean;
begin
   if _misc_GetBaseObjID(goHnd)=gc_baseid_obj then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      if pObj <> nil then
      Result := not gObjProp[TObj(pObj).cid][TObj(pObj).id].bbuilding
      else
      Result := false;
   end
   else
   Result := false;
end;

// _unit_GetRegion
//
function _unit_GetRegion(const goHnd : Integer) : Integer;
begin
   var zInd : Integer = TopologyGetZoneIndex(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd));
   Result := TopologyGetZoneRegionByIndex(zInd);
end;

// _unit_IsWaterUnit
//
function _unit_IsWaterUnit(goHnd : Integer) : Boolean;
begin
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   if pObjProp <> nil then
   Result := (TObjProp(pObjProp).media = gc_obj_media_water)
   else
   Result := false;
end;

// _unit_IsShip
//
function _unit_IsShip(goHnd : Integer) : Boolean;
begin
   Result := _unit_IsWaterUnit(goHnd);
end;

// _unit_IsBattleShip
//
function _unit_IsBattleShip(goHnd : Integer) : Boolean;
begin
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   if pObjProp <> nil then
   Result := ((TObjProp(pObjProp).media = gc_obj_media_water) and (TObjProp(pObjProp).transport = 0))
   else
   Result := false;
end;

// _unit_IsBuilding
//
function _unit_IsBuilding(const goHnd : Integer) : Boolean;
begin
   if _misc_GetBaseObjID(goHnd)=gc_baseid_obj then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      if pObj <> nil then
      Result := gObjProp[TObj(pObj).cid][TObj(pObj).id].bbuilding
      else
      Result := false;
   end
   else
   Result := false;
end;

// _unit_ResetArtParams
//
procedure _unit_ResetArtParams(goHnd : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   begin
      TObj(pObj).inBattle := false;
      TObj(pObj).artX := 0;
      TObj(pObj).artY := 0;
      TObj(pObj).artHnd := 0;
      TObj(pObj).artDistFactor := 0;
   end;
end;

// _unit_GetUsage
//
function _unit_GetUsage(const hnd: Integer) : Integer;
begin
   if _misc_GetBaseObjID(hnd)=gc_baseid_obj then
   begin
      var pobj : Pointer = _unit_GetTObj(hnd);
      if (pobj<>nil) then
      Result := gObjProp[TObj(pobj).cid][TObj(pobj).id].usage
      else
      Result := gc_obj_usage_none;
   end
   else
   Result := gc_obj_usage_none;
end;

// _unit_GetDanger
//
function _unit_GetDanger(const goHnd : Integer) : Float;
begin
   if _unit_IsUnit(goHnd) then
   begin
      var pObjProp : Pointer = _unit_GetObjProp(goHnd);
      if (TObjProp(pObjProp).usage <> gc_obj_usage_peasant) and (not TObjProp(pObjProp).bPriest)
      and (not TObjProp(pObjProp).bDrummer) and (not TObjProp(pObjProp).bOfficer) then
      Result := gc_top_UnitDanger
      else
      Result := 0;
   end
   else
   Result := 0;
end;

// _unit_GetAttract
//
function _unit_GetAttract(const goHnd : Integer) : Float;
begin
   var pobjprop : Pointer = _unit_GetObjProp(goHnd);
   if TObjProp(pobjprop).bCapture or (TObjProp(pobjprop).usage = gc_obj_usage_tower) then
   Result := gc_top_BuildAttract
   else
   Result := 0;
end;

// _unit_CanKillByMask
//
function _unit_CanKillByMask(const mmask, kmask : Integer) : Boolean;
begin
   Result := (mmask and kmask)<>0;
end;

// _unit_CanKill
//
function _unit_CanKill(const goHnd, trgHnd : Integer) : Boolean;
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pobj2 : Pointer = _unit_GetTObj(trgHnd);
   Result := (gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask and gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask)<>0;
end;

// _unit_CanKillIncludeAttMask
//
function _unit_CanKillIncludeAttMask(const goHnd, trgHnd : Integer) : Boolean;
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pobj2 : Pointer = _unit_GetTObj(trgHnd);
   Result := (gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask and gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask)<>0;
   if (not Result) then
   begin
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var i : Integer;
      for i:=0 to gc_obj_MaxWeapon-1 do
      if (TObjProp(pobjprop).weapon[i].enabled) and ((TObjProp(pobjprop).weapon[i].attmask and gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask)<>0) then
      begin
         Result := True;
         break;
      end;
   end;
end;

// _unit_IsArtillery
//
function _unit_IsArtillery(goHnd : Integer) : Boolean;
begin
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   if (pObjProp<>nil) and (TObjProp(pObjProp).bArtillery) then
   begin
      var usage : Integer = TObjProp(pObjProp).usage;
      Result := ((usage = gc_obj_usage_mortar) or (usage = gc_obj_usage_supermortar) or (usage = gc_obj_usage_cannon));
   end
   else
   Result := false;
end;

// _unit_IsMelee
//
function _unit_IsMelee(goHnd : Integer) : Boolean;
begin
   var pObjBase : Pointer = _unit_GetObjBase(goHnd);
   Result := true;
   if pObjBase <> nil then
   begin
      var i : Integer;
      for i := 0 to gc_obj_MaxWeapon-1 do
      if TObjBase(pObjBase).weapon[i].radiusmax > gc_unit_meleeattackradius then
      begin
         Result := false;
         break;
      end;
   end;
end;

// _unit_SameRegion
//
function _unit_SameRegion(hnd1, hnd2 : Integer) : Boolean;
begin
   var reg1 : Integer = _unit_GetRegion(hnd1);
   var reg2 : Integer = _unit_GetRegion(hnd2);
   Result := (reg1 = reg2);
end;

// _unit_SameRegionExt
//
function _unit_SameRegionExt(hnd1, hnd2 : Integer) : Boolean;
begin
   var reg1 : Integer = _unit_GetRegion(hnd1);
   var reg2 : Integer = _unit_GetRegion(hnd2);
   Result := ((reg1 < 0) or (reg2 < 0) or (reg1 = reg2));
end;

// _unit_GetMinAttackRadiusByObjProp
//
function _unit_GetMinAttackRadiusByObjProp(var objprop : TObjProp) : Float;
begin
   var min : Float = -1;
   var i : Integer;
   for i:=0 to gc_obj_MaxWeapon-1 do
   begin
      if (objprop.weapon[i].enabled) then
      begin
         var tmpMin : Float = objprop.weapon[i].radiusmin;
         if (min=-1) or (tmpMin<min) then
         min := tmpMin;
      end;
   end;
   Result := min;
end;

// _unit_GetMaxAttackRadius
//
function _unit_GetMaxAttackRadius(const goHnd : Integer) : Float;
begin
   var max : Float = -1;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var pl : Integer = TObj(pobj).pl;
      var cid : Integer = TObj(pobj).cid;
      var id : Integer = TObj(pobj).id;
      var i : Integer;
      for i:=0 to gc_obj_MaxWeapon-1 do
      begin
         if (gObjProp[cid][id].weapon[i].enabled) then
         begin
            var tmpMax : Float = gPlayer[pl].objbase[cid][id].weapon[i].radiusmax;
            if tmpMax > max then
            max := tmpMax;
         end;
      end;
   end;
   Result := max;
end;

// _unit_GetAttackRadiusTarget
//
function _unit_GetAttackRadiusTarget(goHnd, trgHnd : Integer) : Float;
begin
   Result := 0;
   var i : Integer;
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   var pObjBase : Pointer = _unit_GetObjBase(goHnd);
   var pTrgObjProp : Pointer = _unit_GetObjProp(trgHnd);
   var bCanTarget : Boolean = (TObjProp(pobjprop).kmask and TObjProp(pTrgObjProp).mmask <> 0);

   var maxRad, maxRadMask : Float;
   for i := 0 to gc_obj_MaxWeapon-1 do
   begin
      var enabled : Boolean = TObjProp(pObjProp).weapon[i].enabled;
      var attMask : Integer = TObjProp(pObjProp).weapon[i].attmask;
      var mMask : Integer = TObjProp(pTrgObjProp).mmask;
      if TObjProp(pObjProp).weapon[i].enabled then
      begin
         if (TObjProp(pObjProp).weapon[i].attmask <> 0) and (TObjProp(pObjProp).weapon[i].attmask and TObjProp(pTrgObjProp).mmask <> 0) then
         begin
            maxRadMask := TObjBase(pObjBase).weapon[i].radiusMax;
            break;
         end
         else
         if bCanTarget then
         maxRad := TObjBase(pObjBase).weapon[i].radiusMax;
      end;
   end;

   if maxRadMask > 0 then
   Result := maxRadMask
   else
   Result := maxRad;
end;

// _unit_GetTagStateByType
//
function _unit_GetTagStateByType(const goHnd, statetype : Integer) : Integer;
begin
   Result := GetGameObjectStatesTagByHandle(goHnd) and statetype;
end;

// _unit_SetTagStates
//
procedure _unit_SetTagStates(const goHnd, statetag : Integer);
begin
   if (statetag<>0) then
   begin
      gint_unit_tagstate := statetag;
      GameObjectExecuteStateByHandle(goHnd, 'SetTagStates');
   end;
end;

// _unit_IsAnimationExists
//
function _unit_IsAnimationExists(goHnd : Integer; var anim : String) : Boolean;
begin
   var sf, ef : Integer;
   Result := (anim='') or (GetGameObjectFrameAnimationDataByHandle(goHnd, anim, sf, ef));
end;

// _unit_IsOfficer
//
function _unit_IsOfficer(const goHnd : Integer) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   Result := gObjProp[TObj(pObj).cid][TObj(pObj).id].bOfficer
   else
   Result := false;
end;

// _unit_IsDrummer
//
function _unit_IsDrummer(const goHnd : Integer) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   Result := gObjProp[TObj(pObj).cid][TObj(pObj).id].bDrummer
   else
   Result := false;
end;

// _unit_IsOfficerOrDrummer
//
function _unit_IsOfficerOrDrummer(const goHnd : Integer) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   Result := gObjProp[TObj(pObj).cid][TObj(pObj).id].bOfficer or gObjProp[TObj(pObj).cid][TObj(pObj).id].bDrummer
   else
   Result := false;
end;

// _unit_GetSquad
//
function _unit_GetSquad(goHnd : Integer) : Pointer;
begin
   var pSquad : Pointer;
   if goHnd <> 0 then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      if pObj <> nil then
      begin
         var plInd : Integer = TObj(pObj).pl;
         var squad : Integer = TObj(pObj).squad;
         if squad >= 0 then
         pSquad := gPlayer[plInd].squads.Get(squad)
         else
         pSquad := nil;
      end;
   end;
   Result := pSquad;
end;

// _unit_ConvertObjIDToSID
//
procedure _unit_ConvertObjIDToSID(const cid, id : Integer; var sid : String);
begin
   sid := gCountry[cid].members[id];
end;

// _unit_ConvertObjSIDToID
//
function _unit_ConvertObjSIDToID(const cid : Integer; var sid : String) : Integer;
begin
   //_misc_ProfilerBegin('_unit_ConvertObjSIDToID');
   Result := 0;
   var i : Integer;
   if sid <> '' then
   for i:=0 to gc_country_maxmembers-1 do
   begin
      if (gCountry[cid].members[i]=sid) then
      begin
         Result := i;
         break;
      end;
   end;
   //_misc_ProfilerEnd('_unit_ConvertObjSIDToID');
end;

// _unit_InitBase
//
procedure _unit_InitBase(var objbase : TObjBase; var objprop : TObjProp; const incid, inid : Integer);
begin
   // инициализирует все настройки и различия в прототипах юнитов, в зависимости от нации и типа юнита

   procedure SetObjBuildingBaseSettings(var objprop : TObjProp; bcapture : Boolean; score, usage : Integer);
   begin
      objprop.material := gc_obj_material_building;
      objprop.explmedia := gc_obj_explmedia_building;
      objprop.explradius := _misc_PixelsToTiles(5);
      objprop.bbuilding := True;
      objprop.bslowdeath := True;
      objprop.bcapture := bcapture;
      objprop.bcancapture := not bcapture;
      objprop.bprotector := False;
      objprop.bnohungry := True;
      if (score<>-1) then
      objprop.score := score;
      if (usage<>-1) then
      objprop.usage := usage;
   end;

   procedure SetObjBuildingProperties(var objprop : TObjProp; var objbase : TObjBase; maxhp, buildtime : Integer; costpercent : Float);
   begin
      if (maxhp<>-1) then
      objbase.maxhp := maxhp;
      if (buildtime<>-1) then
      objbase.buildtime := _misc_BuildtimeToTime(buildtime);
      if (costpercent<>-1) then
      begin
         if (costpercent=0) then
         objprop.costpercent := 100
         else
         objprop.costpercent := costpercent;
      end;
   end;

   procedure SetObjBasePrice(var objbase : TObjBase; food, wood, stone, gold, iron, coal : Integer);
   begin
      objbase.price[gc_resource_type_food] := food;
      objbase.price[gc_resource_type_wood] := wood;
      objbase.price[gc_resource_type_stone] := stone;
      objbase.price[gc_resource_type_gold] := gold;
      objbase.price[gc_resource_type_iron] := iron;
      objbase.price[gc_resource_type_coal] := coal;
   end;

   procedure SetObjBuildingExtProperties(var objprop : TObjProp; var objbase : TObjBase; maxhp, buildtime : Integer; costpercent : Float; bcapture : Boolean; score, usage : Integer; food, wood, stone, gold, iron, coal : Integer);
   begin
      SetObjBuildingBaseSettings(objprop, bcapture, score, usage);
      SetObjBuildingProperties(objprop, objbase, maxhp, buildtime, costpercent);
      SetObjBasePrice(objbase, food, wood, stone, gold, iron, coal);
   end;

   procedure SetObjBaseProtection(var objbase : TObjBase; pike, sword, bullet, cannister, arrow, cannonball : Integer);
   begin
      objbase.protection[gc_obj_weapon_kind_pike] := pike;
      objbase.protection[gc_obj_weapon_kind_sword] := sword;
      objbase.protection[gc_obj_weapon_kind_bullet] := bullet;
      objbase.protection[gc_obj_weapon_kind_cannister] := cannister;
      objbase.protection[gc_obj_weapon_kind_arrow] := arrow;
      objbase.protection[gc_obj_weapon_kind_cannonball] := cannonball;
   end;

   procedure SetObjBaseWeapon(var objprop : TObjProp; var objbase : TObjBase; index, damage, pause, radiusmin, radiusmax, detectradiusmin, detectradiusmax, kind : Integer; bSearchMinAttackRadius : Boolean);
   begin
      objprop.weapon[index].enabled := True;
      if (damage<>-1) then
      begin
         objbase.weapon[index].damage := damage;
         objbase.weapon[index].damageinit := damage;
      end;
      if (radiusmin<>-1) then
      objprop.weapon[index].radiusmin := _misc_PixelsToTiles(radiusmin);
      if (radiusmax<>-1) then
      objbase.weapon[index].radiusmax := _misc_PixelsToTiles(radiusmax);
      if (detectradiusmin<>-1) then
      objprop.weapon[index].detectradiusmin := _misc_PixelsToTiles(detectradiusmin);
      if (detectradiusmax<>-1) then
      objprop.weapon[index].detectradiusmax := _misc_PixelsToTiles(detectradiusmax);
      if (kind<>-1) then
      objprop.weapon[index].kind := kind;
      if (pause<>-1) then
      objbase.weapon[index].pause := _misc_FramesToTime(pause);
      if (bSearchMinAttackRadius) then
      objprop.minattackradius := _unit_GetMinAttackRadiusByObjProp(objprop);

      // new mechanics gbool_use_nomeleeminradius
      if (gbool_use_nomeleeminradius) and (objprop.weapon[index].radiusmin<gc_unit_meleeattackradius) then
      objprop.weapon[index].radiusmin := 0;
   end;

   procedure SetObjBaseMaterialCanKill(var objprop : TObjProp; material, ck0, ck1, ck2, ck3, ck4 : Integer);
   begin
      objprop.artind := gc_obj_artind_none;
      objprop.material := material;
      var i : Integer;
      for i:=0 to gc_obj_material_count-1 do
      begin
         var ck : Integer;
         case i of
            0 : ck := ck0;
            1 : ck := ck1;
            2 : ck := ck2;
            3 : ck := ck3;
            4 : ck := ck4;
         end;
         if (ck=0) then
         objprop.cankill[ck] := False
         else
         if (ck>0) then
         objprop.cankill[ck] := True;
      end;
   end;

   procedure SetObjBaseSearchBuildVisionScore(var objprop : TObjProp; var objbase : TObjBase; searchradius : Integer; buildtime : Float; vision, score : Integer);
   begin
      if (searchradius<>-1) then
      objprop.searchradius := _misc_PixelsToTiles(searchradius);
      if (buildtime<>-1) then
      objbase.buildtime := _misc_FramesToTime(buildtime);
      if (vision<>-1) then
      objprop.vision := vision;
      if (score<>-1) then
      objprop.score := score;
   end;

   function CanSetRallyPointByObjBase(var objprop : TObjProp) : Boolean;
   begin
      Result := (objprop.peasantabsorber>0);
      if (not Result) and (objprop.bbuilding) and (objprop.usage<>gc_obj_usage_mill) then
      begin
         var fixedproduceInd : Integer = _country_GetFixedProduceIndexBySID(incid, objprop.sid, False);
         Result := (fixedproduceInd>=0);
      end;
   end;

   const default = -1;
   const bCapture = True;
   const cWalkIntervalDonkey = 1.4;
   const cWalkIntervalInf = 1.25;
   const cWalkIntervalHardHorse = 1.25;
   const cWalkIntervalFastHorse = 1;

   var sid : String = gCountry[incid].members[inid];
   if (sid<>'') then
   begin
      // присвоение id
      objprop.sid := sid;
      objbase.sid := sid;
      var id : Integer = _unit_ConvertObjSIDToID(incid, sid);
      if (inid<>id) then
      ErrorLog('_unit_InitBase inid<>id sid='+sid+' inid='+IntToStr(inid)+' id='+IntToStr(id));
      objprop.id := id;

      // флаг наемник
      var bmercenary : Boolean;
      if StrExists(sid, 'dip') and ((sid='roundshierdip') or (sid='lightinfantrydip') or (sid='archerdip') or (sid='grenadierdip') or (sid='cossacksichdip') or (sid='dragoon18dip') or (sid='archerturdip') or (sid='lightcavalrydip')) then
      bmercenary := True;

      // обнуление параметров, полученных от апгрейдов в прошлой партии
      objbase.shield := 0;

      // получаем национальный идентификатор юнита, из 3х последних символов. идентификтор является вспомогательным для юнитов и обязательным для уникальных построек нации.
      var nation : String;
      if (StrLength(sid)>3) then
      nation := SubStr(sid, StrLength(sid)-2, 3);

      var bCheckBuilding : Boolean;

      // setup юнитов
      case objprop.sid of
         // INFANTRY
         'pikeman', 'pikemanpol', 'pikemantur', 'pikemanrus', 'pikemansco', 'pikemanpor', 'pikemanspa', 'pikemanswi' : begin
            objbase.maxhp := 90;
            SetObjBaseWeapon(objprop, objbase, 0, 8, 0, 35, 100, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 25, 0, 0, 3, 20, 0);
            SetObjBaseProtection(objbase, 3, 2, 4, 210, {7}6, 40);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 144, 1, 10);
            objprop.bstandground := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.aiforce := 10;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'pol' : begin
                  objbase.maxhp := 90;
                  SetObjBaseWeapon(objprop, objbase, 0, 8, default, default, 110, default, default, default, True);
                  SetObjBasePrice(objbase, 25, 0, 0, 1, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 96, default, default);
               end;
               'tur', 'alg' : begin
                  objbase.maxhp := 95;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, default, default, 110, default, default, default, True);
                  SetObjBasePrice(objbase, 55, 0, 0, 5, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 176, default, default);
               end;
               'rus' : begin
                  objbase.maxhp := 85;
                  SetObjBaseWeapon(objprop, objbase, 0, 8, default, 45, 90, default, default, default, True); // c1 damage 9, max range was 100, hp was 100. it have much greater attack speed then in c1
                  SetObjBasePrice(objbase, 45, 0, 0, 4, 15, 0);
                  SetObjBaseProtection(objbase, 2, 1, 4, 140, 4, 25);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 176, default, default);
               end;
               'sco' : begin
                  objbase.maxhp := 100;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, default, default, default, default, default, default, True);
                  SetObjBasePrice(objbase, 35, 0, 0, 2, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 128, 2, default);
               end;
               'spa' : begin
                  objbase.maxhp := 100;
                  SetObjBaseWeapon(objprop, objbase, 0, 10, default, default, default, default, default, default, True);
                  SetObjBasePrice(objbase, 35, 0, 0, 7, 30, 0);
                  SetObjBaseProtection(objbase, 3, 4, 6, 240, 12, 50);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 176, default, default);
               end;
               'swi' : begin
                  objbase.maxhp := 90;
                  SetObjBaseWeapon(objprop, objbase, 0, 10, default, default, default, default, default, default, True);
                  SetObjBasePrice(objbase, 40, 0, 0, 6, 20, 0);
                  SetObjBaseProtection(objbase, 3, 3, 6, 220, {7}6, 45);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 5*32, default, default);
               end;
               'por' : begin
                  objbase.maxhp := 100;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, default, default, default, default, default, default, True);
                  SetObjBasePrice(objbase, 40, 0, 0, 4, 5, 0);
                  SetObjBaseProtection(objbase, 0, 1, 1, 25, 4, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 128{144}, default, default);
               end;
            end;
         end;
         'pikeman18', 'pikeman18swe' : begin
            objbase.maxhp := 85;
            SetObjBaseWeapon(objprop, objbase, 0, 9, 0, 45, 100, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 30, 0, 0, 2, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 40, 2, 10);
            objprop.bstandground := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.aiforce := 10;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'swe' : begin
                  objbase.maxhp := 110;
                  SetObjBaseWeapon(objprop, objbase, 0, 11, default, 35, default, default, default, default, True);
                  SetObjBasePrice(objbase, 40, 0, 0, 3, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 48, 1, 10);
               end;
            end;
         end;
         'lightinfantry', 'lightinfantrydip' : begin
            objbase.maxhp := 55;
            SetObjBaseWeapon(objprop, objbase, 0, 5, 0, 15, 50, 0, 100000, gc_obj_weapon_kind_sword, True);
            SetObjBasePrice(objbase, 25, 0, 0, 0, 1, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 32, 2, 3);
            objprop.bstandground := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.walkintervalfactor := cWalkIntervalInf;
            if (bmercenary) then
            begin
               objbase.maxhp := 50;
               SetObjBaseWeapon(objprop, objbase, 0, 16, default, default, default, default, 50000, default, True);
               SetObjBasePrice(objbase, 0, 0, 0, 4, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 40, 1, 1);
               objprop.bnohungry := True;
               objprop.bmercenary := True;
               objprop.consume[gc_resource_type_gold] := 4;
               objprop.aiforce := 2;
            end
            else
            objprop.aiforce := 3;
         end;
         'roundshier', 'roundshierdip', 'swordsmansco' : begin
            objbase.maxhp := 100;
            SetObjBaseWeapon(objprop, objbase, 0, 6, 0, 25, 60, 0, 50000, gc_obj_weapon_kind_sword, True);
            SetObjBasePrice(objbase, 20, 0, 0, 3, 25, 0);
            SetObjBaseProtection(objbase, 3, 3, 7, 225, 16, 80);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 128, 1, 7);
            objprop.bstandground := True;
            objprop.walkintervalfactor := cWalkIntervalInf;
            objprop.usage := gc_obj_usage_lightinfantry;
            case nation of
               'sco' : begin
                  objbase.maxhp := 180;
                  objprop.walkintervalfactor := 1.12;
                  SetObjBasePrice(objbase, 110, 5, 0, 10, 0, 0);
                  SetObjBaseProtection(objbase, 1, 2, 2, 110, 6, 10);
                  SetObjBaseWeapon(objprop, objbase, 0, 10, default, default, default, default, default, default, True);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 224, 2, default);
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.5);
               end;
            end;
            if (bmercenary) then
            begin
               objbase.maxhp := 75{45}; // in c1 was 35
               SetObjBaseWeapon(objprop, objbase, 0, 6, default, default, default, default, 50000, default, True);
               SetObjBasePrice(objbase, 0, 0, 0, 12, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 48, default, 1);
               SetObjBaseProtection(objbase, 5, 3, 8, 225, {19}17, 80); // in c1 protection was same as unupgraded unit
               objprop.bnohungry := True;
               objprop.bmercenary := True;
               objprop.consume[gc_resource_type_gold] := 20;
               objprop.aiforce := 2;
            end
            else
            objprop.aiforce := 5;
         end;
         'musketeer', 'musketeerpol', 'musketeernet', 'pandur', 'chasseur', 'highlander', 'serdiuk', 'jannisary', 'musketeersco', 'gauduk', 'pandurhun', 'jagerpor', 'jagerswi' : begin
            objbase.maxhp := 70;
            var weapInd : Integer = 1;
            SetObjBaseWeapon(objprop, objbase, weapInd, 12, 150, 400, 800, 0, 100000, gc_obj_weapon_kind_bullet, True);
            objprop.weapon[weapInd].weaponsid := 'SHOTMUSKET';
            objprop.weapon[weapInd].cost[gc_resource_type_iron] := 2;
            objprop.weapon[weapInd].cost[gc_resource_type_coal] := 4;
            objprop.weapon[weapInd].fxshot := 'shotmusket';
            objprop.weapon[weapInd].addradius := _misc_PixelsToTiles(32);
            SetObjBasePrice(objbase, 45, 0, 0, 6, 5, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 192, 2, 20);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_shooter;
            objprop.aiforce := 3;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case objprop.sid of
               'musketeerpol' : begin
                  objbase.maxhp := 70;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 9, 100, 300, 700, default, default, default, True);
                  SetObjBasePrice(objbase, 40, 0, 0, 3, 3, 0);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 1;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 2;
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 144, 1, default);
               end;
               'musketeernet' : begin
                  objbase.maxhp := 65;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 10, 120, default, default, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 1;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 3;
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 160, default, default);
                  SetObjBasePrice(objbase, 50, 0, 0, 8, 4, 0);
               end;
               'pandur' : begin
                  objbase.maxhp := 85;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 17, 150, default, 900, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 6;
                  SetObjBasePrice(objbase, 40, 0, 0, 15, 10, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 5.5*gc_time_to_frames, default, 25);
                  objprop.aiforce := 25;
               end;
               'chasseur' : begin
                  objbase.maxhp := 75;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 20, 190, 500, 1050, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 8;
                  SetObjBasePrice(objbase, 50, 0, 0, 45, 15, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1050, 7.5*gc_time_to_frames, 4, 35);
                  objprop.aiforce := 10;
               end;
               'highlander' : begin
                  objbase.maxhp := 130;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 16, 160, 300, 850, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 4;
                  SetObjBasePrice(objbase, 90, 0, 0, 25, 10, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 6.5*gc_time_to_frames, 1, default);
                  objprop.aiforce := 10;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.33);
               end;
               'serdiuk' : begin
                  objbase.maxhp := 85;
                  var weapInd : Integer = 1;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 12, 130, 400, 900, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 6;
                  SetObjBasePrice(objbase, 60, 0, 0, 11, 5, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 352, default, 12);
                  objprop.aiforce := 2;
               end;
               'jannisary' : begin
                  objbase.maxhp := 65;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 12, 150, 400, 850, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 5;
                  SetObjBasePrice(objbase, 55, 0, 0, 13, 5, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 256, 1, default);
                  objprop.aiforce := 7;
               end;
               'musketeersco' : begin
                  objbase.maxhp := 90;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 12, 150, 350, 850, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 5;
                  SetObjBasePrice(objbase, 55, 0, 0, 8, 7, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 224, default, default);
                  objprop.aiforce := 15;
               end;
               'gauduk' : begin
                  objbase.maxhp := 60;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 9, 100, 300, 750, default, default, default, True);
                  SetObjBasePrice(objbase, 35, 0, 0, 4, 4, 0);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 1;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 2;
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 750, 144, 2, default);
               end;
               'pandurhun' : begin
                  objbase.maxhp := 75;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 19, 160, default, 1000, default, default, default, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 7;
                  SetObjBasePrice(objbase, 30, 0, 0, 25, 10, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 6.5*gc_time_to_frames, 3, 25);
                  objprop.aiforce := 25;
               end;
               'jagerpor' : begin
                  objbase.maxhp := 50;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 10, 190, default, 800, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 4;
                  SetObjBasePrice(objbase, 30, 0, 0, 2, 5, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 2.25*gc_time_to_frames, 1, 10);
                  objprop.aiforce := 10;
               end;
               'jagerswi' : begin
                  objbase.maxhp := 65;
                  SetObjBaseWeapon(objprop, objbase, weapInd, 20, 220, 500, 1200, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[weapInd].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[weapInd].cost[gc_resource_type_coal] := 9;
                  SetObjBasePrice(objbase, 40, 0, 0, 70, 20, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1200, 8.5*gc_time_to_frames, 4, 25);
                  objprop.aiforce := 25;
               end;
            end;
         end;
         'musketeerspa', 'musketeeraus', 'strelet' : begin
            objbase.maxhp := 50;
            SetObjBaseWeapon(objprop, objbase, 0, 9, 90, 400, 800, 0, 100000, gc_obj_weapon_kind_bullet, True);
            objprop.weapon[0].weaponsid := 'SHOTMUSKET';
            objprop.weapon[0].cost[gc_resource_type_iron] := 2;
            objprop.weapon[0].cost[gc_resource_type_coal] := 4;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objprop.weapon[0].fxshot := 'shotmusket';
            SetObjBasePrice(objbase, 45, 0, 0, 6, 10, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 600, 1, 20);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_shooter;
            objprop.aiforce := 3;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case objprop.sid of
               'musketeeraus' : begin
                  objbase.maxhp := 55;
                  SetObjBaseWeapon(objprop, objbase, 0, 12, 160, 400, 800, default, default, default, True);
                  SetObjBasePrice(objbase, 35, 0, 0, 9, 15, 0);
                  SetObjBaseProtection(objbase, 2, 2, 5, 165, 5, 35);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 208, default, 20);
               end;
               'musketeerspa' : begin
                  objbase.maxhp := 85;
                  SetObjBaseWeapon(objprop, objbase, 0, 15, 190, 400, 850, default, default, default, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 6;
                  SetObjBasePrice(objbase, 40, 0, 0, 12, 20, 0);
                  SetObjBaseProtection(objbase, 3, 2, 5, 210, 7, 40);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 240, default, default);
               end;
               'strelet' : begin
                  objbase.maxhp := 85;
                  SetObjBaseWeapon(objprop, objbase, 0, 12, 150, 200, 700, default, default, default, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 4;
                  SetObjBasePrice(objbase, 70, 0, 0, 7, 9, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 272, default, default);
               end;
            end;
         end;
         'musketeer18', 'musketeer18pru', 'musketeer18bav', 'musketeer18den', 'musketeer18sax' : begin
            objbase.maxhp := 100;
            SetObjBaseWeapon(objprop, objbase, 0, 10{18}, 0, 35, 65, 0, 400, gc_obj_weapon_kind_pike, True);
            objprop.weapon[0].attmask := objprop.weapon[0].attmask or (1 shl gc_obj_material_body);
            objprop.weapon[0].attmask := objprop.weapon[0].attmask or (1 shl gc_obj_material_woodwall);
            SetObjBaseWeapon(objprop, objbase, 1, 16, 150, 400, 900, 400, 50000, gc_obj_weapon_kind_bullet, False);
            objprop.weapon[1].weaponsid := 'SHOTMUSKET';
            objprop.weapon[1].cost[gc_resource_type_iron] := 2;
            objprop.weapon[1].cost[gc_resource_type_coal] := 3;
            objprop.weapon[1].fxshot := 'shotmusket';
            objprop.weapon[1].addradius := _misc_PixelsToTiles(32);
            SetObjBasePrice(objbase, 50, 0, 0, 40, 40, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 144, 2, 20);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_shooter;
            objprop.aiforce := 20;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'pru' : begin
                  SetObjBaseWeapon(objprop, objbase, 0, 10{22}{was 24}, default, default, 85, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 22{55}, 150{165}, default, 950, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3{1};
                  objprop.weapon[1].cost[gc_resource_type_coal] := 4{1};
                  SetObjBasePrice(objbase, 70, 0, 0, 80{200;240}, 40, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 950, 192, default, default);
               end;
               'bav' : begin
                  SetObjBaseWeapon(objprop, objbase, 0, 5{9}, default, default, 85, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 22{55}, 190, 300, 950, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 4;
                  SetObjBasePrice(objbase, 60, 0, 0, 55{35;160}, 35{45}, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 950, 160{225;180}, default, default);
               end;
               'sax' : begin
                  objbase.maxhp := 90;
                  SetObjBaseWeapon(objprop, objbase, 0, 7{12}, default, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 19, 140, default, default, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 3;
                  SetObjBasePrice(objbase, 40, 0, 0, 45{35;45}, 40{45}, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 144{225;144}, default, default);
               end;
               'den' : begin
                  objbase.maxhp := 100;
                  SetObjBaseWeapon(objprop, objbase, 0, 8{18;10}, default, 35, 85, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 29{60;40}, {c1 was 150}190, 400, 900, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 5;
                  SetObjBasePrice(objbase, 50, 0, 0, 80, 40, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 176{150;225;192}, 2, default);
               end;
            end;
         end;
         'archer', 'archerdip', 'archertur', 'archerturdip', 'archersco', 'archerscodip' : begin
            objbase.maxhp := 40;
            SetObjBaseWeapon(objprop, objbase, 0, 15{199}, 75{295;160}, 400{300;350}, 700+100{c1 500}, 0, 100000, gc_obj_weapon_kind_arrow, True); // c3 added range +100 cause of uniqrnd range dispertion
            objprop.weapon[0].weaponsid := 'STRELA';
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(175);
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objprop.weapon[0].cost[gc_resource_type_wood] := 1;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            SetObjBaseWeapon(objprop, objbase, 1, 150, 125, 400{300}, 600{c1 500}, 0, 100000, gc_obj_weapon_kind_firearrow, False); // c3 added range +100 cause of uniqrnd range dispertion
            objprop.weapon[1].weaponsid := 'OSTRELA';
            objbase.weapon[1].dispertion := _misc_PixelsToTiles(200);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_building);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_wood);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_woodwall);
            objprop.weapon[1].cost[gc_resource_type_wood] := 1;
            objprop.weapon[1].addradius := _misc_PixelsToTiles(32);
            SetObjBasePrice(objbase, 20, 2, 0, 1, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 1.5*32{25;32}, 2, 15);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_archer;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'tur' : begin
                  objbase.maxhp := 65;
                  SetObjBaseWeapon(objprop, objbase, 0, 20{12}, 85, 300, 900, default, default, default, True);
                  objbase.weapon[0].dispertion := _misc_PixelsToTiles(100);
                  SetObjBaseWeapon(objprop, objbase, 1, 150, 140, 450, 900, default, default, default, False);
                  SetObjBasePrice(objbase, 45, 3, 0, 4, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 3*32, 1, default);
               end;
               'sco' : begin
                  objbase.maxhp := 150;
                  SetObjBaseWeapon(objprop, objbase, 0, 20{12}, 100, 250, 1000, default, default, default, True);
                  objbase.weapon[0].dispertion := _misc_PixelsToTiles(150);
                  objprop.weapon[0].cost[gc_resource_type_wood] := 2;
                  SetObjBaseWeapon(objprop, objbase, 1, 150, 140, 250, 1000, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_wood] := 2;
                  SetObjBasePrice(objbase, 80, 5, 0, 7, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 192, default, default);
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.33);
               end;
            end;
            if (bmercenary) then
            begin
               objbase.maxhp := 20;
               SetObjBaseWeapon(objprop, objbase, 0, 25, 80{60}, 350, {650}700, 0, 100000, gc_obj_weapon_kind_arrow, True);
               objbase.weapon[0].dispertion := _misc_PixelsToTiles(200);
               SetObjBaseWeapon(objprop, objbase, 1, 100, 25, 300, 750, 0, 100000, gc_obj_weapon_kind_firearrow, False);
               objbase.weapon[0].dispertion := _misc_PixelsToTiles(200);
               SetObjBasePrice(objbase, 0, 0, 0, 15, 0, 0);
               SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 650, 40, 1, 1);
               objprop.costpercent := 100.5;
               objprop.weapon[0].cost[gc_resource_type_wood] := 1;
               objprop.weapon[1].cost[gc_resource_type_wood] := 1;
               objprop.consume[gc_resource_type_gold] := 16;
               objprop.bmercenary := True;
               objprop.bnohungry := True;
               objprop.aiforce := 2;
            end
            else
            objprop.aiforce := 2;
         end;
         'officer', 'officerrus', 'officertur', 'officersco' : begin
            objbase.maxhp := 125;
            SetObjBaseWeapon(objprop, objbase, 0, 30, 0, 35, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 50, 0, 0, 150, 30, 0);
            SetObjBaseProtection(objbase, 2, 2, 5, 200, 10, 30);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 320, 2, 10);
            objprop.consume[gc_resource_type_gold] := 60{25};
            objprop.bstandground := True;
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.bofficer := True;
            objprop.aiforce := 2;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'rus' : begin
                  SetObjBasePrice(objbase, 100, 0, 0, 125, 5, 0);
                  SetObjBaseWeapon(objprop, objbase, 0, 40, 0, 35, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 400, 1, 10);
               end;
               'tur' : begin
                  SetObjBasePrice(objbase, 50, 0, 0, 100, 0, 0);
                  SetObjBaseWeapon(objprop, objbase, 0, 30, 0, 35, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 240, 3, default);
                  objprop.consume[gc_resource_type_gold] := 50;
               end;
               'sco' : begin
                  objbase.maxhp := 150;
                  SetObjBaseWeapon(objprop, objbase, 0, 40, 0, 35, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 130, 10, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 320, 2, default);
                  objprop.consume[gc_resource_type_gold] := 70;
               end;
            end;
         end;
         'officer18' : begin
            objbase.maxhp := 125;
            SetObjBaseWeapon(objprop, objbase, 0, 50, 0, 35, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 50, 0, 0, 200, 10, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 192{150}, 4, 10);
            objprop.consume[gc_resource_type_gold] := 90;
            objprop.bstandground := True;
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.bofficer := True;
            objprop.aiforce := 2;
            objprop.walkintervalfactor := cWalkIntervalInf;
         end;
         'drummer', 'drummerrus', 'drummertur', 'bagpiper', 'drummer18' : begin
            objbase.maxhp := 75;
            SetObjBasePrice(objbase, 60{50}, 0, 0, 20, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, 0, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 160, 6, 10);
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.bdrummer := True;
            objprop.aiforce := 2;
            objprop.walkintervalfactor := cWalkIntervalInf;
            if (objprop.sid='bagpiper') then
            begin
               objbase.maxhp := 150{95};
               SetObjBasePrice(objbase, 120, 0, 0, 20, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 224, 5, default);
            end
            else
            if (objprop.sid='drummer18') then
            begin
               objbase.maxhp := 100;
               SetObjBasePrice(objbase, 50, 0, 0, 30, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 192, 8, default);
            end
            else
            case nation of
               'rus' : begin
                  objbase.maxhp := 100;
                  SetObjBasePrice(objbase, 90, 0, 0, 15, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 192, 5, default);
               end;
               'tur' : begin
                  objbase.maxhp := 50;
                  SetObjBasePrice(objbase, 30, 0, 0, 15, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 128, 7, default);
               end;
            end;
         end;
         'priest', 'pope', 'mullah', 'padre' : begin
            objbase.maxhp := 100; // 100
            const cBuildTimeMod = 0.75; // changes in c3.
            SetObjBaseWeapon(objprop, objbase, 0, 5*4, 0, 0, 400{300}, 0, 70000, gc_obj_weapon_kind_heal, True); // +damage, changes in c3 +radius*1.333
            SetObjBasePrice(objbase, 60, 0, 0, 25, 0, 0); // 50, 5
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 400{300*1.5}, 640, 1, 10);
            objprop.bpriest := True;
            objprop.aiforce := 1;
            objprop.walkintervalfactor := cWalkIntervalInf;
            //test
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.consume[gc_resource_type_gold] := 20;
            if (objprop.sid='pope') then
            begin
               objbase.maxhp := 75; // 65
               SetObjBaseWeapon(objprop, objbase, 0, 25{28}, default, default, 350, default, default, gc_obj_weapon_kind_heal, True); // +damage, changes in c3
               SetObjBasePrice(objbase, 40, 0, 0, 20, 0, 0); // 50 15
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 500{400}, 640, 1, 10);
            end
            else
            if (objprop.sid='mullah') then
            begin
               objbase.maxhp := 75{125}; //75
               SetObjBaseWeapon(objprop, objbase, 0, 15{16}, default, default, 500, default, default, gc_obj_weapon_kind_heal, True); // +damage, changes in c3
               SetObjBasePrice(objbase, 30, 0, 0, 10, 0, 0); // 50
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 500{400}, 480, 1, 10);
               objprop.consume[gc_resource_type_gold] := 15;
            end
            else
            if (objprop.sid='padre') then
            begin
               objbase.maxhp := 90;
               SetObjBaseWeapon(objprop, objbase, 0, 30, default, default, 400, default, default, gc_obj_weapon_kind_heal, True); // +damage, changes in c3
               SetObjBasePrice(objbase, 50, 0, 0, 40, 0, 0); // 50 15
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 400{400}, 25*32, 1, 10);
               objprop.consume[gc_resource_type_gold] := 40;
            end;
         end;
         'peaaus', 'peatur', 'pearus', 'peapol', 'peaspa', 'peaeng', 'peaukr', 'peasco' : begin
            objbase.maxhp := 50;
            objbase.speed := gc_obj_speed_peasant;
            SetObjBaseWeapon(objprop, objbase, 0, 20, 0, 10, 65{50}, 0, 50000, gc_obj_weapon_kind_sword, True);
            SetObjBasePrice(objbase, 100, 0, 0, 0, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 300, 400, 1, 20);
            objprop.consume[gc_resource_type_food] := 32;
            objprop.usage := gc_obj_usage_peasant;
            objprop.bcapture := True;
            objprop.aiforce := 1;
            objprop.walkintervalfactor := cWalkIntervalInf;
            case nation of
               'aus' : begin
                  objbase.maxhp := 50{100};
               end;
               'rus' : begin
                  objbase.maxhp := 50;
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 400, 3, default);
                  objprop.consume[gc_resource_type_food] := 26;
               end;
               'ukr' : begin
                  objbase.maxhp := 75{60};
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 360, 4, default);
                  objprop.bcapture := False;
               end;
               'sco' : begin
                  objbase.maxhp := 60;
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 400, 400, 1, default);
                  objprop.bcapture := False;
               end;
               'alg', 'tur' : begin
                  objprop.consume[gc_resource_type_food] := 28;
               end;
            end;
         end;
         'grenadier', 'grenadierdip', 'grenadierden', 'grenadierpru', 'grenadierbav', 'grenadiersax', 'grenadierhun' : begin
            objbase.maxhp := 120{100};
            SetObjBaseWeapon(objprop, objbase, 0, 18{22;20}, 0, 25, 80, 0, 400, gc_obj_weapon_kind_pike, True);
            SetObjBaseWeapon(objprop, objbase, 1, 16{12}, 170{90}, 400, 900, 400, 50000, gc_obj_weapon_kind_bullet, False);
            objprop.weapon[1].weaponsid := 'SHOTMUSKET';
            objprop.weapon[1].cost[gc_resource_type_iron] := 2;
            objprop.weapon[1].cost[gc_resource_type_coal] := 3;
            objprop.weapon[1].fxshot := 'shotmusket';
            objprop.weapon[1].addradius := _misc_PixelsToTiles(32);
            SetObjBaseWeapon(objprop, objbase, 2, 110, 75, 200, 500, 0, 50000, gc_obj_weapon_kind_mortarball, False);
            objprop.weapon[2].weaponsid := 'NUCLGRE';
            objbase.weapon[2].dispertion := _misc_PixelsToTiles(250);
            objprop.weapon[2].attmask := objprop.weapon[2].attmask or (1 shl gc_obj_material_building);
            objprop.weapon[2].attmask := objprop.weapon[2].attmask or (1 shl gc_obj_material_wood);
            objprop.weapon[2].attmask := objprop.weapon[2].attmask or (1 shl gc_obj_material_woodwall);
            SetObjBasePrice(objbase, 80, 0, 0, 60, 40, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 192{150;180}, 2, 20);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_grenadier;
            objprop.walkintervalfactor := cWalkIntervalInf;
            objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.2);
            case objprop.sid of
               'grenadierden' : begin
                  objbase.maxhp := 125;
                  SetObjBaseWeapon(objprop, objbase, 0, 22{25;23}, 0, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 19{25;23}, 190, 450, default, default, default, default, True);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 3;
                  SetObjBaseWeapon(objprop, objbase, 2, default, default, default, default, default, default, default, False);
                  SetObjBasePrice(objbase, 100, 0, 0, 90, 40, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 208{150;180}{c1 was 135}, default, default);
               end;
               'grenadierpru' : begin
                  objbase.maxhp := 125;
                  SetObjBaseWeapon(objprop, objbase, 0, 18{28;25}, 0, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 16, 140, default, default, default, default, default, False);
                  SetObjBaseWeapon(objprop, objbase, 2, default, default, default, default, default, default, default, False);
                  SetObjBasePrice(objbase, 90, 0, 0, 100, 45, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 224, default, default);
               end;
               'grenadierbav' : begin
                  objbase.maxhp := 125;
                  SetObjBaseWeapon(objprop, objbase, 0, 14{20;15}, 0, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 19{14;15}, default, 300, default, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 3;
                  SetObjBaseWeapon(objprop, objbase, 2, default, default, default, default, default, default, default, False);
                  SetObjBasePrice(objbase, 95, 0, 0, 70, 40, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 192{150;180}, default, default);
               end;
               'grenadiersax' : begin
                  objbase.maxhp := 100;
                  SetObjBaseWeapon(objprop, objbase, 0, 22{25}, 0, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 19{15}, default, default, 950, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 3;
                  SetObjBaseWeapon(objprop, objbase, 2, default, default, default, default, default, default, default, False);
                  SetObjBasePrice(objbase, 50, 0, 0, 60, 40, 0);
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 950, 192{135;150}, default, default);
               end;
               'grenadierhun' : begin
                  objbase.maxhp := 125;
                  SetObjBaseWeapon(objprop, objbase, 0, 30, 0, default, default, default, default, default, True);
                  SetObjBaseWeapon(objprop, objbase, 1, 16, 170, 500, 900, default, default, default, False);
                  objprop.weapon[1].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[1].cost[gc_resource_type_coal] := 3;
                  SetObjBaseWeapon(objprop, objbase, 2, default, 90, default, 600, default, default, default, False);
                  SetObjBasePrice(objbase, 90, 0, 0, 80, 40, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 208, 2, default);
               end;
            end;
            if (bmercenary) then
            begin
               objbase.maxhp := 30;
               SetObjBaseWeapon(objprop, objbase, 0, 30, default, default, default, default, 400, gc_obj_weapon_kind_pike, True); // in c1 detect radiusmax was 500
               SetObjBaseWeapon(objprop, objbase, 1, 16{4;15}, 150{120}, 400, 800, 400, default, gc_obj_weapon_kind_bullet, False); // in c1 pause was 180 detect radiusmin was 500
               objprop.weapon[1].cost[gc_resource_type_iron] := 2;
               objprop.weapon[1].cost[gc_resource_type_coal] := 5;
               SetObjBaseWeapon(objprop, objbase, 2, 200, 100, 140, {370}400, default, default, default, False); // c1 dmg was 110, attack pause was 75
               SetObjBasePrice(objbase, 0, 0, 0, 25, 0, 0);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 48, 1, 2);
               objprop.costpercent := 100.5;
               objprop.bnohungry := True;
               objprop.bmercenary := True;
               objprop.consume[gc_resource_type_gold] := 60;
               objprop.aiforce := 3;
            end
            else
            objprop.aiforce := 20;
         end;
         // CAVALRY
         'croat', 'hussar', 'hussarpru', 'wingedhussar', 'cossacksich', 'cossacksichdip', 'hackapell', 'raidersco', 'hussarhun', 'hussarswi' : begin
            objbase.maxhp := 200+30;
            objbase.speed := gc_obj_speed_fasthorse;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_horse);
            SetObjBaseWeapon(objprop, objbase, 0, 9, 0, 25, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 75, 0, 0, 2, 5, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 528, 3, 40);
            objprop.bstandground := True;
            objprop.usage := gc_obj_usage_fasthorse;
            case objprop.sid of
               'croat' : begin
                  objbase.maxhp := 240+20;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, 0, 25, 65, 0, 100000, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 80, 0, 0, 6, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 15.75*32, 2, 40);
                  objprop.aiforce := 20;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'hackapell' : begin
                  objbase.maxhp := 220+25;
                  SetObjBaseWeapon(objprop, objbase, 0, 12, default, default, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 80, 0, 0, 7, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 18*32{960}, 3, 35);
                  objprop.aiforce := 20;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'hussar' : begin
                  SetObjBaseWeapon(objprop, objbase, 0, 12, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 70, 0, 0, 20, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, 15*32{528;475;570}, default, 35);
                  objprop.aiforce := 15;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'hussarpru' : begin
                  objbase.maxhp := 210+30;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 80, 0, 0, 15, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 11.25*32{384;325;420}, 5{8}, 35);
                  objprop.aiforce := 15;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'wingedhussar' : begin
                  objbase.maxhp := 225;
                  SetObjBaseWeapon(objprop, objbase, 0, 14{13}, default, 20, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 30, 25, 0);
                  SetObjBaseProtection(objbase, 1, 2, 5, 160, 10, 30);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 832{1250}, 2, 50);
                  objprop.aiforce := 50;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
               end;
               'cossacksich', 'cossacksichdip' : begin
                  objbase.maxhp := 250;
                  SetObjBaseWeapon(objprop, objbase, 0, 13, default, 20, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 0, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800{900 in c1}, 432{550}, 3, 35);
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
                  if (bmercenary) then
                  begin
                     objbase.maxhp := 150;
                     objprop.costpercent := 100.5;
                     SetObjBaseWeapon(objprop, objbase, 0, 8, default, 20, default, default, default, gc_obj_weapon_kind_sword, True);
                     SetObjBasePrice(objbase, 0, 0, 0, 60, 0, 0);
                     SetObjBaseSearchBuildVisionScore(objprop, objbase, 700{1200 in c1}, 80{50}, 1, 10);
                     objprop.bnohungry := True;
                     objprop.bmercenary := True;
                     objprop.consume[gc_resource_type_gold] := 150;
                     objprop.aiforce := 3;
                  end
                  else
                  objprop.aiforce := 25;
               end;
               'raidersco' : begin
                  objbase.maxhp := 280;
                  SetObjBaseWeapon(objprop, objbase, 0, 11, default, 20, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 8, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 720, 2, 35);
                  objprop.aiforce := 25;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
               end;
               'hussarhun' : begin
                  objbase.maxhp := 225+25;
                  SetObjBaseWeapon(objprop, objbase, 0, 10, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 100, 0, 0, 30, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 672, 3, 35);
                  objprop.aiforce := 15;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'hussarswi' : begin
                  objbase.maxhp := 240+25;
                  SetObjBaseWeapon(objprop, objbase, 0, 14, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 120, 0, 0, 30, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 19.5*32{384;325;420}, 4{8}, 35);
                  objprop.aiforce := 15;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
            end;
         end;
         'reiter', 'cuirassier', 'reiterswe', 'reiterpol', 'hetman', 'cossackdon', 'vityaz', 'cossackregister', 'spakh', 'mameluke', 'guardcavalrysax', 'lancersco', 'sipahi' : begin
            objbase.maxhp := 300{300};
            objbase.speed := gc_obj_speed_hardhorse;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_horse);
            SetObjBaseWeapon(objprop, objbase, 0, 15, 0, 20, 65, 0, 100000, gc_obj_weapon_kind_pike, True);
            SetObjBasePrice(objbase, 120, 0, 0, 10, 40, 0);
            SetObjBaseProtection(objbase, 2, 6, 6, 190, 14, 40);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 768, 1, 50);
            objprop.bstandground := True;
            objprop.usage := gc_obj_usage_hardhorse;
            objprop.walkintervalfactor := cWalkIntervalHardHorse;
            objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
            case objprop.sid of
               'reiter' : begin
                  objprop.aiforce := 50;
                  // c3 add start
                  SetObjBasePrice(objbase, 120, 0, 0, 10, 40, 0);
                  SetObjBaseProtection(objbase, 2, 6, 6, 190, 15, 40);
                  // c3 add finish
               end;
               'cuirassier' : begin
                  SetObjBaseWeapon(objprop, objbase, 0, 14, default, default, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 120, 0, 0, 35, 25, 0);
                  SetObjBaseProtection(objbase, 2, 4, 10, 160, 5, 80);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 22.5*32, 3, 40);
                  objprop.aiforce := 70;
               end;
               'reiterswe' : begin
                  SetObjBaseWeapon(objprop, objbase, 0, 14, default, default, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 7, 20, 0);
                  SetObjBaseProtection(objbase, 2, 3, 7, 140, 7, 35);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 720, 2, default);
                  objprop.aiforce := 50;
               end;
               'guardcavalrysax' : begin
                  objbase.maxhp := 320;
                  SetObjBaseWeapon(objprop, objbase, 0, 15, default, default, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 140, 0, 0, 50, 20, 0);
                  SetObjBaseProtection(objbase, 2, 5, 9, 150, 9, 70);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 768, 3, default);
                  objprop.aiforce := 50;
               end;
               'reiterpol' : begin
                  objbase.maxhp := 190;
                  SetObjBaseWeapon(objprop, objbase, 0, 9, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 60, 0, 0, 5, 2, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 264{300}, 3, default);
                  objprop.aiforce := 20;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.5);
               end;
               'hetman' : begin
                  objbase.maxhp := 320;
                  SetObjBaseWeapon(objprop, objbase, 0, 70, default, default, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 150, 0, 0, 150, 10, 0);
                  SetObjBaseProtection(objbase, 0, 1, 3, 75, 3, 15);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 528{432}{450}, 7, default);
                  objprop.consume[gc_resource_type_gold] := 200;
                  objprop.aiforce := 70;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*3);
               end;
               'cossackdon' : begin
                  objbase.maxhp := 220;
                  SetObjBaseWeapon(objprop, objbase, 0, 13, default, 30, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 100, 1, 0, 0, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 432{550}, 3, 35);
                  objprop.aiforce := 20;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'vityaz' : begin
                  objbase.maxhp := 380;
                  SetObjBaseWeapon(objprop, objbase, 0, 14, default, 25, default, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 160, 0, 0, 13, {c1 was 35}25, 0);
                  SetObjBaseProtection(objbase, 2, 4, {c1 was 9}3, {c1 was 255}160, {c1 was 5}17, 40);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 816{1500}, 2, default);
                  objprop.aiforce := 50;
               end;
               'cossackregister' : begin
                  objbase.maxhp := 250;
                  SetObjBaseWeapon(objprop, objbase, 0, 12, default, 30, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 70, 0, 0, 15, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 336, 3, 15);
                  objprop.aiforce := 10;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'spakh' : begin
                  objbase.maxhp := 230;
                  SetObjBaseWeapon(objprop, objbase, 0, 15, default, 30, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 80, 0, 0, 6, 5, 0);
                  SetObjBaseProtection(objbase, 0, 1, 0, 10, 2, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 288{325}, 3, 10);
                  objprop.aiforce := 5;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'mameluke' : begin
                  objbase.maxhp := 280;
                  SetObjBaseWeapon(objprop, objbase, 0, 16, default, 30, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 100, 5, 0, 8, 0, 0);
                  SetObjBaseProtection(objbase, 1, 3, 1, 75, 8, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 384, 3, 15);
                  objprop.aiforce := 20;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'lancersco' : begin
                  objbase.maxhp := 320;
                  SetObjBaseWeapon(objprop, objbase, 0, 11, default, 30, 100, default, default, gc_obj_weapon_kind_pike, True);
                  SetObjBasePrice(objbase, 120, 0, 0, 6, 0, 0);
                  SetObjBaseProtection(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 600, 672, 2, 15);
                  objprop.aiforce := 10;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'sipahi' : begin
                  objbase.maxhp := 360;
                  SetObjBaseWeapon(objprop, objbase, 0, 15, default, default, default, default, default, gc_obj_weapon_kind_sword, True);
                  SetObjBasePrice(objbase, 130, 0, 0, 20, 70, 0);
                  SetObjBaseProtection(objbase, 3, 7, 4, 225, 24, 60);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 18*32, 2, 15);
                  objprop.aiforce := 10;
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
               end;
            end;
         end;
         'dragoon', 'kingmusketeer', 'dragoon18fra', 'dragoonpol', 'dragoon18', 'dragoon18net', 'dragoon18pie', 'dragoon18dip', 'lightcavalry', 'lightcavalrydip' : begin
            objbase.maxhp := 220{300};
            objbase.speed := gc_obj_speed_hardhorse;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_horse);
            SetObjBaseWeapon(objprop, objbase, 0, 15{11}, 180{c1 was 200}, 300, 800, 0, 100000, gc_obj_weapon_kind_bullet, True);
            objprop.weapon[0].weaponsid := 'SHOTMUSKET';
            objprop.weapon[0].cost[gc_resource_type_iron] := 2;
            objprop.weapon[0].cost[gc_resource_type_coal] := 4{3};
            objprop.weapon[0].fxshot := 'shotmusketcav';
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            SetObjBasePrice(objbase, 90, 0, 0, 7, 5, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 15*32{624}, 3, 50); // in c1 builttime was 1750
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.usage := gc_obj_usage_horseshooter;
            objprop.walkintervalfactor := cWalkIntervalHardHorse;
            objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
            case objprop.sid of
               'dragoon' : begin
                  objprop.aiforce := 10;
               end;
               'dragoonpol' : begin
                  objbase.maxhp := 185;
                  SetObjBaseWeapon(objprop, objbase, 0, 13{12;10}, 160, 400, 850, default, default, default, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 3;
                  SetObjBasePrice(objbase, 70, 0, 0, 5, 4, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 432, 2, default);
               end;
               'kingmusketeer' : begin
                  objbase.maxhp := 280;
                  SetObjBaseWeapon(objprop, objbase, 0, 43{100}, 220{200}, 200, 700, default, default, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 6;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 10;
                  SetObjBasePrice(objbase, 100, 0, 0, 100, 8, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 864{960}{2000}, 4, 100);
                  objprop.consume[gc_resource_type_gold] := 150;
                  objprop.aiforce := 50;
                  objprop.weapon[0].fxshot := 'shotmusketking';
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
               end;
               'dragoon18fra' : begin
                  objbase.maxhp := 140;
                  SetObjBaseWeapon(objprop, objbase, 0, 10{7}, 150{160}, 350, 800, default, default, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 3;
                  objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
                  SetObjBasePrice(objbase, 50, 0, 0, 30, 6, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 800, 480, 2, 40);
                  //objprop.consume[gc_resource_type_gold] := 150;
                  objprop.aiforce := 25;
                  objprop.weapon[0].fxshot := 'shotmusketdrag';
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*1.5);
               end;
               'dragoon18net' : begin
                  objbase.maxhp := 320;
                  SetObjBaseWeapon(objprop, objbase, 0, 17, 160, 300, 850, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 3;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 4;
                  objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
                  SetObjBasePrice(objbase, 100, 0, 0, 70, 7, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 768{960}{1600}, 3, 40);
                  //objprop.consume[gc_resource_type_gold] := 150;
                  objprop.weapon[0].fxshot := 'shotmusketdrag';
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2.5);
               end;
               'dragoon18pie' : begin
                  objbase.maxhp := 200;
                  SetObjBaseWeapon(objprop, objbase, 0, 19, 160, 400, 900, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 5;
                  objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
                  SetObjBasePrice(objbase, 60, 0, 0, 65, 7, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 20.25*32{960}{1600}, 5, 40);
                  //objprop.consume[gc_resource_type_gold] := 150;
                  objprop.weapon[0].fxshot := 'shotmusketdrag';
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'lightcavalry' : begin
                  objbase.maxhp := 175;
                  SetObjBaseWeapon(objprop, objbase, 0, 14, 170, 400, 1000, 0, 100000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 3;
                  objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
                  SetObjBasePrice(objbase, 90, 0, 0, 50, 6, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 672, 2, 30);
                  //objprop.consume[gc_resource_type_gold] := 150;
                  objprop.weapon[0].fxshot := 'shotmusketdrag';
                  objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
               end;
               'dragoon18', 'dragoon18dip', 'lightcavalrydip' : begin
                  objbase.maxhp := 225{320};
                  SetObjBaseWeapon(objprop, objbase, 0, 19{18}, 170, default, 900, default, default, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 5;
                  SetObjBasePrice(objbase, 70, 0, 0, 60, 7, 0);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 900, 720{1750}, default, 40);
                  //objprop.consume[gc_resource_type_gold] := 85;
                  objprop.weapon[0].fxshot := 'shotmusketdrag';
                  if (bmercenary) then
                  begin
                     objbase.maxhp := 100;
                     SetObjBaseWeapon(objprop, objbase, 0, 18, 72{69}{in c1 was 37}, 350, 800, default, default, gc_obj_weapon_kind_bullet, True);
                     objprop.weapon[0].cost[gc_resource_type_iron] := 5;
                     objprop.weapon[0].cost[gc_resource_type_coal] := 8;
                     SetObjBasePrice(objbase, 0, 0, 0, 120, 0, 0);
                     SetObjBaseSearchBuildVisionScore(objprop, objbase, 850, 64, 1, 25);
                     objprop.costpercent := 102;
                     objprop.consume[gc_resource_type_gold] := 120;
                     objprop.bmercenary := True;
                     objprop.bnohungry := True;
                     objprop.usage := gc_obj_usage_shooter;
                     objprop.aiforce := 10;
                  end
                  else
                  objprop.aiforce := 15;
               end;
            end;
         end;
         'tatar' : begin
            objbase.maxhp := 185{300};
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_horse);
            SetObjBaseWeapon(objprop, objbase, 0, 15, 50, 500, 1100, 0, 100000, gc_obj_weapon_kind_arrow, True);
            objprop.weapon[0].weaponsid := 'STRELA';
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(150);
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objprop.weapon[0].cost[gc_resource_type_wood] := 1;
            SetObjBaseWeapon(objprop, objbase, 1, 140, 150, 500, 1100, 0, 100000, gc_obj_weapon_kind_firearrow, False);
            objprop.weapon[1].weaponsid := 'OSTRELA';
            objbase.weapon[1].dispertion := _misc_PixelsToTiles(150);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_building);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_wood);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_woodwall);
            SetObjBasePrice(objbase, 70, 2, 0, 6, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1100, 11.25*32, 4, 10);
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            //objprop.usage := gc_obj_usage_grenadier;
            objprop.usage := gc_obj_usage_archer;
            objprop.walkintervalfactor := cWalkIntervalHardHorse;
            objprop.aiforce := 4;
            objprop.consume[gc_resource_type_food] := floor(gc_obj_foodperunit*2);
         end;
         // MISC
         'field' : begin // field
            objbase.price[gc_resource_type_gold] := 5;
            objprop.usage := gc_obj_usage_field;
         end;
         // ARTILLERY
         'cannon' : begin
            objbase.maxhp := 9000 {12500}; // c1 = 25000
            objprop.motionstyle := gc_obj_motionstyle_slowrotate;
            objbase.speed := gc_obj_speed_cannon;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_artillery);
            objprop.rotatespeed := gc_obj_rotatespeed_cannon;
            objbase.shield := 75; // C1 was 90
            SetObjBaseWeapon(objprop, objbase, 0, 1800, 350, 550, 2160, 550, 100000, gc_obj_weapon_kind_cannonball, False);
            objprop.weapon[0].weaponsid := 'PPOINTT';
            objprop.weapon[0].cost[gc_resource_type_iron] := 20;
            objprop.weapon[0].cost[gc_resource_type_coal] := 40;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(225);
            objprop.weapon[0].fxshot := 'shotcannon';
            SetObjBaseWeapon(objprop, objbase, 1, 0, 350{1200}{c1 was 1400}, 0, 450, 0, 550, gc_obj_weapon_kind_cannister, True);
            objprop.weapon[1].weaponsid := 'PSMPOINTTPUS';
            objprop.weapon[1].cost[gc_resource_type_iron] := 24;
            objprop.weapon[1].cost[gc_resource_type_coal] := 21;
            objprop.weapon[1].fxshot := 'shotcannon';
            SetObjBasePrice(objbase, 0, 250, 0, 400, 400, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_iron, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 2400{3840}, 1{8}, 50);
            objprop.consume[gc_resource_type_gold] := 300{500};
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bcapture := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.bartprepare := True;
            objprop.bartillery := True;
            objprop.usage := gc_obj_usage_cannon;
            objprop.aiforce := 1;
            objprop.explmedia := gc_obj_explmedia_wood;
            objprop.explradius := _misc_PixelsToTiles(25);
            objprop.artind := gc_obj_artind_cannon;
         end;
         'howitzer' : begin
            objbase.maxhp := 3000{150};
            objprop.motionstyle := gc_obj_motionstyle_slowrotate;
            objbase.speed := gc_obj_speed_howitzer;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_artillery);
            objprop.rotatespeed := gc_obj_rotatespeed_howitzer;
            objbase.shield := 75;
            SetObjBaseWeapon(objprop, objbase, 0, 4000, 600{1250}, 700, 1400{950}, 0, 70000, gc_obj_weapon_kind_cannonball, True); // was gc_obj_weapon_kind_mortarball, min radius was 500
            objprop.weapon[0].weaponsid := 'DIMMORT1';
            objprop.weapon[0].cost[gc_resource_type_iron] := 20;
            objprop.weapon[0].cost[gc_resource_type_coal] := 100;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(300){250};
            objprop.weapon[0].fxshot := 'shothowitzer';
            SetObjBasePrice(objbase, 0, 250, 0, 350, 300, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_iron, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1000, 3008{4800}, 1, 25);
            //objprop.costpercent := 115;
            objprop.consume[gc_resource_type_gold] := 350{50};
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bcapture := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.bartprepare := True; // was false in c1
            objprop.bartillery := True;
            objprop.usage := gc_obj_usage_mortar;
            objprop.aiforce := 1;
            objprop.explmedia := gc_obj_explmedia_wood;
            objprop.explradius := _misc_PixelsToTiles(25);
            objprop.artind := gc_obj_artind_howitzer;
         end;
         'mortar' : begin
            objbase.maxhp := 400{100};
            objprop.motionstyle := gc_obj_motionstyle_slowrotate;
            objbase.speed := gc_obj_speed_mortar;
            objbase.shield := 25;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_artillery);
            objprop.rotatespeed := gc_obj_rotatespeed_mortar;
            SetObjBaseWeapon(objprop, objbase, 0, 200, 250, 1250, 2600, 0, 70000, gc_obj_weapon_kind_mortarball, True);
            objprop.weapon[0].weaponsid := 'DIMMORT2NEW'; // 50% chance not to explode
            //objprop.weapon[0].weaponsid := 'DIMMORT2';
            objprop.weapon[0].cost[gc_resource_type_iron] := 20;
            objprop.weapon[0].cost[gc_resource_type_coal] := 30;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(200){100};
            objprop.weapon[0].fxshot := 'shotmortar';
            SetObjBasePrice(objbase, 0, 100, 0, 75, 200, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_iron, gc_obj_material_woodwall, gc_obj_material_building, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 800, 1, 100);
            objprop.consume[gc_resource_type_gold] := 50;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bcapture := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.bartillery := True;
            objprop.usage := gc_obj_usage_supermortar;
            objprop.explmedia := gc_obj_explmedia_wood;
            objprop.explradius := _misc_PixelsToTiles(5);
            objprop.artind := gc_obj_artind_mortar;
         end;
         'multicannon' : begin
            objbase.maxhp := 2000{1000};
            objbase.speed := gc_obj_speed_multicannon;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_artillery);
            objprop.rotatespeed := gc_obj_rotatespeed_multicannon;
            objbase.shield := 50;
            SetObjBaseWeapon(objprop, objbase, 0, 500, 60, 10, 700, 0, 50000, gc_obj_weapon_kind_cannister, True); // max range in c1 950
            objprop.weapon[0].weaponsid := 'PSMPOINTT';
            objprop.weapon[0].cost[gc_resource_type_iron] := 40;
            objprop.weapon[0].cost[gc_resource_type_coal] := 30;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objprop.weapon[0].fxshot := 'shotmcannon';
            SetObjBasePrice(objbase, 0, 200, 0, 400, 250, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_iron, gc_obj_material_body, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 700, 1600, 1, 25);
            objprop.consume[gc_resource_type_gold] := 300;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bcapture := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.bartillery := True;
            objprop.usage := gc_obj_usage_mcannon;
            objprop.aiforce := 1;
            objprop.explmedia := gc_obj_explmedia_wood;
            objprop.explradius := _misc_PixelsToTiles(25*2);
            objprop.artind := gc_obj_artind_multicannon;
            objbase.protection[gc_obj_weapon_kind_cannister] := 500;
         end;
         'framegun' : begin
            objbase.maxhp := 3000;
            objprop.motionstyle := gc_obj_motionstyle_slowrotate;
            objbase.speed := gc_obj_speed_cannon;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_artillery);
            objprop.rotatespeed := gc_obj_rotatespeed_cannon;
            objbase.shield := 50; // C1 was 90
            SetObjBaseWeapon(objprop, objbase, 0, 500, 90, 200, 1800, 250, 100000, gc_obj_weapon_kind_cannonball, False);
            objprop.weapon[0].weaponsid := 'PPOINTTFRAME';
            objprop.weapon[0].cost[gc_resource_type_iron] := 30;
            objprop.weapon[0].cost[gc_resource_type_coal] := 40;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(250);
            objprop.weapon[0].fxshot := 'shotcannon';
            SetObjBasePrice(objbase, 0, 200, 0, 300, 150, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_iron, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1200, 1600, 1, 50);
            objprop.consume[gc_resource_type_gold] := 300;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bcapture := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.bartprepare := True;
            objprop.bartillery := True;
            objprop.usage := gc_obj_usage_cannon;
            objprop.aiforce := 1;
            objprop.explmedia := gc_obj_explmedia_wood;
            objprop.explradius := _misc_PixelsToTiles(25);
            objprop.artind := gc_obj_artind_multicannon;
         end;
         // SHIPS
         'fishboat' : begin
            objbase.maxhp := 300;
            objbase.speed := gc_obj_speed_fishboat;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_fishboat);
            objprop.rotatespeed := gc_obj_rotatespeed_ships;
            objprop.media := gc_obj_media_water;
            objprop.motionstyle := gc_obj_motionstyle_ship;
            SetObjBasePrice(objbase, 0, 600, 0, 0, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_wood, 0, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 1280, 1, 50);
            objbase.fishingspeed := 50 div 4;
            objbase.fishingmax := 1000;
            objprop.costpercent := 105;
            objprop.bnohungry := True;
            objprop.usage := gc_obj_usage_fisher;
            objprop.explmedia := gc_obj_explmedia_ship;
            objprop.explradius := _misc_PixelsToTiles(25);
         end;
         'yacht', 'yachttur' : begin
            objbase.maxhp := 31000;
            objbase.speed := gc_obj_speed_yacht;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_yacht);
            objprop.rotatespeed := gc_obj_rotatespeed_ships;
            objprop.media := gc_obj_media_water;
            objprop.motionstyle := gc_obj_motionstyle_ship;
            SetObjBaseWeapon(objprop, objbase, 0, 1000, 350, 300, 1100, 0, 50000, gc_obj_weapon_kind_cannonball, True);
            objprop.weapon[0].weaponsid := 'PPOINTTKOR';
            objprop.weapon[0].cost[gc_resource_type_iron] := 4;
            objprop.weapon[0].cost[gc_resource_type_coal] := 9;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(250);
            SetObjBasePrice(objbase, 0, 900, 0, 450, 150, 200);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_wood, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1050, 1536, 3, 100);
            objprop.costpercent := 105;
            objprop.consume[gc_resource_type_gold] := 50;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_yacht;
            objprop.explmedia := gc_obj_explmedia_ship;
            objprop.explradius := _misc_PixelsToTiles({75 in c1}50);
            objprop.bshotdirection := True;
            objprop.bshotforward := False;
            objprop.bshotleftflank := True;
            objprop.bshotrightflank := True;
            objprop.bshotback := False;
            if (objprop.sid='yachttur') then
            begin
               objbase.maxhp := 35000;
               objbase.speed := gc_obj_speed_yachttur;
               objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_yachttur);
               objbase.shield := 100;
               SetObjBaseWeapon(objprop, objbase, 0, 30, 400, default, 1000, 300, 1000, default, True);
               objprop.weapon[0].cost[gc_resource_type_iron] := 5;
               objprop.weapon[0].cost[gc_resource_type_coal] := 15;
               objbase.weapon[0].dispertion := _misc_PixelsToTiles(25);
               SetObjBaseWeapon(objprop, objbase, 1, 0, 700, 1000, 1650, 1000, 100000, default, False);
               objprop.weapon[1].weaponsid := 'PPOINTTKOR';
               objprop.weapon[1].cost[gc_resource_type_iron] := 5;
               objprop.weapon[1].cost[gc_resource_type_coal] := 15;
               objbase.weapon[1].dispertion := _misc_PixelsToTiles(25);
               SetObjBaseSearchBuildVisionScore(objprop, objbase, default, default{300}, 3, 200); // construction speed reduced 10 times then in c1.
               objprop.consume[gc_resource_type_gold] := 1500;
            end;
         end;
         'galley' : begin
            objbase.maxhp := 50000;
            objbase.speed := gc_obj_speed_galley;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_galley);
            objprop.rotatespeed := gc_obj_rotatespeed_ships;
            objprop.media := gc_obj_media_water;
            objprop.motionstyle := gc_obj_motionstyle_ship;
            objbase.shield := 80{100}; // C1 was 100
            SetObjBaseWeapon(objprop, objbase, 0, 100, 150, 300, 1200, 300, 100000, gc_obj_weapon_kind_cannonball, True);
            objprop.weapon[0].weaponsid := 'PPOINTTKOR';
            objprop.weapon[0].cost[gc_resource_type_iron] := 4;
            objprop.weapon[0].cost[gc_resource_type_coal] := 9;
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(25);
            SetObjBaseWeapon(objprop, objbase, 1, 1000, 50, 1100, 3100, 1100, 100000, gc_obj_weapon_kind_mortarball, False);
            objprop.weapon[1].weaponsid := 'DIMMORT2KOR';
            objprop.weapon[1].cost[gc_resource_type_iron] := 4;
            objprop.weapon[1].cost[gc_resource_type_coal] := 9;
            objprop.weapon[1].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[1].dispertion := _misc_PixelsToTiles(200);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_building);
            objprop.weapon[1].attmask := objprop.weapon[1].attmask or (1 shl gc_obj_material_woodwall);
            SetObjBasePrice(objbase, 0, 9500, 0, 900, 800, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_wood, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1100, 1600, 2, 200);
            objprop.consume[gc_resource_type_gold] := 750;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bnohungry := True;
            objprop.usage := gc_obj_usage_galley;
            objprop.explmedia := gc_obj_explmedia_ship;
            objprop.explradius := _misc_PixelsToTiles({100 in c1}75);
            objprop.bshotdirection := True;
            objprop.bshotforward := True;
            objprop.bshotleftflank := False;
            objprop.bshotrightflank := False;
            objprop.bshotback := False;
         end;
         'frigate', 'xebec', 'battleship', 'chaika' : begin
            objbase.maxhp := 50000;
            objbase.speed := gc_obj_speed_frigate;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_frigate);
            objprop.rotatespeed := gc_obj_rotatespeed_ships;
            objprop.media := gc_obj_media_water;
            objprop.motionstyle := gc_obj_motionstyle_ship;
            SetObjBaseWeapon(objprop, objbase, 0, 1800, 75, 400, 1650, 0, 50000, gc_obj_weapon_kind_cannonball, True);
            objprop.weapon[0].weaponsid := 'PPOINTTKOR';
            objprop.weapon[0].cost[gc_resource_type_iron] := 25;
            objprop.weapon[0].cost[gc_resource_type_coal] := 35;
            objprop.weapon[0].addradius := _misc_PixelsToTiles(32);
            objbase.weapon[0].dispertion := _misc_PixelsToTiles(250);
            SetObjBasePrice(objbase, 0, 5000, 0, 1100, 600, 800);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_wood, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 1800, 7360, 8, 300);
            objprop.consume[gc_resource_type_gold] := 150;
            objprop.bstandground := True;
            objprop.bshowdelay := True;
            objprop.bnohungry := True;
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_frigate;
            objprop.explmedia := gc_obj_explmedia_ship;
            objprop.explradius := _misc_PixelsToTiles({110 in c1}80);
            objprop.bshotdirection := True;
            objprop.bshotforward := False;
            objprop.bshotleftflank := True;
            objprop.bshotrightflank := True;
            objprop.bshotback := False;
            case objprop.sid of
               'xebec' : begin
                  objbase.maxhp := 65000;
                  objbase.speed := gc_obj_speed_xebec;
                  objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_xebec);
                  SetObjBaseWeapon(objprop, objbase, 0, default, 50, 550, 1700, default, 70000, gc_obj_weapon_kind_cannonball, True);
                  SetObjBasePrice(objbase, 0, 7000, 0, 1600, 320, 960);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, default, default, default, 400);
                  objprop.consume[gc_resource_type_gold] := 0;
               end;
               'battleship' : begin
                  objbase.maxhp := 65500;
                  objbase.shield := 100;
                  objbase.speed := gc_obj_speed_battleship;
                  objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_battleship);
                  SetObjBaseWeapon(objprop, objbase, 0, default, 20, 600, 1950, 600, default, gc_obj_weapon_kind_cannonball, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 5;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 15;
                  SetObjBasePrice(objbase, 0, 9000, 0, 3200, 700, 6500);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1680, 12480, 8, 2000);
                  objprop.consume[gc_resource_type_gold] := 15000;
                  objprop.bmercenary := True;
                  objprop.usage := gc_obj_usage_battleship;
                  objprop.explradius := _misc_PixelsToTiles({125 in c1}105);
               end;
               'chaika' : begin
                  objbase.maxhp := 25000;
                  objbase.speed := gc_obj_speed_chaika;
                  objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_chaika);
                  objprop.usage := gc_obj_usage_yacht;
                  objprop.costpercent := 105;
                  SetObjBaseWeapon(objprop, objbase, 0, 1000, 350 div 2, 300, 1100, 0, 50000, gc_obj_weapon_kind_cannonball, True);
                  objprop.weapon[0].cost[gc_resource_type_iron] := 4;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 9;
                  SetObjBasePrice(objbase, 0, 1050, 0, 600, 200, 400);
                  SetObjBaseSearchBuildVisionScore(objprop, objbase, 1050, 1280, 4, 100);
                  objprop.consume[gc_resource_type_gold] := 0;
                  objprop.explradius := _misc_PixelsToTiles(50);
                  objprop.bshotforward := True;
                  objprop.bshotleftflank := False;
                  objprop.bshotrightflank := False;
                  objprop.bshotback := True;
               end;
            end;
         end;
         'ferry' : begin
            objbase.maxhp := 62000;
            objbase.speed := gc_obj_speed_ferry;
            objprop.radius := _misc_PixelsToTiles(gc_obj_radius_ship_ferry);
            objprop.rotatespeed := gc_obj_rotatespeed_ships;
            objprop.media := gc_obj_media_water;
            objprop.motionstyle := gc_obj_motionstyle_ship;
            SetObjBasePrice(objbase, 0, 300, 0, 50, 100, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_wood, 0, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 1792, 1, 50);
            objprop.transport := 80+40{c3};
            objprop.bnohungry := True;
            objprop.usage := gc_obj_usage_transport;
            objprop.explmedia := gc_obj_explmedia_ship;
            objprop.explradius := _misc_PixelsToTiles({130 in c1}95);
            objprop.exitMaxDelay := 0.375;
         end;
         'unitbox' : begin
            objbase.maxhp := 100;
            SetObjBasePrice(objbase, 100, 0, 0, 0, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, 0, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 100, 1, 10);
         end;
         'misdonkey', 'mistrader', 'misflagman' : begin
            objbase.maxhp := 150;
            SetObjBasePrice(objbase, 50, 0, 0, 10, 0, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, 0, 0, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 0, 150, 1, 0);
            objprop.usage := gc_obj_usage_lightinfantry;
            objprop.aiforce := 2;
            case objprop.sid of
               'misdonkey' : begin
                  objprop.walkintervalfactor := cWalkIntervalDonkey;
               end;
               'misflagman' : begin
                  objbase.maxhp := 100;
                  objprop.vision := 8;
                  objprop.bdrummer := True;
               end;
               'mistrader' : begin
                  objbase.maxhp := 50;
               end;
            end;
         end;
         'misgeneral' : begin
            objbase.maxhp := 150;
            SetObjBaseWeapon(objprop, objbase, 0, 140, 0, 15, 95, 0, 100000, gc_obj_weapon_kind_sword, True);
            SetObjBasePrice(objbase, 50, 0, 0, 225, 15, 0);
            SetObjBaseMaterialCanKill(objprop, gc_obj_material_body, gc_obj_material_body, gc_obj_material_woodwall, 0, 0, 0);
            SetObjBaseSearchBuildVisionScore(objprop, objbase, 500, 150, 1, 0);
            objbase.shield := 10;
            objprop.consume[gc_resource_type_gold] := 50;
            objprop.bstandground := True;
            objprop.bturnoff := True;
            objprop.usage := gc_obj_usage_lightinfantry;
            //objprop.bofficer := True;
            objprop.aiforce := 2;
         end;
         else
         bCheckBuilding := True;
      end;
      if (not bCheckBuilding) then //unit found
      begin
         objprop.bprotector := not objprop.bcapture;
         objprop.bcancapture := (not objprop.bcapture) and (objprop.usage<>gc_obj_usage_peasant);
      end;

      var aus, fra, eng, spa, rus, ukr, pol, swe, pru, ven, tur, alg, mis, net, den, por, pie, sax, bav, hun, swi, sco, tat, lit : Boolean;
      _country_IDToBoolean(incid, aus, fra, eng, spa, rus, ukr, pol, swe, pru, ven, tur, alg, mis, net, den, por, pie, sax, bav, hun, swi, sco, tat, lit);

      if (not mis) then
      begin
         // setup общих построек для разных наций
         if (bCheckBuilding) then
         begin
            var i : Integer;
            for i:=0 to 5 do
            begin
               var commoneur, commonrus, commontur, commonspa, commonpor : Boolean;
               var commonsid : String;
               case i of
                  0 : begin
                     commonsid := 'eur';
                     commoneur := True;
                  end;
                  1 : begin
                     commonsid := 'rus';
                     commonrus := True;
                  end;
                  2 : begin
                     commonsid := 'tur';
                     commontur := True;
                  end;
                  3 : begin
                     commonsid := 'spa';
                     commonspa := True;
                     commonpor := True;
                  end;
                  4 : begin
                     commonsid := 'ukr';
                     commonrus := True;
                  end;
                  5 : begin
                     commonsid := 'por';
                     commonpor := True;
                  end;
               end;
               case objprop.sid of
                  commonsid+'mil' : begin
                     SetObjBuildingBaseSettings(objprop, bCapture, 20, gc_obj_usage_mill);
                     SetObjBuildingProperties(objprop, objbase, 20000, 300, 200);
                     SetObjBasePrice(objbase, 0, 30, 150, 0, 0, 0);
                     objprop.resourcebase[gc_resource_type_food] := True;
                     if (commonrus) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 15000, default, default);
                        SetObjBasePrice(objbase, 0, 210, 0, 0, 0, 0);
                     end;
                  end;
                  commonsid+'por' : begin
                     SetObjBuildingBaseSettings(objprop, False, 500, gc_obj_usage_port);
                     SetObjBuildingProperties(objprop, objbase, 65000, 5000, 150);
                     SetObjBasePrice(objbase, 0, 1600, 800, 0, 400, 0);
                     if (commonpor) then
                     begin
                        SetObjBaseWeapon(objprop, objbase, 0, 1000, 280, 550, 1500, 550, 50000, gc_obj_weapon_kind_cannonball, True);
                        objprop.weapon[0].weaponsid := 'PPOINTTTOW';
                        objprop.weapon[0].cost[gc_resource_type_iron] := 10;
                        objprop.weapon[0].cost[gc_resource_type_coal] := 30;
                        objprop.weapon[0].fxshot := 'shottower';
                        objbase.weapon[0].dispertion := _misc_PixelsToTiles(100);
                        SetObjBaseMaterialCanKill(objprop, gc_obj_material_building, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
                        objprop.searchradius := _misc_PixelsToTiles(1400);
                        objprop.consume[gc_resource_type_gold] := 250;
                        objprop.vision := 3;
                        objprop.bshowdelay := True;
                        objprop.bturnoff := True;
                        objprop.explradius := _misc_PixelsToTiles(1);
                        objprop.bartprepare := True;
                     end;
                     if (commonrus) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 60000, default, 150);
                        SetObjBasePrice(objbase, 0, 1200, 800, 0, 400, 0);
                     end;
                     if (commontur) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 55000, default, 150);
                        SetObjBasePrice(objbase, 0, 800, 800, 0, 400, 0);
                     end;
                     if (ukr) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 60000, default, 150);
                        SetObjBasePrice(objbase, 0, 2000, 0, 0, 0, 0);
                     end;
                  end;
                  commonsid+'mar' : begin
                     SetObjBuildingBaseSettings(objprop, bCapture, 500, 0);
                     SetObjBuildingProperties(objprop, objbase, 4000, 750, 2000);
                     SetObjBasePrice(objbase, 0, 450, 0, 0, 0, 0);
                     objprop.bmarket := True;
                     if (commonspa) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, default, 500, 2000);
                     end;
                     if (commontur) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 4500, default, 1500);
                        SetObjBasePrice(objbase, 0, 450, 150, 0, 0, 0);
                     end;
                  end;
                  commonsid+'sto' : begin
                     SetObjBuildingBaseSettings(objprop, True, 30, gc_obj_usage_storage);
                     SetObjBuildingProperties(objprop, objbase, 10000, 100, 150);
                     SetObjBasePrice(objbase, 0, 50, 20, 0, 0, 0);
                     objprop.resourcebase[gc_resource_type_wood] := True;
                     objprop.resourcebase[gc_resource_type_stone] := True;
                     if (commonspa) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, default, default, 150);
                        SetObjBasePrice(objbase, 0, 20, 20, 0, 0, 0);
                     end;
                     if (commonrus) then
                     SetObjBuildingProperties(objprop, objbase, default, default, 200);
                     if (commontur) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, default, default, 200);
                        SetObjBasePrice(objbase, 0, 30, 10, 0, 0, 0);
                     end;
                  end;
                  commonsid+'tow' : begin
                     SetObjBuildingBaseSettings(objprop, False, 500, gc_obj_usage_tower);
                     SetObjBuildingProperties(objprop, objbase, 20000, 3937{4000}, 120);
                     SetObjBasePrice(objbase, 0, 100, 100, 150, 0, 0);
                     SetObjBaseWeapon(objprop, objbase, 0, 1000, 400, 550, 1500, 550, 50000, gc_obj_weapon_kind_cannonball, True);
                     objprop.weapon[0].weaponsid := 'PPOINTTTOW';
                     objprop.weapon[0].cost[gc_resource_type_iron] := 10;
                     objprop.weapon[0].cost[gc_resource_type_coal] := 30;
                     objprop.weapon[0].fxshot := 'shottower';
                     objbase.weapon[0].dispertion := _misc_PixelsToTiles(100);
                     SetObjBaseMaterialCanKill(objprop, gc_obj_material_building, gc_obj_material_body, gc_obj_material_wood, gc_obj_material_woodwall, gc_obj_material_iron, gc_obj_material_building);
                     objprop.searchradius := _misc_PixelsToTiles(1400);
                     objprop.consume[gc_resource_type_gold] := 500;
                     objprop.vision := 3;
                     objprop.bshowdelay := True;
                     objprop.bturnoff := True;
                     objprop.explradius := _misc_PixelsToTiles(1);
                     objprop.bartprepare := True;
                     if (commonrus) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 21000, 4725, 125);
                        objbase.weapon[0].dispertion := _misc_PixelsToTiles(125);
                        objbase.shield := 5;
                        SetObjBaseWeapon(objprop, objbase, 0, default, 300, default, default, default, default, gc_obj_weapon_kind_cannonball, True);
                     end;
                     if (commontur) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, 22500, 3150, 125);
                        SetObjBasePrice(objbase, 0, 150, 90, 100, 0, 0);
                        SetObjBaseWeapon(objprop, objbase, 0, 1200, 500, default, 1600, default, default, gc_obj_weapon_kind_cannonball, True);
                        objprop.weapon[0].cost[gc_resource_type_coal] := 40;
                        objprop.weapon[0].cost[gc_resource_type_iron] := 15;
                        objprop.searchradius := _misc_PixelsToTiles(1500);
                     end;
                  end;
                  commonsid+'swa', commonsid+'sga' : begin
                     SetObjBuildingBaseSettings(objprop, False, 1, gc_obj_usage_none);
                     SetObjBuildingProperties(objprop, objbase, 50000, 288{420}, 0);
                     SetObjBasePrice(objbase, 0, 0, 50, 0, 0, 0);
                     objprop.consume[gc_resource_type_stone] := 250; // C1 was 750
                     objprop.bturnoff := True;
                     objprop.bwall := True;
                     objprop.usage := gc_obj_usage_hardwall;
                     if (commonrus) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, default, 640{945}, 0); // c1 bt = 2000
                        SetObjBasePrice(objbase, 0, 0, 60, 0, 0, 0);
                        objprop.consume[gc_resource_type_stone] := 200;
                     end;
                     if (commontur) then
                     begin
                        SetObjBuildingProperties(objprop, objbase, default, 384{577}, 0); // c1 bt = 1100
                        SetObjBasePrice(objbase, 0, 0, 60, 0, 0, 0);
                        objprop.consume[gc_resource_type_stone] := 150; // C1 was 250
                     end;
                     objprop.bslowdeath := False;
                     if (objprop.sid=commonsid+'sga') then
                     begin
                        objprop.bgate := True;
                        objbase.maxhp := 32000;
                     end;
                  end;
                  'ukrwwa', 'ukrwga' : begin
                     SetObjBuildingBaseSettings(objprop, False, 1, gc_obj_usage_none);
                     SetObjBuildingProperties(objprop, objbase, 1500, 18{26}, 0);
                     if (ukr) then
                     SetObjBuildingProperties(objprop, objbase, 2500, 26{39}, 0);
                     SetObjBasePrice(objbase, 0, 10, 0, 0, 0, 0);
                     if (ukr) then
                     SetObjBasePrice(objbase, 0, 12, 0, 0, 0, 0);
                     objprop.material := gc_obj_material_woodwall;
                     objprop.consume[gc_resource_type_wood] := 32;
                     if (ukr) then
                     objprop.consume[gc_resource_type_wood] := 40;
                     objprop.bturnoff := True;
                     objprop.bwall := True;
                     objbase.shield := 15+80;
                     objprop.bslowdeath := False;
                     objprop.usage := gc_obj_usage_weakwall;
                     if (objprop.sid='ukrwga') then
                     begin
                        objprop.bgate := True;
                        if (ukr) then
                        objbase.maxhp := 1500
                        else
                        objbase.maxhp := 1000;
                     end;
                  end;
                  commonsid+'gol', commonsid+'iro', commonsid+'coa' : begin
                     SetObjBuildingBaseSettings(objprop, True, 500, gc_obj_usage_mine);
                     SetObjBuildingProperties(objprop, objbase, 2500, 300, 0);
                     SetObjBasePrice(objbase, 0, 100, 100, 0, 0, 0);
                     objprop.peasantabsorber  := 5;
                     objprop.bslowdeath := False;
                     objprop.exitMaxDelay := 0.5;
                     case objprop.sid of
                        commonsid+'gol' : objprop.produce[gc_resource_type_gold] := 13;
                        commonsid+'iro' : objprop.produce[gc_resource_type_iron] := 13;
                        commonsid+'coa' : objprop.produce[gc_resource_type_coal] := 13;
                     end;
                  end;
                  else
                  bCheckBuilding := True;
               end;
               if (not bCheckBuilding) then
               break;
            end;
         end;

         // setup уникальных построек
         if (bCheckBuilding) then
         begin
            var i : Integer;
            for i:=0 to gc_MaxCountryCount-1 do
            begin
               const aus = 0;
               const fra = 1;
               const eng = 2;
               const spa = 3;
               const rus = 4;
               const ukr = 5;
               const pol = 6;
               const swe = 7;
               const pru = 8;
               const ven = 9;
               const tur = 10;
               const alg = 11;
               const mis = 12;
               const net = 13;
               const den = 14;
               const por = 15;
               const pie = 16;
               const sax = 17;
               const bav = 18;
               const hun = 19;
               const swi = 20;
               const sco = 21;
               const tat = 22;
               const lit = 23;

               var csid : String;
               _country_GetSIDByID(i, csid);
               if (bCheckBuilding) then
               begin
                  bCheckBuilding := False;
                  case objprop.sid of
                     // BUILDINGS
                     csid+'cen' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 4000, 500, 300, True, 1000, gc_obj_usage_center, 0, 700, 700, 0, 0, 0);
                        objprop.resourcebase[gc_resource_type_food] := True;
                        objprop.resourcebase[gc_resource_type_wood] := True;
                        objprop.resourcebase[gc_resource_type_stone] := True;
                        objprop.farm := 100;
                        case i of
                           aus : SetObjBuildingExtProperties(objprop, objbase, default, 150, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           fra : SetObjBuildingExtProperties(objprop, objbase, 4500, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           eng : SetObjBuildingExtProperties(objprop, objbase, 4030, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           spa : SetObjBuildingExtProperties(objprop, objbase, 4250, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           rus : begin
                              SetObjBuildingExtProperties(objprop, objbase, 4050, default, default, True, default, default, 0, 680, 700, 0, 0, 0);
                              objprop.farm := 75;
                           end;
                           ukr : begin
                              SetObjBuildingExtProperties(objprop, objbase, 5300, default, 400, True, default, default, 0, 700, 0, 0, 0, 0);
                              objprop.farm := 200;
                           end;
                           pol : SetObjBuildingExtProperties(objprop, objbase, 4300, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           swe : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           pru : SetObjBuildingExtProperties(objprop, objbase, 4200, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           ven : SetObjBuildingExtProperties(objprop, objbase, 5100, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 600, 500, 0, 0, 0);
                           alg : begin
                              SetObjBuildingExtProperties(objprop, objbase, 5500, default, default, True, default, default, 0, 450, 700, 0, 0, 0);
                              objprop.farm := 50;
                           end;
                           den : SetObjBuildingExtProperties(objprop, objbase, 4030, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                           net : SetObjBuildingExtProperties(objprop, objbase, 4950, default, default, True, default, default, 0, 700, 700, 0, 0, 0);
                        end;
                     end;
                     csid+'hou' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 4000, 100, 104{103}, True, 100, gc_obj_usage_farm, 0, 100, 100, 0, 0, 0);
                        objprop.farm := 25{15};
                        case i of
                           eng : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           spa : SetObjBuildingExtProperties(objprop, objbase, 4200, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           rus : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, True, default, default, 0, 60*2, 0, 0, 0, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 4150, default, 105{104}, True, default, default, 0, 60*2, 0, 0, 0, 0);
                           pol : SetObjBuildingExtProperties(objprop, objbase, 4100, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           swe : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           pru : SetObjBuildingExtProperties(objprop, objbase, 4500, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           ven : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, default, default, 106{105}, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 4300, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           den : SetObjBuildingExtProperties(objprop, objbase, 4000, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                           net : SetObjBuildingExtProperties(objprop, objbase, 4500, default, default, True, default, default, 0, 50*2, 50*2, 0, 0, 0);
                        end;
                     end;
                     csid+'aca' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 63000, 2000, 300, True, 500, gc_obj_usage_none, 0, 1250, 1100, 0, 0, 0);
                        case i of
                           aus : SetObjBuildingExtProperties(objprop, objbase, 65000, default, default, True, default, default, 0, 1250, 1100, 0, 0, 0);
                           fra : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1250, 1100, 0, 0, 0);
                           eng : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1150, 1200, 0, 0, 0);
                           spa : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1350, 1000, 0, 0, 0);
                           rus : SetObjBuildingExtProperties(objprop, objbase, 65000, 2700, default, True, default, default, 0, 1250, 1300, 0, 0, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 65000, 150, default, True, default, default, 0, 1350, 1200, 0, 0, 0);
                           pol : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 950, 800, 0, 0, 0);
                           swe : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1350, 1000, 0, 0, 0);
                           pru : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1200, 1150, 0, 0, 0);
                           ven : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1090, 1260, 0, 0, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, 65000, 500, default, True, default, default, 0, 1450, 1100, 0, 0, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 65000, 500, default, True, default, default, 0, 1450, 1100, 0, 0, 0);
                           den : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1450, 900, 0, 0, 0);
                           net : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 1050, 1230, 0, 0, 0);
                        end;
                     end;
                     csid+'art' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 40000, 787, 200, True, 500, gc_obj_usage_none, 0, 100, 1000, 0, 0, 1400);
                        objprop.artdepo[0] := 5;
                        objprop.artdepo[1] := 5;
                        objprop.artdepo[2] := 10; // c1 = 30
                        objprop.artdepo[3] := 3;
                        objprop.bartdepo := True;
                        case i of
                           ukr : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 4250, 4400, 100, 0, 1400);
                           tur : SetObjBuildingExtProperties(objprop, objbase, default, default, default, True, default, default, 0, 500, 1200, 0, 0, 1400);
                        end;
                     end;
                     csid+'dip' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 4500, 1000, 100, False, 500, gc_obj_usage_dipcenter, 0, 4900, 1700, 0{1000}, 0, 0);
                        //objprop.price[gc_resource_type_gold] := 1000; // in cossacks 1, in md file there is error, when price element count is 2, while price contains 3 resource types
                        case i of
                           rus : SetObjBuildingExtProperties(objprop, objbase, 6500, default, default, False, default, default, 0, 7900, 3700, 0{2500}, 0, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 5000, default, default, False, default, default, 0, 3900, 2700, 0{500}, 0, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, 5500, default, default, False, default, default, 0, 4600, 2020, 0{1300}, 0, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 5500, default, default, False, default, default, 0, 4600, 2020, 0{1300}, 0, 0);
                        end;
                     end;
                     csid+'tem' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 4200, 500, 300, False, 500, gc_obj_usage_none, 0, 1000, 1200, 0, 500, 0);
                        case i of
                           fra : SetObjBuildingExtProperties(objprop, objbase, 6000, 1000, default, False, default, default, 0, 1100, 2000, 0, 600, 0);
                           rus : SetObjBuildingExtProperties(objprop, objbase, 4500, default, default, False, default, default, 0, 1150, 1650, 100, 500, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 5300, default, default, False, default, default, 0, 1100, 1400, 0, 300, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, 5000, 300, default, False, default, default, 0, 1000, 1200, 0, 500, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 5000, 300, default, False, default, default, 0, 1000, 1200, 0, 500, 0);
                        end;
                     end;
                     csid+'bar' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 40000, 300, 500, False, 500, gc_obj_usage_none, 0, 100, 100, 500, 0, 0);
                        objprop.farm := 150;
                        case i of
                           rus : begin
                              SetObjBuildingExtProperties(objprop, objbase, 25000, 250, 300, False, default, default, 0, 200, 20, 0, 0, 0);
                              objprop.farm := 25;
                           end;
                           ukr : begin
                              SetObjBuildingExtProperties(objprop, objbase, 20000, default, 300, False, default, default, 0, 150, 150, 0, 0, 0);
                              objprop.farm := 75;
                           end;
                           tur, alg : begin
                              SetObjBuildingExtProperties(objprop, objbase, 35000, default, 500, False, default, default, 0, 400, 400, 0, 0, 0);
                              objprop.farm := 50;
                           end;
                           sco : begin
                              SetObjBuildingExtProperties(objprop, objbase, 30000, default, 500, False, default, default, 0, 100, 100, 500, 0, 0);
                           end;
                        end;
                     end;
                     csid+'ba2' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 55000, 18000, 200, False, 500, gc_obj_usage_none, 0, 1700, 2950, 4000{12500}, 0, 0);
                        objprop.farm := 250;
                        case i of
                           sco : begin
                              SetObjBuildingExtProperties(objprop, objbase, 40000, 2000, 250, False, default, default, 0, 640, 2400, 2400, 0, 0);
                              objprop.farm := 150;
                           end;
                        end;
                     end;
                     csid+'sta' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 20000, 2000, 200, False, 500, gc_obj_usage_none, 0, 2500, 100, 600, 0, 0);
                        case i of
                           eng : SetObjBuildingExtProperties(objprop, objbase, 25000, 1200, default, False, default, default, 0, 2350, 0, 800, 0, 0);
                           rus : SetObjBuildingExtProperties(objprop, objbase, 25000, 1200, default, False, default, default, 0, 7950, 0, 550, 0, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 10000, 500, 300, False, default, default, 0, 3200, 850, 850, 0, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, 55000, 500, 700, False, default, default, 0, 1000, 2600, 0, 0, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 55000, 500, 700, False, default, default, 0, 1000, 2200, 0, 0, 0);
                           sco : SetObjBuildingExtProperties(objprop, objbase, 25000, 1200, default, False, default, default, 0, 2350, 0, 800, 0, 0);
                        end;
                     end;
                     csid+'bla' : begin
                        SetObjBuildingExtProperties(objprop, objbase, 5500, 300, 400, True, 500, gc_obj_usage_none, 0, 100, 30, 0, 640, 0);
                        case i of
                           fra : SetObjBuildingExtProperties(objprop, objbase, default, default, 600, True, default, default, 0, 100, 30, 0, 640, 0);
                           ukr : SetObjBuildingExtProperties(objprop, objbase, 4500, 200, default, True, default, default, 0, 100, 30, 0, 640, 0);
                           tur : SetObjBuildingExtProperties(objprop, objbase, 6500, 350, default, True, default, default, 0, 100, 30, 0, 640, 0);
                           alg : SetObjBuildingExtProperties(objprop, objbase, 6500, 350, default, True, default, default, 0, 100, 30, 0, 640, 0);
                        end;
                     end;
                     else
                     bCheckBuilding := True;
                  end;
               end
               else
               break;
            end;
         end;
      end
      else
      //if (mis) then
      begin
         if (bCheckBuilding) then
         begin
            var csid : String = 'mis';
            bCheckBuilding := False;
            case objprop.sid of
               'misblg', 'misblg2' : begin
                  SetObjBuildingBaseSettings(objprop, False, 500, gc_obj_usage_tower);
                  SetObjBuildingProperties(objprop, objbase, 10000, 4500, 300);
                  SetObjBasePrice(objbase, 0, 100, 100, 150, 0, 0);
                  SetObjBaseWeapon(objprop, objbase, 0, 85, 1, 400 {C1 Was 550}, 950, 550, 50000, gc_obj_weapon_kind_bullet, True);
                  objprop.weapon[0].weaponsid := 'SHOTBLOCKHOUSE';
                  objprop.weapon[0].cost[gc_resource_type_iron] := 2;
                  objprop.weapon[0].cost[gc_resource_type_coal] := 2;
                  objprop.weapon[0].fxshot := 'shotmusketpoint';
                  SetObjBaseMaterialCanKill(objprop, gc_obj_material_building, gc_obj_material_body, 0, 0, 0, 0);
                  objprop.searchradius := _misc_PixelsToTiles(1000);
                  objprop.vision := 3;
                  objprop.bshowdelay := True;
                  objprop.explradius := _misc_PixelsToTiles(1);
               end;
               'misyurt' : begin
                  SetObjBuildingBaseSettings(objprop, False, 0, gc_obj_usage_none);
                  SetObjBuildingProperties(objprop, objbase, 10000, 100, 100);
                  SetObjBasePrice(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseMaterialCanKill(objprop, gc_obj_material_building, 0, 0, 0, 0, 0);
                  objprop.bslowdeath := True;
                  objprop.vision := 2;
                  objprop.explradius := _misc_PixelsToTiles(1);
               end;
               'miscommandcenter' : begin
                  SetObjBuildingBaseSettings(objprop, False, 0, gc_obj_usage_none);
                  SetObjBuildingProperties(objprop, objbase, 25000, 1000, 100);
                  SetObjBasePrice(objbase, 0, 0, 0, 0, 0, 0);
                  SetObjBaseMaterialCanKill(objprop, gc_obj_material_building, 0, 0, 0, 0, 0);
                  objprop.bslowdeath := True;
                  objprop.vision := 2;
                  objprop.explradius := _misc_PixelsToTiles(1);
               end;
               else
               begin
                  if (StrPos(csid, objprop.sid)=1) then
                  begin
                     SetObjBuildingBaseSettings(objprop, False, 0, gc_obj_usage_none);
                     SetObjBuildingProperties(objprop, objbase, 10000, 100, 100);
                     SetObjBasePrice(objbase, 0, 0, 0, 0, 0, 0);
                     SetObjBaseMaterialCanKill(objprop, gc_obj_material_none, 0, 0, 0, 0, 0);
                     objprop.bslowdeath := False;
                     objprop.vision := 0;
                     objprop.explradius := 0;
                  end
                  else
                  bCheckBuilding := True;
               end;
            end;
         end;
      end;

      // post processing
      objprop.mmask := objprop.mmask or (1 shl objprop.material);
      var i, j : Integer;
      for i:=0 to gc_obj_material_count-1 do
      begin
         if (objprop.cankill[i]) then
         objprop.kmask := objprop.kmask or (1 shl i);
      end;
      for i:=0 to gc_obj_MaxWeapon-1 do
      begin
         if (objprop.weapon[i].enabled) then
         begin
            // dont pay attention on attmask, cause we dont want to autosearchvictim for custom attmask
            //for j:=0 to gc_obj_material_count-1 do
            //if (objprop.weapon[i].attmask[j]) then
            //objprop.kmask := objprop.kmask or (1 shl j);

            if (objprop.weapon[i].weaponsid<>'') then
            objprop.weapon[i].weaponid := _weapon_GetWeaponIdBySID(objprop.weapon[i].weaponsid);

            {if (objbase.weapon[i].pause>0) then
            begin
               var sf, ef : Integer;
               var anim : String;
               case i of
                  0 : anim := gc_anim_attack0;
                  1 : anim := gc_anim_attack1;
                  2 : anim := gc_anim_attack2;
                  else
                  Log('unit have more then 3 weapons, check this');
               end;
               if (anim<>'') and (GetGameObjectFrameAnimationDataByHandle(goHnd, anim, sf, ef)) then
               begin
                  var frames : Integer = 1+ef-sf;
                  objbase.weapon[i].pause := objbase.weapon[i].pause-_misc_FramesToTime(frames);
                  if (objbase.weapon[i].pause<0) then
                  objbase.weapon[i].pause := 0;
               end;
            end;}
         end;
      end;
      var airole : integer = gc_ai_unit_none;
      for i:=0 to gc_country_maxmembers do
      begin
         if (gCountry[incid].members[i]=sid) then
         begin
            airole := gCountry[incid].membersairole[i];
            break;
         end;
      end;
      objprop.airole := airole;

      objprop.bcansetrally := CanSetRallyPointByObjBase(objprop);
   end;
end;

// _unit_ResetOrder
//
procedure _unit_ResetOrder(order : TOrder);
begin
   order.itype := gc_obj_order_type_none;
   order.bexecute := False;
   order.bremove := False;
   order.static := false;
   order.info.trg := 0;
   order.info.x := 0;
   order.info.y := 0;
   order.info.dx := 0;
   order.info.dy := 0;
   order.info.dir := 0;
   order.info.z := 0;
   order.info.upgradeid := 0;
   order.info.produceid := 0;
   order.info.restype := 0;
   order.info.amount := 0;
   order.info.progress := 0;
end;

// _unit_InitObj
//
procedure _unit_InitObj(goHnd, cid : Integer; var basename : String; var obj : TObj);
begin
   obj.baseid := gc_baseid_obj;
   var id : Integer = _unit_ConvertObjSIDToID(cid, basename);
   obj.id := id;
   if (GetGameObjectUniqueIdByHandle(goHnd)=0) then
   obj.uid := GameObjectMakeUniqId(goHnd)
   else
   obj.uid := GetGameObjectUniqueIdByHandle(goHnd);
   obj.pl := GetPlayerIndexByHandle(GetGameObjectPlayerHandleByHandle(goHnd));
   if (cid<0) then
   ErrorLog('_unit_InitObj cid<0');
   obj.cid := cid;
   //_country_GetSIDByID(cid, obj.csid);
   obj.hp := gPlayer[obj.pl].objbase[cid][id].maxhp;
   obj.kill := 0;
   obj.resamount := 0;
   obj.restype := gc_resource_type_none;
   obj.buildprogress := 0;
   obj.bdead := False;
   obj.bbuilt := True;
   obj.bstandground := False;
   obj.bsearchenemy := True;
   obj.insideofuid := 0;
   obj.insidereserved := 0;
   obj.squad := -1;
   obj.brally := false;
   obj.rallyx := 0;
   obj.rallyy := 0;
   obj.rallytmpx := 0;
   obj.rallytmpy := 0;
   obj.standtime := 0;
   obj.attackdelay := 0;
   obj.attackmaxdelay := 0;
   obj.exitdelay := 0;
   obj.lastprogresstime := 0;
   obj.progresstick := floor(RandomExt*32);
   obj.idlegridx := -1;
   obj.idlegridy := -1;
   obj.scangridx := -1;
   obj.scangridy := -1;
   obj.ctrlgroupmask := 0;
   obj.bpathrequested := False;
   obj.bleaverequested := False;
   obj.bhiderequested := False;
   obj.trghnd := 0;
   obj.topzone := -1;
   obj.scenariogroupid := -1;
   obj.smokecount := 0;
   obj.wallvariation := 0;
   obj.inbattle := False;
   obj.artx := 0;
   obj.arty := 0;
   obj.arthnd := 0;
   obj.artdistfactor := 0;
   obj.uniqrnd := RandomExt;
   obj.individual.benabled := False;
   obj.individual.upglevel := 0;
   obj.individual.attackrate := 1;
   obj.individual.addpeasantabsorber := 0;
   obj.individual.addtransport := 0;
   var i : Integer;
   for i:=0 to gc_obj_MaxOrderCount-1 do
   _unit_ResetOrder(obj.orders[i]);
end;

// _unit_SetOrderTrg
//
procedure _unit_SetOrderTrg(const goHnd, ind, trgHnd : Integer; bUpdateOrdList : Boolean);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if bUpdateOrdList then
      begin
         _misc_DecreaseResGridWorkerCount(goHnd, ind);
         _misc_IncreaseResGridWorkerCount(goHnd, trgHnd);
      end;

      TObj(pobj).orders[ind].info.trg := trgHnd;
   end;
end;

// _unit_ClearOrder
//
procedure _unit_ClearOrder(const goHnd, ind : Integer; bUpdateOrdList, bSorting : Boolean);
begin
   // очистка приказа
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var porder : Pointer = TObj(pobj).orders[ind];
   if pOrder <> nil then
   begin
      var porderinfo : Pointer = TObj(pobj).orders[ind].info;
      if pOrderInfo <> nil then
      begin
         // очистка резерва места в шахте или пароме
         if (not bSorting) and ((TOrder(porder).itype=gc_obj_order_type_gotomine) or (TOrder(porder).itype=gc_obj_order_type_gototransport)) then
         begin
            var trgHnd : Integer = TOrderInfo(porderinfo).trg;
            if (trgHnd<>0) then
            begin
               var pobj2 : Pointer = _unit_GetTObj(trgHnd);
               if (pobj2<>nil) then
               begin
                  TObj(pobj2).insidereserved := TObj(pobj2).insidereserved-1;
                  if (TObj(pobj2).insidereserved<0) then
                  begin
                     TObj(pobj2).insidereserved := 0;
                     //TODO : 161113 find out why log appears
                     //ErrorLog('_unit_ClearOrder : insidereserved<0');
                  end;
               end;
            end;
         end;

         //TOrder(porder).uid := 0;
         //TOrder(porder).next := 0;
         TOrder(porder).itype := gc_obj_order_type_none;
         TOrder(porder).bexecute := False;
         TOrder(porder).bremove := False;
         TOrder(porder).static := false;
         //TOrder(porder).priority := 0;
         //TOrderInfo(porderinfo).trg := 0;
         _unit_SetOrderTrg(goHnd, ind, 0, bUpdateOrdList);
         TOrderInfo(porderinfo).x := 0;
         TOrderInfo(porderinfo).y := 0;
         TOrderInfo(porderinfo).dx := 0;
         TOrderInfo(porderinfo).dy := 0;
         TOrderInfo(porderinfo).dir := 0;
         TOrderInfo(porderinfo).z := 0;
         TOrderInfo(porderinfo).upgradeid := 0;
         TOrderInfo(porderinfo).produceid := 0;
         TOrderInfo(porderinfo).restype := 0;
         TOrderInfo(porderinfo).amount := 0;
         TOrderInfo(porderinfo).progress := 0;
      end;
   end;
end;

// _unit_SortOrders
//
procedure _unit_SortOrders(const goHnd : Integer);
begin
   // сортирока приказов (пустые приказы переносятся вконец списка)
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var ind : Integer = -1;
   var i : Integer;
   for i := 0 to gc_obj_MaxOrderCount-1 do
   begin
      if TObj(pobj).orders[i].itype = gc_obj_order_type_none then
      begin
         if ind < 0 then
         ind := i;
      end
      else
      if ind >= 0 then
      begin
         TObj(pobj).orders[ind] := TObj(pobj).orders[i];
         _unit_ClearOrder(goHnd, i, False, True);
         ind := ind+1;
      end;
   end;
end;

// _unit_RemoveOrder
//
procedure _unit_RemoveOrder(const goHnd, ind : Integer);
begin
   // удаление приказа юнита
   _unit_ClearOrder(goHnd, ind, True, False);
   _unit_SortOrders(goHnd);
end;

// _unit_OrdersOffset
//
procedure _unit_OrdersOffset(const goHnd, fromind, offset : Integer);
begin
   // смещение приказов. используется, когда нужно вставить приказ вперед списка, а остальные сдвинуть за него
   if (offset>0) then
   begin
      var i : Integer;
      for i := gc_obj_MaxOrderCount-1 downto gc_obj_MaxOrderCount-offset do
      _unit_ClearOrder(goHnd, i, true, false);

      var pobj : Pointer = _unit_GetTObj(goHnd);
      for i:=gc_obj_MaxOrderCount-(1+offset) downto fromind do
      begin
         if (TObj(pobj).orders[i].itype<>gc_obj_order_type_none) or (TObj(pobj).orders[i+1].itype<>gc_obj_order_type_none) then
         TObj(pobj).orders[i+offset] := TObj(pobj).orders[i];
      end;
      for i := fromInd to fromInd+offset-1 do
      _unit_ClearOrder(goHnd, i, false, false);
   end
   else
   ErrorLog('_unit_OrdersOffset offset=0');
end;

// _unit_ClearOrders
//
procedure _unit_ClearOrders(const goHnd : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var i : Integer;
   for i := 0 to gc_obj_MaxOrderCount-1 do
   begin
      if not TObj(pObj).orders[i].static then
      _unit_ClearOrder(goHnd, i, True, False);
   end;
   _unit_SortOrders(goHnd);
end;

// _unit_FullClearOrders
//
procedure _unit_FullClearOrders(const goHnd : Integer);
begin
   var i : Integer;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   for i := 0 to gc_obj_MaxOrderCount-1 do
   _unit_ClearOrder(goHnd, i, True, False);
end;

// _unit_SetClientSTO
//
procedure _unit_SetClientSTO(const goHnd, trgHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObjFast(goHnd); // no logs
   if (pobj<>nil) then
   begin
      var goSTOHnd : Integer;
      if (TObj(pobj).prevstouid<>0) then
      goSTOHnd := GetGameObjectHandleByUniqueId(TObj(pobj).prevstouid);
      if (trgHnd<>goSTOHnd) then
      begin
         if (goSTOHnd<>0) then
         begin
            var pstolist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_stolist);
            var pattlist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_attlist);
            if (pstolist<>nil) then
            TIntegerList(pstolist).Remove(goHnd);
            if (pattlist<>nil) then
            TIntegerList(pattlist).Remove(goHnd);
         end;

         if (trgHnd<>0) then
         begin
            var pstolist2 : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
            if (pstolist2<>nil) then
            begin
               if (TIntegerList(pstolist2).IndexOf(goHnd)=-1) then
               TIntegerList(pstolist2).Add(goHnd);
            end;
         end;
         TObj(pobj).prevstouid := GetGameObjectUniqueIdByHandle(trgHnd);
         SetGameObjectSTOHandleByHandle(goHnd, trgHnd);
      end;
   end
   else
   SetGameObjectSTOHandleByHandle(goHnd, trgHnd); // perhaps, no need to setup manualy, cause engine already do that on OnTagStates. but it looks useful on FixArgs on target death
end;

// _unit_SetSTO_Normal
//
procedure _unit_SetSTO_Normal(const goHnd, trgHnd : Integer);
begin
   var goSTOHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   if (trgHnd<>goSTOHnd) then
   begin
      if (goSTOHnd<>0) then
      begin
         var pstolist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_stolist);
         var pattlist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_attlist);
         if (pstolist<>nil) then
         TIntegerList(pstolist).Remove(goHnd);
         if (pattlist<>nil) then
         TIntegerList(pattlist).Remove(goHnd);
      end;

      if (trgHnd<>0) then
      begin
         var pstolist2 : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
         if (pstolist2<>nil) then
         begin
            if (TIntegerList(pstolist2).IndexOf(goHnd)=-1) then
            TIntegerList(pstolist2).Add(goHnd);
         end;
      end;
   end;
   SetGameObjectSTOHandleByHandle(goHnd, trgHnd);
end;

// _unit_SetSTO
//
procedure _unit_SetSTO(const goHnd, trgHnd : Integer);
begin
   if (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) and (not _misc_IsProcessAI) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd); // no logs
      if (pobj<>nil) then
      begin
         var goSTOHnd : Integer;
         if _misc_IsProcessAI then
         goSTOHnd := GetGameObjectSTOHandleByHandle(goHnd)
         else
         if (TObj(pobj).prevstouid<>0) then
         goSTOHnd := GetGameObjectHandleByUniqueId(TObj(pobj).prevstouid);
         if (trgHnd<>goSTOHnd) then
         begin
            if (goSTOHnd<>0) then
            begin
               var pstolist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_stolist);
               var pattlist : Pointer = _misc_GetObjectArgData(goSTOHnd, gc_argunit_attlist);
               if (pstolist<>nil) then
               TIntegerList(pstolist).Remove(goHnd);
               if (pattlist<>nil) then
               TIntegerList(pattlist).Remove(goHnd);
            end;

            if (trgHnd<>0) then
            begin
               var pstolist2 : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
               if (pstolist2<>nil) then
               begin
                  if (TIntegerList(pstolist2).IndexOf(goHnd)=-1) then
                  TIntegerList(pstolist2).Add(goHnd);
               end;
            end;
            TObj(pobj).prevstouid := GetGameObjectUniqueIdByHandle(trgHnd);
            SetGameObjectSTOHandleByHandle(goHnd, trgHnd);
         end;
      end
      else
      SetGameObjectSTOHandleByHandle(goHnd, trgHnd);
   end
   else
   _unit_SetSTO_Normal(goHnd, trgHnd);
end;

// _unit_SetSTP
//
procedure _unit_SetSTP(const goHnd : Integer; pX, pZ : Float);
begin
   if (GetGameObjectStateTargetPositionXByHandle(goHnd)<>pX) or (GetGameObjectStateTargetPositionZByHandle(goHnd)<>pZ) then
   SetGameObjectStateTargetPositionByHandle(goHnd, pX, 0, pZ);
end;

// _unit_AddInside
//
procedure _unit_AddInside(goHnd, trgHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      TObj(pobj).insideofuid := GetGameObjectUniqueIdByHandle(trgHnd);
      var pobj2 : Pointer = _unit_GetTObj(trgHnd);
      var i : Integer;
      for i:=0 to gc_ResCount-1 do
      begin
         if (gObjProp[TObj(pobj2).cid][TObj(pobj2).id].produce[i]>0) then
         gPlayer[TObj(pobj2).pl].counter.resincome[i] := gPlayer[TObj(pobj2).pl].counter.resincome[i]+gObjProp[TObj(pobj2).cid][TObj(pobj2).id].produce[i];
      end;
   end;
end;

// _unit_RemoveFromInside
//
procedure _unit_RemoveFromInside(goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var trgHnd : Integer;
      if (_net_IsReplay) or (_net_IsClient) then
      begin
         if (TObj(pobj).insideofuid<>0) then
         trgHnd := GetGameObjectHandleByUniqueId(TObj(pobj).insideofuid); // fix AV in rep and perhaps on possibly on client side
         //else
         if (trgHnd=0) then
         trgHnd := GetGameObjectSTOHandleByHandle(goHnd);
      end
      else
      begin
         trgHnd := GetGameObjectSTOHandleByHandle(goHnd);
         var trgUID : Integer = GetGameObjectUniqueIdByHandle(trgHnd);
         if (TObj(pobj).insideofuid<>0) and (TObj(pobj).insideofuid<>trgUID) then
         trgHnd := GetGameObjectHandleByUniqueId(TObj(pobj).insideofuid); // fix AV in game
      end;
      //if (GetCountOfPlayers>0) and (GetPlayerIndexInterfaceIO=TObj(pobj).pl) and (gProfileUserStruct.achs.ach[gc_ach_skirmish_ferryunload].cur=0) and (trgHnd<>0) and (_unit_isWaterUnit(trgHnd)) then
      //gProfileUserStruct.achs.ach[gc_ach_skirmish_ferryunload].cur := 1;

      //_unit_SetSTO(goHnd, 0);
      TObj(pobj).insideofuid := 0;
      TObj(pobj).bLeaveRequested := false;
      if (trgHnd<>0) then
      begin
         var pobj2 : Pointer = _unit_GetTObj(trgHnd);
         if (pobj2<>nil) then
         begin
            var pObjInside : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_inside);
            if (pObjInside<>nil) then
            TIntegerList(pObjInside).Remove(goHnd);

            var i : Integer;
            for i:=0 to gc_ResCount-1 do
            begin
               if (gObjProp[TObj(pobj2).cid][TObj(pobj2).id].produce[i]>0) then
               gPlayer[TObj(pobj2).pl].counter.resincome[i] := gPlayer[TObj(pobj2).pl].counter.resincome[i]-gObjProp[TObj(pobj2).cid][TObj(pobj2).id].produce[i];
            end;
         end;
      end
      else
      Log('_unit_RemoveFromInside : trgHnd=0');
   end;
end;

// _unit_DoHideInside
//
procedure _unit_DoHideInside(goHnd, trgHnd : Integer);
begin
   _unit_AddInside(goHnd, trgHnd);
   _unit_FullClearOrders(goHnd);

   var tag : Integer = gc_statetag_visual_hide;
   // for_ the case_ when game speed is very high and unit didn't get essential_birth tag from DoProgressOrders
   // prior to entering mine
   if (GetGameObjectStatesTagByHandle(goHnd) and gc_statetag_essential) <> gc_statetag_essential_birth then
   tag := tag or gc_statetag_essential_birth;

   _unit_SetTagStates(goHnd, tag);
end;

// _unit_IdleGridAddUnit
//
procedure _unit_IdleGridAddUnit(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var gridX, gridY : Integer;
   if (_misc_PosToIdleGridIndices(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), gridX, gridY)) then
   begin
      if (gridX<>TObj(pobj).idlegridx) or (gridY<>TObj(pobj).idlegridy) or (TObj(pobj).idlegridx=-1) or (TObj(pobj).idlegridy=-1) then
      begin
         gIdleGrid[gridX,gridY] := gIdleGrid[gridX,gridY]+1;
         TObj(pobj).idlegridx := gridX;
         TObj(pobj).idlegridy := gridY;
      end;
   end;
   //else
   //ErrorLog('_unit_IdleGridAddUnit : position out of range');
end;

// _unit_IdleGridRemoveUnit
//
procedure _unit_IdleGridRemoveUnit(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (TObj(pobj).idlegridx>-1) and (TObj(pobj).idlegridy>-1) then
   begin
      gidleGrid[TObj(pobj).idlegridx,TObj(pobj).idlegridy] := gidleGrid[TObj(pobj).idlegridx,TObj(pobj).idlegridy]-1;
      TObj(pobj).idlegridx := -1;
      TObj(pobj).idlegridy := -1;
   end;
end;

// _unit_ScanGridAddUnit
//
procedure _unit_ScanGridAddUnit(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var gridX, gridY : Integer;
   if (_misc_PosToScanGridIndices(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), gridX, gridY)) then
   begin
      if (gridX<>TObj(pobj).scangridx) or (gridY<>TObj(pobj).scangridy) or (TObj(pobj).scangridx=-1) or (TObj(pobj).scangridy=-1) then
      begin
         if TObj(pobj).bdead then // DEBUG
         ErrorLog('ACHTUNG _unit_ScanGridAddUnit bdead unit added to scangrid');
         gScanGrid[gridx, gridy].Add(gohnd, gObjProp[TObj(pobj).cid][TObj(pobj).id].material, TObj(pobj).pl);
         gScanGridUnits[TObj(pobj).pl, gridx, gridy].Add(gohnd);
         TObj(pobj).scangridx := gridX;
         TObj(pobj).scangridy := gridY;
      end;
   end;
   //else
   //ErrorLog('_unit_ScanGridAddUnit : position out of range');
end;

// _unit_ScanGridRemoveUnit
//
procedure _unit_ScanGridRemoveUnit(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (TObj(pobj).scangridx>-1) and (TObj(pobj).scangridy>-1) then
   begin
      gScanGridUnits[TObj(pobj).pl, TObj(pobj).scangridx, TObj(pobj).scangridy].Remove(gohnd);
      gScanGrid[TObj(pobj).scangridx, TObj(pobj).scangridy].Remove(gohnd, gObjProp[TObj(pobj).cid][TObj(pobj).id].material, TObj(pobj).pl);
      TObj(pobj).scangridx := -1;
      TObj(pobj).scangridy := -1;
   end;
end;

// _unit_TopologyAdd
//
procedure _unit_TopologyAdd(const goHnd : Integer);
begin
   if _misc_GetBaseObjID(goHnd) = gc_baseid_obj then
   begin
      var posX : Float = GetGameObjectPositionXByHandle(goHnd);
      var posZ : Float = GetGameObjectPositionZByHandle(goHnd);
      var curZone : Integer = TopologyGetZoneIndex(posX, posZ);
      if curZone >= 0 then
      begin
         var pObj : Pointer = _unit_GetTObj(goHnd);
         var plInd : Integer = TObj(pObj).pl;
         if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
         begin
            var pBuffer : Pointer = TopologyGetZoneBufferByIndex(curZone);
            if pBuffer <> nil then
            begin
               var danger : Float = _unit_GetDanger(goHnd);
               var attract : Float = _unit_GetAttract(goHnd);

               TTopZone(pBuffer).changeDanger[plInd] := TTopZone(pBuffer).changeDanger[plInd] + danger;
               TTopZone(pBuffer).changeAttract[plInd] := TTopZone(pBuffer).changeAttract[plInd] + attract;
               TTopZone(pBuffer).changed := true;

               if curZone < gInt_MinTopZoneChanged then
               gInt_MinTopZoneChanged := curZone;

               if curZone > gInt_MaxTopZoneChanged then
               gInt_MaxTopZoneChanged := curZone;
            end;
         end;

         TObj(pObj).topzone := curZone;
      end;
   end;
end;

// _unit_TopologyAddExt
//
procedure _unit_TopologyAddExt(goHnd : Integer);
begin
   if _misc_GetBaseObjID(goHnd) = gc_baseid_obj then
   begin
      var posX : Float =  GetGameObjectPositionXByHandle(goHnd);
      var posZ : Float =  GetGameObjectPositionZByHandle(goHnd);
      var curZone : Integer = TopologyGetZoneIndex(posX, posZ);
      if curZone >= 0 then
      begin
         var pObj : Pointer = _unit_GetTObj(goHnd);
         var plInd : Integer = TObj(pObj).pl;
         if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
         begin
            var i : Integer;
            var danger : Float = _unit_GetDanger(goHnd);
            var conCount : Integer = TopologyGetZoneConnectionsCountByIndex(curZone);
            for i := 0 to conCount-1 do
            begin
               var conInd : Integer = TopologyGetZoneConnectionByIndex(curZone, i);
               var conDist : Integer = TopologyGetZoneConnectionDistByIndex(curZone, i);
               var pBuffer : Pointer = TopologyGetZoneBufferByIndex(conInd);
               if pBuffer <> nil then
               TTopZone(pBuffer).danger[plInd] := TTopZone(pBuffer).danger[plInd] + danger * Pow(gc_top_DangerDecFactor, conDist);
            end;

            var attract : Float = _unit_GetAttract(goHnd);
            if attract <> 0 then
            begin
               var pBuffer : Pointer = TopologyGetZoneBufferByIndex(curZone);
               if pBuffer <> nil then
               TTopZone(pBuffer).attract[plInd] := TTopZone(pBuffer).attract[plInd] + attract;
            end;

            TObj(pObj).topzone := curZone;
         end;
      end;
   end;
end;

// _unit_TopologyRemove
//
procedure _unit_TopologyRemove(goHnd : Integer);
begin
   if _misc_GetBaseObjID(goHnd) = gc_baseid_obj then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      var curZone : Integer = TObj(pObj).topzone;
      if curZone >= 0 then
      begin
         var plInd : Integer = TObj(pObj).pl;
         if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
         begin
            var pBuffer : Pointer = TopologyGetZoneBufferByIndex(curZone);
            if pBuffer <> nil then
            begin
               var danger : Float = _unit_GetDanger(goHnd);
               var attract : Float = _unit_GetAttract(goHnd);

               TTopZone(pBuffer).changeDanger[plInd] := TTopZone(pBuffer).changeDanger[plInd] - danger;
               TTopZone(pBuffer).changeAttract[plInd] := TTopZone(pBuffer).changeAttract[plInd] - attract;

               TTopZone(pBuffer).changed := true;

               if curZone < gInt_MinTopZoneChanged then
               gInt_MinTopZoneChanged := curZone;

               if curZone > gInt_MaxTopZoneChanged then
               gInt_MaxTopZoneChanged := curZone;
            end;
         end;

         TObj(pObj).topzone := -1;
      end;
   end;
end;

// _unit_TopologyRemoveExt
//
procedure _unit_TopologyRemoveExt(goHnd : Integer);
begin
   if _misc_GetBaseObjID(goHnd) = gc_baseid_obj then
   begin
      var posX : Float =  GetGameObjectPositionXByHandle(goHnd);
      var posZ : Float =  GetGameObjectPositionZByHandle(goHnd);
      var pObj : Pointer = _unit_GetTObj(goHnd);
      var curZone : Integer = TObj(pObj).topzone;
      if curZone >= 0 then
      begin
         var plInd : Integer = TObj(pObj).pl;
         if (plInd >= 0) and (plInd < gc_MaxPlayerCount) then
         begin
            var i : Integer;
            var danger : Float = _unit_GetDanger(goHnd);
            var conCount : Integer = TopologyGetZoneConnectionsCountByIndex(curZone);
            for i := 0 to conCount-1 do
            begin
               var conInd : Integer = TopologyGetZoneConnectionByIndex(curZone, i);
               var conDist : Integer = TopologyGetZoneConnectionDistByIndex(curZone, i);
               var pBuffer : Pointer = TopologyGetZoneBufferByIndex(conInd);
               if pBuffer <> nil then
               TTopZone(pBuffer).danger[plInd] := _misc_MaxFloat(0, TTopZone(pBuffer).danger[plInd] - danger * Pow(gc_top_DangerDecFactor, conDist));
            end;

            var attract : Float = _unit_GetAttract(goHnd);
            if attract <> 0 then
            begin
               var pBuffer : Pointer = TopologyGetZoneBufferByIndex(curZone);
               if pBuffer <> nil then
               TTopZone(pBuffer).attract[plInd] := _misc_MaxFloat(0, TTopZone(pBuffer).attract[plInd] - attract);
            end;
         end;
      end;
   end;
end;

// _unit_PathListAdd
//
procedure _unit_PathListAdd(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if (not TObj(pobj).bpathrequested) then
      begin
         if gObjProp[TObj(pObj).cid][TObj(pObj).id].media = gc_obj_media_water then
         gWaterPathList.Add(goHnd)
         else
         gGOPathList.Add(goHnd);

         TObj(pobj).bpathrequested := True;
      end;
   end
   else
   ErrorLog('_unit_PathListAdd : pobj=nil');
end;

// _unit_PathListRemove
//
procedure _unit_PathListRemove(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if (TObj(pobj).bpathrequested) then
      begin
         if gObjProp[TObj(pObj).cid][TObj(pObj).id].media = gc_obj_media_water then
         gWaterPathList.Remove(goHnd)
         else
         gGOPathList.Remove(goHnd);

         TObj(pobj).bpathrequested := False;
      end;
   end
   else
   ErrorLog('_unit_PathListRemove : pobj=nil');
end;

// _unit_ProcessTower
//
procedure _unit_ProcessTower(pEnemyInfo : Pointer; tInd : Integer);
begin
   if pEnemyInfo <> nil then
   begin
      var pTower : Pointer = TEnemyInfo(pEnemyInfo).towers.Get(tInd);
      if pTower <> nil then
      begin
         var goHnd : Integer = TTower(pTower).hnd;
         if goHnd <> 0 then
         begin
            var pObj : Pointer = _unit_GetTObj(goHnd);
            var pObjBase : Pointer = gPlayer[TObj(pObj).pl].objBase[TObj(pObj).cid][TObj(pObj).id];

            if gObjProp[TObj(pObj).cid][TObj(pObj).id].usage = gc_obj_usage_tower then
            begin
               var attPause : Float = TObjBase(pObjBase).weapon[0].pause;
               if TObj(pObj).individual.bEnabled and (attPause > 0) then
               attPause := attPause * TObj(pObj).individual.attackRate;
               var oldPause : Float = TTower(pTower).pause;

               if (TTower(pTower).radius <> TObjBase(pObjBase).weapon[0].radiusMax) or (TTower(pTower).pause <> attPause) or
               TTower(pTower).remove then
               begin
                  var oldRadius : Float = TTower(pTower).radius;
                  var oldPause : Float = TTower(pTower).pause;

                  var curRad : Float = TObjBase(pObjBase).weapon[0].radiusMax / gc_scangrid_size;
                  var oldRad : Float = oldRadius / gc_scangrid_size;
                  var maxRad : Float;
                  if TTower(pTower).remove then
                  maxRad := curRad
                  else
                  maxRad := _misc_MaxFloat(curRad, oldRad);

                  var iRad : Integer = Floor(maxRad);

                  var i, j : Integer;
                  var x : Float = GetGameObjectPositionXByHandle(goHnd);
                  var y : Float = GetGameObjectPositionZByHandle(goHnd);
                  var gridX, gridY : Integer;
                  _misc_PosToScanGridIndices(x, y, gridX, gridY);

                  var minX : Integer = Max(gridX-iRad, 0);
                  var maxX : Integer = Min(gridX+iRad, gc_scangrid_countx-1);

                  if attPause > 0 then
                  begin
                     var curDan : Float = 5 * 100 / attPause;
                     var oldDan : Float;
                     if oldPause > 0 then
                     oldDan := 5 * 100 / oldPause;

                     for i := minX to maxX do
                     begin
                        var size : Integer = MathCeil(Sqrt(Sqr(maxRad)-Sqr(i-gridX)));
                        var minY : Integer = Max(gridY-size, 0);
                        var maxY : Integer = Min(gridY+size, gc_scangrid_county-1);

                        for j := minY to maxY do
                        if Sqr(i-gridX) + Sqr(j-gridY) <= Sqr(maxRad) then
                        begin
                           var zone : Integer;
                           if TTower(pTower).remove then
                           zone := 4 // -curDan
                           else
                           begin
                              if oldRad = 0 then
                              zone := 2 // +curDan
                              else
                              if oldRad < 0 then
                              zone := 1 // +curDan-oldDan
                              else
                              begin
                                 if curRad > oldRad then
                                 begin
                                    if Sqr(i-gridX) + Sqr(j-gridY) <= Sqr(oldRad) then
                                    begin
                                       if oldDan > 0 then
                                       zone := 1; // +curDan-oldDan
                                    end
                                    else
                                    zone := 2; // +curDan
                                 end
                                 else
                                 begin
                                    if Sqr(i-gridX) + Sqr(j-gridY) <= Sqr(curRad) then
                                    begin
                                       if oldDan > 0 then
                                       zone := 1; // +curDan-oldDan
                                    end
                                    else
                                    begin
                                       if oldDan > 0 then
                                       zone := 3 // -oldDan
                                       else
                                       zone := 4; // -curDan
                                    end;
                                 end;
                              end;
                           end;

                           case zone of
                              1 : TEnemyInfo(pEnemyInfo).towerMap[i, j] := TEnemyInfo(pEnemyInfo).towerMap[i, j] + curDan - oldDan;
                              2 : TEnemyInfo(pEnemyInfo).towerMap[i, j] := TEnemyInfo(pEnemyInfo).towerMap[i, j] + curDan;
                              3 : TEnemyInfo(pEnemyInfo).towerMap[i, j] := TEnemyInfo(pEnemyInfo).towerMap[i, j] - oldDan;
                              4 : TEnemyInfo(pEnemyInfo).towerMap[i, j] := TEnemyInfo(pEnemyInfo).towerMap[i, j] - curDan;
                           end;
                        end;
                     end;
                  end;

                  TTower(pTower).update := false;
                  TTower(pTower).pause := attPause;
                  TTower(pTower).radius := TObjBase(pObjBase).weapon[0].radiusMax;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_IsGuard
//
function _unit_IsGuard(const goHnd : Integer) : Boolean;
begin
   Result := False;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var i : Integer;
      for i:=0 to gc_obj_MaxOrderCount-1 do
      if (TObj(pobj).orders[i].itype=gc_obj_order_type_none) then
      exit
      else
      if (TObj(pobj).orders[i].itype=gc_obj_order_type_guard) then
      begin
         Result := True;
         exit;
      end;
   end;
end;

// _unit_AIAddDefendingBuilding
//
function _unit_AIAddDefendingBuilding(plInd : Integer; goHnd : Integer) : Pointer;
begin
   Result := nil;
   var index : Integer = gPlayer[plInd].aiData.defbuildings.IndexByHandle(goHnd);
   if index=-1 then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      var pObjProp : Pointer = _unit_GetObjProp(goHnd);
      if TObjProp(pObjProp).bCapture then
      begin
         var pDefBuilding : Pointer = gPlayer[plInd].aiData.defbuildings.Add;
         var airole : Integer = TObjProp(pObjProp).airole;
         var aiPriority : Integer = 1;
         case airole of
            gc_ai_unit_center   : aiPriority:=3;
            gc_ai_unit_mill     : aiPriority:=2;
            gc_ai_unit_store    : aiPriority:=2;
            gc_ai_unit_academi  : aiPriority:=1;
            gc_ai_unit_artdepo  : aiPriority:=1;
            gc_ai_unit_market   : aiPriority:=2;
            gc_ai_unit_smith    : aiPriority:=3;
            gc_ai_unit_house    : aiPriority:=1;
            gc_ai_unit_minegold : aiPriority:=2;
            gc_ai_unit_mineiron : aiPriority:=2;
            gc_ai_unit_minecoal : aiPriority:=2;
         end;
         TAiProtectedBuilding(pDefBuilding).aipriority := aiPriority;
         TAiProtectedBuilding(pDefBuilding).targetHnd := goHnd;
         Result:=pDefBuilding;
      end;
   end
   else
   Result:=gPlayer[plInd].aiData.defbuildings.Get(index);
end;

// _unit_AIRemoveDefendingBuilding
//
procedure _unit_AIRemoveDefendingBuilding(plInd : Integer; goHnd : Integer);
begin
   var i : Integer;
   var index : Integer = -1;
   var pDefBuilding : Pointer;
   for i := 0 to gPlayer[plInd].aiData.defbuildings.GetCount-1 do
   begin
      pDefBuilding := gPlayer[plInd].aiData.defbuildings.get(i);
      if (pDefBuilding <> nil) and (TAiProtectedBuilding(pDefBuilding).targetHnd=goHnd) then
      begin
         index:=i;
         break;
      end;
   end;
   if (index>=0) then
   begin
      for i := 0 to TAiProtectedBuilding(pDefBuilding).defenders.GetCount-1 do
      begin
         var goHnd : Integer = TAiProtectedBuilding(pDefBuilding).defenders.Get(i);
         if (goHnd<>0) then
         begin
            gPlayer[plInd].aiData.guards.Remove(goHnd);
            gPlayer[plInd].aiData.defenders.Add(goHnd);
         end
         else
         ErrorLog('_unit_AIRemoveDefendingBuilding hnd=0');
      end;
      gPlayer[plInd].aiData.defbuildings.Delete(index);
   end;
end;

procedure _ai_DefBuildingRemoveGuard(plInd, goHnd : Integer);
begin
   var pDefBuilding : Pointer;
   var i, j : Integer;
   for [MAIN]i:=0 to gPlayer[plInd].aiData.defbuildings.GetCount-1 do
   begin
      pDefBuilding := gPlayer[plInd].aiData.defbuildings.Get(i);
      if (pDefBuilding <> nil) then
      begin
         for j:=0 to TAiProtectedBuilding(pDefBuilding).defenders.GetCount-1 do
         if TAiProtectedBuilding(pDefBuilding).defenders.Get(j) = goHnd then
         begin
            TAiProtectedBuilding(pDefBuilding).defenders.Delete(j);
            break(MAIN);
         end;
      end;
   end;
end;

// _unit_AddToAIData
//
procedure _unit_AddToAIData(goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var pl : Integer = TObj(pObj).pl;
      var pObjProp : Pointer = gObjProp[TObj(pObj).cid][TObj(pObj).id];
      var airole : Integer = TObjProp(pObjProp).airole;
      if (not TObjProp(pObjProp).bBuilding) then
      begin
         var usage : Integer = TObjProp(pObjProp).usage;
         if (usage <> gc_obj_usage_peasant) then
         begin
            if _unit_IsOfficerOrDrummer(goHnd) then
            begin
               // TODO: not finished. Code for officer and drummer
            end
            else
            begin
               var bai : Boolean = gPlayer[pl].bai;
               case TObjProp(pObjProp).media of
                  gc_obj_media_land : begin
                     if not _unit_IsGuard(goHnd) then
                     begin
                        //unit only for defending houses:
                        if (airole=gc_ai_unit_light_dip) then
                        gPlayer[pl].aiData.defenders.Add(goHnd)
                        else
                        begin
                           case usage of
                              gc_obj_usage_mortar, gc_obj_usage_cannon, gc_obj_usage_fasthorse,
                              gc_obj_usage_archer, gc_obj_usage_supermortar, gc_obj_usage_mcannon : begin
                                 gPlayer[pl].aiData.agressors.Add(goHnd);
                              end;
                              gc_obj_usage_hardhorse, gc_obj_usage_shooter, gc_obj_usage_grenadier, gc_obj_usage_horseshooter : begin
                                 if bai then
                                 begin
                                    if (gPlayer[pl].aidata.guardsCount>0) and (gPlayer[pl].aiData.guards.GetCount < gc_ai_max_guards) then
                                    gPlayer[pl].aiData.defenders.Add(goHnd)
                                    else
                                    gPlayer[pl].aiData.agressors.Add(goHnd);
                                 end
                                 else
                                 if ((usage=gc_obj_usage_shooter) or (usage=gc_obj_usage_grenadier)) and (gPlayer[pl].aiData.guards.GetCount < gc_ai_max_guards) and (RandomExt < _misc_RandToRandom(3000)) then
                                 gPlayer[pl].aiData.defenders.Add(goHnd)
                              end
                              else
                              begin
                                 if TObjProp(pObjProp).bPriest or ((gPlayer[pl].aiData.freewarriors.GetCount > 15) and (gPlayer[pl].aiData.guards.GetCount > gc_ai_max_guards)) then
                                 gPlayer[pl].aiData.agressors.Add(goHnd)
                                 else
                                 begin
                                    if bai then
                                    begin
                                       if (RandomExt < _misc_RandToRandom(10000)) and (gPlayer[pl].aiData.freewarriors.GetCount < 20) then
                                       gPlayer[pl].aiData.freewarriors.Add(goHnd)
                                       else
                                       if (RandomExt < _misc_RandToRandom(6000)) and (gPlayer[pl].aiData.guards.GetCount < gc_ai_max_guards) then
                                       gPlayer[pl].aiData.defenders.Add(goHnd)
                                       else
                                       gPlayer[pl].aiData.agressors.Add(goHnd);
                                    end
                                    else
                                    begin
                                       if (usage=gc_obj_usage_lightinfantry) and (gPlayer[pl].aiData.guards.GetCount < gc_ai_max_guards) and (RandomExt < _misc_RandToRandom(20000)) then
                                       gPlayer[pl].aiData.defenders.Add(goHnd);
                                    end;
                                 end;
                              end;
                           end;
                        end;
                     end;
                  end;
                  gc_obj_media_water : begin
                     // TODO: not finished. Code for water units
                  end;
               end;
            end;
         end;
      end
      else
      _unit_AIAddDefendingBuilding(pl, gohnd);
   end;
end;

// _unit_RemoveFromAIData
//
procedure _unit_RemoveFromAIData(goHnd : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var pl : Integer = TObj(pObj).pl;
   if gPlayer[pl].bAI then
   begin
      var pObjProp : Pointer = gObjProp[TObj(pObj).cid][TObj(pObj).id];
      if not TObjProp(pObjProp).bBuilding then
      begin
         if TObjProp(pObjProp).usage <> gc_obj_usage_peasant then
         begin
            gPlayer[pl].aiData.defenders.Remove(goHnd);
            var index : Integer = gPlayer[pl].aiData.guards.IndexOf(goHnd);
            if index <> -1 then
            begin
               gPlayer[pl].aiData.guards.Delete(index);
               _ai_DefBuildingRemoveGuard(pl, goHnd);
            end;
            gPlayer[pl].aiData.agressors.Remove(goHnd);
            gPlayer[pl].aiData.freewarriors.Remove(goHnd);
         end;
      end
      else
      _unit_AIRemoveDefendingBuilding(pl, gohnd);
   end;
end;

// _unit_RemoveObjFromPlayerLists
//
procedure _unit_RemoveObjFromPlayerLists(goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var cid : Integer = TObj(pobj).cid;
   var id : Integer = TObj(pobj).id;
   var pl : Integer = TObj(pobj).pl;
   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   if (TObjProp(pobjprop).bbuilding) then
   begin
      gPlayer[pl].lists.buildings.Remove(goHnd);
      gPlayer[pl].lists.deluids.Remove(goHnd);
   end;
   case (TObjProp(pobjprop).usage) of
      gc_obj_usage_storage, gc_obj_usage_center, gc_obj_usage_mill : gPlayer[pl].lists.storehouses.Remove(goHnd);
      gc_obj_usage_port : gPlayer[pl].lists.ports.Remove(goHnd);
      gc_obj_usage_tower : begin
         if _misc_IsProcessAI{_net_IsOffline or _net_IsServer} then
         begin
            var i, j : Integer;
            for i := gEnemyInfoList.GetCount-1 downto 0 do
            begin
               var pEnInfo : Pointer = gEnemyInfoList.Get(i);
               for j := TEnemyInfo(pEnInfo).towers.GetCount-1 downto 0 do
               begin
                  var pTower : Pointer = TEnemyInfo(pEnInfo).towers.Get(j);
                  if TTower(pTower).hnd = goHnd then
                  begin
                     TTower(pTower).remove := true;
                     _unit_ProcessTower(pEnInfo, j);
                     TEnemyInfo(pEnInfo).towers.Delete(j);
                  end;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_AddObjToPlayerLists
//
procedure _unit_AddObjToPlayerLists(goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var cid : Integer = TObj(pobj).cid;
   var id : Integer = TObj(pobj).id;
   var pl : Integer = TObj(pobj).pl;
   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   if (TObjProp(pobjprop).bbuilding) then
   gPlayer[pl].lists.buildings.Add(goHnd); // добавление из списка построек
   case (TObjProp(pobjprop).usage) of
      gc_obj_usage_storage, gc_obj_usage_center, gc_obj_usage_mill : gPlayer[pl].lists.storehouses.Add(goHnd);
      gc_obj_usage_port : gPlayer[pl].lists.ports.Add(goHnd);
      gc_obj_usage_tower : begin
         if _misc_IsProcessAI{_net_IsOffline or _net_IsServer} then
         begin
            var i : Integer;
            for i := 1 to gc_MaxPlayerCount do
            if i <> gPlayer[pl].team then
            begin
               var pEnemyInfo : Pointer = _misc_GetTeamEnemyInfo(i);
               if (pEnemyInfo <> nil) and TEnemyInfo(pEnemyInfo).enabled then
               TEnemyInfo(pEnemyInfo).towers.Add(goHnd);
            end;
         end;
      end;
   end;
end;

// _unit_AddObjToPlayerCounters
//
procedure _unit_AddObjToPlayerCounters(goHnd : Integer; bbuilt, bcaptured, bcustom : Boolean);
begin
   // добавление юнита в счетчики игрока
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   var pl : Integer = TObj(pobj).pl;
   if (pl>=0) and (pl<gc_MaxPlayerCount) then
   begin
      var cid : Integer = TObj(pobj).cid;
      var id : Integer = TObj(pobj).id;
      var bbuilding : Boolean = (TObjProp(pobjprop).bbuilding);
      if (not bbuilding) or (bbuilt) or (bcaptured and TObj(pobj).bbuilt) then
      begin
         // счетчик, количество построенных юнитов/построек данного типа
         gPlayer[pl].counter.built[cid][id] := gPlayer[pl].counter.built[cid][id]+1;

         // счетчик, доступных жилых мест
         if (TObjProp(pobjprop).farm>0) then
         gPlayer[pl].farm := gPlayer[pl].farm+TObjProp(pobjprop).farm;

         // счетчик, потребления ресурсов (помимо еды на питание)
         var i : Integer;
         for i:=0 to gc_ResCount-1 do
         gPlayer[pl].counter.resconsume[i] := gPlayer[pl].counter.resconsume[i]+TObjProp(pobjprop).consume[i];

         // счетчик, статистики после игры
         if (not bcaptured) and (not bcustom) then
         gPlayer[pl].stat.produced[cid][id] := gPlayer[pl].stat.produced[cid][id]+1;

         if (not bbuilding) then
         begin
            // счетчик, использования жилых мест и счетчик потребления еды
            gPlayer[pl].counter.farmused := gPlayer[pl].counter.farmused+1;
            if (not TObjProp(pobjprop).bnohungry) then
            gPlayer[pl].counter.resconsume[gc_resource_type_food] := gPlayer[pl].counter.resconsume[gc_resource_type_food]+gc_obj_foodperunit;
         end
         else
         _unit_AddObjToPlayerLists(goHnd);

         if (TObjProp(pobjprop).bartdepo) then
         begin
            var i : Integer;
            for i:=0 to gc_MaxArtilleryType-1 do
            gPlayer[pl].artlimit[i] := gPlayer[pl].artlimit[i]+TObjProp(pobjprop).artdepo[i];
         end;
         if (TObjProp(pobjprop).bartillery) and (TObjProp(pobjprop).artind>gc_obj_artind_none) then
         gPlayer[pl].artcount[TObjProp(pobjprop).artind] := gPlayer[pl].artcount[TObjProp(pobjprop).artind]+1;

         // счетчик, очков игрока
         var scoremodifier : Integer;
         if (bcaptured) then
         scoremodifier := 5
         else
         scoremodifier := 1;
         gPlayer[pl].counter.scores := gPlayer[pl].counter.scores+TObjProp(pobjprop).score*scoremodifier;
      end;

      if (bcaptured) or (not bbuilding) or (not bbuilt) or (bcustom) then
      begin
         // счетчик, количество юнитов/построек данного типа
         gPlayer[pl].counter.all[cid][id] := gPlayer[pl].counter.all[cid][id]+1;

         // счетчик, общего количества юнитов и построек всех типов у игрока
         gPlayer[pl].counter.total := gPlayer[pl].counter.total+1;

         // AI data
         if (_misc_IsProcessAI) and (gPlayer[pl].bai) or (not gPlayer[pl].bai and gPlayer[pl].playeradviser.adviser.benable) then
         _unit_AddToAIData(goHnd);

         _unit_ScanGridAddUnit(goHnd);
      end;

      // set uniform color player
      _misc_ApplyKeyColorRecursiveChild(goHnd, gMap.players[pl].color);

      if (bcaptured) or (not bbuilding) or (bbuilt) or (bcustom) then
      _unit_TopologyAdd(goHnd);
   end;
end;

// _unit_RemoveObjFromPlayerCounters
//
procedure _unit_RemoveObjFromPlayerCounters(goHnd : Integer; bbuilt, bcaptured : Boolean);
begin
   // удаление юнита из счетчиков игрока, происходит при уничтожении юнита.
   var pobj : Pointer = _unit_GetTObj(goHnd);

   if (TObj(pobj).bdead) then
   ErrorLog('_unit_RemoveObjFromPlayerCounters : already bdead. gohnd='+IntToStr(goHnd));

   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   var pl : Integer = TObj(pobj).pl;
   if (pl>=0) and (pl<gc_MaxPlayerCount) then
   begin
      var cid : Integer = TObj(pobj).cid;
      var id : Integer = TObj(pobj).id;
      var bbuilding : Boolean = (TObjProp(pobjprop).bbuilding);
      if (not bbuilding) or bbuilt or (bcaptured and TObj(pobj).bbuilt) then
      begin
         // счетчик, количество построенных юнитов/построек данного типа
         gPlayer[pl].counter.built[cid][id] := gPlayer[pl].counter.built[cid][id]-1;

         // счетчик, доступных жилых мест
         if (TObjProp(pobjprop).farm>0) then
         gPlayer[pl].farm := gPlayer[pl].farm-TObjProp(pobjprop).farm;

         // счетчик, потребления ресурсов (помимо еды на питание)
         var i : Integer;
         for i:=0 to gc_ResCount-1 do
         gPlayer[pl].counter.resconsume[i] := gPlayer[pl].counter.resconsume[i]-TObjProp(pobjprop).consume[i];

         // счетчик, статистики после игры
         if (not bcaptured) then
         gPlayer[pl].stat.killed[cid][id] := gPlayer[pl].stat.killed[cid][id]+1;

         if (not bbuilding) then
         begin
            // счетчик, использования жилых мест и счетчик потребления еды
            gPlayer[pl].counter.farmused := gPlayer[pl].counter.farmused-1;
            if (not TObjProp(pobjprop).bnohungry) then
            gPlayer[pl].counter.resconsume[gc_resource_type_food] := gPlayer[pl].counter.resconsume[gc_resource_type_food]-gc_obj_foodperunit;

            if (TObj(pObj).squad>-1) then
            begin
               var ind : Integer = TObj(pObj).squad;
               var pSquad : Pointer = gPlayer[TObj(pobj).pl].squads.Get(ind);
               if (_unit_GetTagStateByType(goHnd, gc_statetag_move) = gc_statetag_move_walk) and (TSquad(pSquad).fMoveCount > 0) then
               TSquad(pSquad).fMoveCount := TSquad(pSquad).fMoveCount-1;

               TSquad(pSquad).Remove(goHnd);
               if TSquad(pSquad).GetCount <= 0 then
               begin
                  if (_net_IsServer) or ((_net_IsOffline) and (not _net_IsReplay)) then
                  begin
                     var plHnd : Integer = GetPlayerHandleByIndex(pl);
                     _misc_DisbandSquad(plHnd, ind, true);
                  end;
               end;
            end;
         end
         else
         _unit_RemoveObjFromPlayerLists(goHnd);

         if (TObjProp(pobjprop).bartdepo) then
         begin
            var i : Integer;
            for i:=0 to gc_MaxArtilleryType-1 do
            gPlayer[pl].artlimit[i] := gPlayer[pl].artlimit[i]-TObjProp(pobjprop).artdepo[i];
         end;
         if (TObjProp(pobjprop).bartillery) and (TObjProp(pobjprop).artind>gc_obj_artind_none) then
         gPlayer[pl].artcount[TObjProp(pobjprop).artind] := gPlayer[pl].artcount[TObjProp(pobjprop).artind]-1;

         // счетчик, очков игрока
         var scoremodifier : Integer;
         if (bcaptured) then
         scoremodifier := 5
         else
         if (gPlayer[pl].brebellion) and (TObjProp(pobjprop).bmercenary) then
         scoremodifier := 3
         else
         scoremodifier := 2;
         gPlayer[pl].counter.scores := gPlayer[pl].counter.scores-TObjProp(pobjprop).score*scoremodifier;
         if (gPlayer[pl].counter.scores<0) then
         gPlayer[pl].counter.scores := 0;
      end;

      if (TObjProp(pobjprop).usage=gc_obj_usage_hardwall) or (TObjProp(pobjprop).usage=gc_obj_usage_weakwall) then
      gWallSystem.RemoveHandle(pl, goHnd);

      if (gPlayer[pl].bai) and bbuilding then
      begin
         var i : Integer;
         for i:=0 to gc_ai_max_construction_sites-1 do
         if (gPlayer[pl].aidata.buildprojects[i].gohnd=goHnd) then
         gPlayer[pl].aidata.buildprojects[i].ResetBuildingProject;
      end;

      // AI data
      if _misc_IsProcessAI then
      _unit_RemoveFromAIData(goHnd);

      // счетчик, количество юнитов/построек данного типа
      gPlayer[pl].counter.all[cid][id] := gPlayer[pl].counter.all[cid][id]-1;

      // счетчик, общего количества юнитов и построект всех типов у игрока
      gPlayer[pl].counter.total := gPlayer[pl].counter.total-1;

      _unit_ScanGridRemoveUnit(goHnd);
      _unit_TopologyRemove(goHnd);

      if (gLanSyncUnitsParamsUIDList.GetCount>0) then
      gLanSyncUnitsParamsUIDList.Remove(GetGameObjectUniqueIdByHandle(goHnd));

      if GetPlayerHandleByIndex(pl)=GetPlayerHandleInterfaceIO then
      begin
         if (GetGameObjectPickedByHandle(goHnd)) then
         begin
            gSelectedObjects.Remove(goHnd);
            SetGameObjectPickedByHandle(goHnd, False);
            gbool_gui_update_unitcontrol := True;
            gbool_gui_update_rallypoints := True;
         end;
      end;
      var i : Integer;
      if (TObj(pobj).ctrlgroupmask>0) then
      begin
         for i:=0 to 9 do
         begin
            if (TObj(pobj).ctrlgroupmask and (1 shl i)<>0) then
            gCtrlGroups[pl][i].Remove(goHnd);
         end;
         TObj(pobj).ctrlgroupmask := 0;
      end;
   end;
end;

// _unit_SetArmyOrderTarget
//
procedure _unit_SetArmyOrderTarget(pArmy : Pointer; goHnd : Integer);
begin
   if pArmy <> nil then
   begin
      var goUID : Integer;
      if (goHnd<>0) then
      goUID := GetGameObjectUniqueIdByHandle(goHnd);
      TArmy(pArmy).fOrder.targetUID := goUID;
   end;
end;

// _unit_SetArmyOrderTargetExt
//
procedure _unit_SetArmyOrderTargetExt(pArmy : Pointer; goHnd, tag : Integer);
begin
   if pArmy <> nil then
   begin
      _unit_SetArmyOrderTarget(pArmy, goHnd);
      TArmy(pArmy).fOrder.tag := tag;
   end;
end;

// _unit_SelectedGroupRemoveUnit
//
procedure _unit_SelectedGroupRemoveUnit(hnd : Integer);
begin
   var grHnd : Integer = GetGameObjectGrHandleByHandle(hnd);
   if (grHnd<>0) and (GetCountOfPlayers>=gc_MaxPlayerCount) then
   begin
      var plHnd : Integer = GetGameObjectPlayerHandleByHandle(hnd);
      var selGrHnd : Integer = GetPlayerGrHandleByHandle(plHnd, gc_group_selected);
      if (grHnd=selGrHnd) then
      GroupRemoveGameObjectByHandle(selGrHnd, hnd);
   end;
end;

// _unit_SearchResourceInRadius
//
function _unit_SearchResourceInRadius(const goHnd : Integer; const pX, pZ, rad, waitrnd : Float; const restype : Integer; const bBestPossible : Boolean) : Integer;
begin
   Result := 0;
   var standtime : Float;
   if (goHnd<>0) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) then
      begin
         standtime := TObj(pobj).standtime;
         if (standtime>0.1) then
         begin
            if (random<waitrnd) then
            begin
            end
            else
            exit;
         end;
      end;
   end;
   //var profHnd : Integer = _misc_ProfilerBeginHnd('srir');

   var trgHnd : Integer;
   var minRTrgDist, minRMyDist : Float = -1;
   var minRTrgHnd : Integer;

   var minBestPossibleTrgDist : Float = -1;
   var minBestPossibleTrgHnd : Integer;

   var tmpRDist : Float;
   var stofactor, attfactor, mydstfactor, resdstfactor, mydstDelta : Float;
   var dstMy, dstRes, trX, trZ : Float;

   var myX, myZ : Float;
   var mySTO : Integer;
   if (goHnd<>0) then
   begin
      myX := GetGameObjectPositionXByHandle(goHnd);
      myZ := GetGameObjectPositionZByHandle(goHnd);
      mySTO := GetGameObjectSTOHandleByHandle(goHnd);
   end
   else
   begin
      myX := pX;
      myZ := pZ;
   end;

   var maxatt, midatt, maxsto : Integer;
   case restype of
      gc_resource_type_none : begin
         midatt := 1;
         maxatt := gc_gameplay_resource_maxattackers_none;
         maxsto := 7;
      end;
      gc_resource_type_food : begin
         midatt := 1;
         maxatt := gc_gameplay_resource_maxattackers_food;
         maxsto := 4;
      end;
      gc_resource_type_wood : begin
         midatt := 1;
         maxatt := gc_gameplay_resource_maxattackers_wood;
         maxsto := 3;
      end;
      gc_resource_type_stone : begin
         midatt := 1;
         maxatt := gc_gameplay_resource_maxattackers_stone;
         maxsto := 7;
      end;
   end;
   _misc_MakeListEnvironmentInRadius(pX, pZ, rad, True, (restype=gc_resource_type_none) or (restype=gc_resource_type_food), (restype=gc_resource_type_none) or (restype=gc_resource_type_wood), (restype=gc_resource_type_none) or (restype=gc_resource_type_stone), False, gIntegerList);
   var bskipcheck : Boolean = (random>0.75);
   var newind : Integer;
   var rndind : Integer;
   var count : Integer = gIntegerList.GetCount;
   if (count>0) then
   begin
      rndind := floor(random*count);
      var i : Integer;
      for i:=0 to count-1 do
      begin
         newind := (rndind+i) mod count;
         trgHnd := gIntegerList.Get(newind);
         if (trgHnd<>0) then
         begin
            var stocount, attcount : Integer;
            trX := GetGameObjectPositionXByHandle(trgHnd);
            trZ := GetGameObjectPositionZByHandle(trgHnd);
            dstMy := VectorDistance(myX, 0, myZ, trX, 0, trZ);
            dstRes := VectorDistance(pX, 0, pZ, trX, 0, trZ);

            var pstolist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argenv_stolist);
            var pattlist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argenv_attlist);
            attcount := TIntegerList(pattlist).GetCount;
            if (mySTO=trgHnd) then
            stocount := 0
            else
            stocount := TIntegerList(pstolist).GetCount;
            if (attcount>=midatt) then attfactor := 1+attcount*1.5;
            if (stocount>=midatt) then stofactor := 1+stocount*1.5;
            mydstfactor := (dstMy/5);
            resdstfactor := (dstRes/4);
            tmpRDist := (1+mydstfactor)*(1+resdstfactor)*(1+stofactor)*(1+attfactor);

            var pres : Pointer = _res_GetTRes(trgHnd);
            if (pres<>nil) and (TRes(pres).brised) then
            begin
               if ((bskipcheck) or ((attcount<maxatt) and (stocount<maxsto)) and ((tmpRDist<minRTrgDist) or (minRTrgHnd=0))) then
               begin
                  if (TRes(pres).itype<>gc_resource_type_none) and ((restype=gc_resource_type_none) or (restype=TRes(pres).itype)) then
                  begin
                     minRTrgDist := tmpRDist;
                     minRMyDist := dstMy;
                     minRTrgHnd := trgHnd;
                  end;
               end
               else
               if (bBestPossible) then
               begin
                  if ((tmpRDist<minBestPossibleTrgDist) or (minBestPossibleTrgHnd=0)) then
                  begin
                     minBestPossibleTrgDist := -1;
                     minBestPossibleTrgHnd := trgHnd;
                  end;
               end;
            end;
         end
         else
         ErrorLog('_unit_SearchResourceInRadius : trgHnd=0');
      end;
   end;
   if (minRTrgHnd<>0) then
   Result := minRTrgHnd
   else
   if (bBestPossible) and (minBestPossibleTrgHnd<>0) then
   Result := minBestPossibleTrgHnd;

   //_misc_ProfilerEndHnd(profHnd);
end;

// _unit_GetExitPoint
//
function _unit_GetExitPoint(goHnd, ind : Integer; rotate : Boolean; var x, y, z : Float) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (ind >= 0) and (pObj <> nil) then
   begin
      var count : Integer = gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitCount;
      if (ind < count) then
      begin
         x := gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitPoints[ind].x;
         y := gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitPoints[ind].y;
         z := gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitPoints[ind].z;
         if rotate and _unit_IsUnit(goHnd) then
         begin
            var dirX : Float = GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
            var dirZ : Float = GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
            var angle : Float = _misc_GetDirAngleToXVector(dirX, dirZ);
            VectorRotateY(x, y, z, angle);
         end;
         Result := true;
      end
      else
      begin
         x := 0;
         y := 0;
         z := 0;
         Result := false;
      end;
   end
   else
   begin
      x := 0;
      y := 0;
      z := 0;
      Result := false;
   end;
end;

// _unit_GetLastExitPoint
//
function _unit_GetLastExitPoint(goHnd : Integer; rotate : Boolean; var x, y, z : Float) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   begin
      var count : Integer = gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitCount;
      Result := _unit_GetExitPoint(goHnd, count-1, rotate, x, y, z);
   end
   else
   begin
      x := 0;
      y := 0;
      z := 0;
      Result := false;
   end;
end;

// _unit_GetAbsExitPoint
//
function _unit_GetAbsExitPoint(goHnd, ind : Integer; rotate : Boolean; var x, y, z : Float) : Boolean;
begin
   GetGameObjectAbsolutePositionByHandle(goHnd, x, y, z);
   var px, py, pz : Float;
   Result := _unit_GetExitPoint(goHnd, ind, rotate, px, py, pz);
   x := x + px;
   y := y + py;
   z := z + pz;
end;

// _unit_GetAbsLastExitPoint
//
function _unit_GetAbsLastExitPoint(goHnd : Integer; rotate : Boolean; var x, y, z : Float) : Boolean;
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   begin
      var count : Integer = gCustomObjPoints[TObj(pObj).cid, TObj(pObj).id].exitCount;
      Result := _unit_GetAbsExitPoint(goHnd, count-1, rotate, x, y, z);
   end
   else
   begin
      GetGameObjectAbsolutePositionByHandle(goHnd, x, y, z);
      Result := false;
   end;
end;

// _unit_GetBuildingExitData
//
procedure _unit_GetBuildingExitData(exitGOHnd, buildHnd : Integer; checkRes : Boolean; var resHnd : Integer; var exitX, exitZ : Float);
begin
   var y : Float;
   _unit_GetAbsLastExitPoint(buildHnd, true, exitX, y, exitZ);

   //if TObj(pBuildObj).bRally then
   begin
      var pBuildObj : Pointer = _unit_GetTObj(buildHnd);
      var rallyX : Float = TObj(pBuildObj).rallytmpx;
      var rallyY : Float = TObj(pBuildObj).rallytmpy;

      if checkRes then
      begin
         var pExitObj : Pointer = _unit_GetTObj(exitGOHnd);
         if (gObjProp[TObj(pExitObj).cid][TObj(pExitObj).id].usage = gc_obj_usage_peasant) then
         begin
            resHnd := _unit_SearchResourceInRadius(exitGOHnd, rallyX, rallyY, gc_obj_rally_resource_searchradius, gc_obj_res_searchwaitrnd, gc_resource_type_none, False);
            if resHnd = 0 then
            resHnd := _unit_SearchResourceInRadius(exitGOHnd, rallyX, rallyY, gc_obj_rally_resource_searchradius*1.5, gc_obj_res_searchwaitrnd, gc_resource_type_none, True);
         end;
      end;

      if resHnd = 0 then
      begin
         exitX := rallyX;
         exitZ := rallyY;
      end;
   end;
end;

// _unit_AddOrder
//
function _unit_AddOrder(const goHnd, {uid, next,} itype{, priority}, itrghnd : Integer; const ix, iy, idx, idy, idir, iz : Float; const iupgradeid : Integer; const iproduceid, irestype, iamount, iprogress : Integer; const bClearOrders, bDoFirst : Boolean) : Pointer;
begin
   // универсальная функция добавления приказа юнита
   Result := nil;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if (not TObj(pobj).bdead) then
      begin
         var ind : Integer = -1;
         var infiniteorderind : Integer = -1;
         if (bClearOrders) then
         begin
            ind := 0;
            _unit_ClearOrders(goHnd);
         end;
         if (bDoFirst) then
         begin
            ind := 0;
            if (TObj(pobj).orders[0].itype=gc_obj_order_type_gainres) and (itype=gc_obj_order_type_gainres) then
            _unit_SetOrderTrg(goHnd, 0, 0, True);
            TObj(pobj).orders[0].bexecute := False;
            if TObj(pobj).orders[0].itype<>gc_obj_order_type_none then
            _unit_OrdersOffset(goHnd, 0, 1);
         end
         else
         if (not bClearOrders) then
         begin
            var bfound : Boolean;
            var tmpind : Integer = -1;
            var i : Integer;
            for i:=0 to gc_obj_MaxOrderCount-1 do
            begin
               tmpind := i;
               if (TObj(pobj).orders[i].itype=gc_obj_order_type_none) and (not bfound) then
               begin
                  ind := i;
                  bfound := True;
               end
               else
               if (TObj(pobj).orders[i].itype=gc_obj_order_type_gainres) and (infiniteorderind=-1) then
               infiniteorderind := i;
            end;
         end;

         if (infiniteorderind>-1) then
         begin
            if (TObj(pobj).orders[infiniteorderind].itype=gc_obj_order_type_gainres) then
            _unit_SetOrderTrg(goHnd, infiniteorderind, 0, True);
            TObj(pobj).orders[infiniteorderind].bexecute := False;
            _unit_OrdersOffset(goHnd, infiniteorderind, 1);
            ind := infiniteorderind;
         end;

         if (ind>=0) then
         begin
            var porder : Pointer = TObj(pobj).orders[ind]; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(porder);
            var porderinfo : Pointer = TObj(pobj).orders[ind].info; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(porderinfo);
            //TOrder(porder).uid := 0;
            //TOrder(porder).next := 0;
            TOrder(porder).itype := itype;
            TOrder(porder).bexecute := False;
            TOrder(porder).bremove := False;
            TOrder(porder).static := false;
            //TOrder(porder).priority := 0;
            //TOrderInfo(porderinfo).trg := itrghnd;
            _unit_SetOrderTrg(goHnd, ind, itrghnd, True);
            TOrderInfo(porderinfo).x := ix;
            TOrderInfo(porderinfo).y := iy;
            TOrderInfo(porderinfo).dx := idx;
            TOrderInfo(porderinfo).dy := idy;
            TOrderInfo(porderinfo).dir := idir;
            TOrderInfo(porderinfo).z := iz;
            TOrderInfo(porderinfo).upgradeid := iupgradeid;
            TOrderInfo(porderinfo).produceid := iproduceid;
            TOrderInfo(porderinfo).restype := irestype;
            TOrderInfo(porderinfo).amount := iamount;
            TOrderInfo(porderinfo).progress := iprogress;

            Result := pOrder;
         end;
      end
      else
      ErrorLog('_unit_AddOrder : bdead unit try to add order : goHnd='+IntToStr(goHnd)+' squad='+IntToStr(TObj(pobj).squad));
   end;
end;

//_unit_OrderDoFishing
//
procedure _unit_OrderDoFishing(const goHnd : Integer);
begin
   var bDoFirst : Boolean = True;
   var posX : Float = GetGameObjectPositionXByHandle(goHnd);// + random*10;
   var posZ : Float = GetGameObjectPositionZByHandle(goHnd);// + random*10;
   var angle : Float = random*180;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (pObj<>nil) then
   begin
      TObj(pObj).restype := gc_resource_type_food;
      _unit_AddOrder(goHnd, gc_obj_order_type_fishing, 0, posX, posZ, 0, 0, angle, 0, 0, 0, gc_resource_type_food, 0, 0, False, bDoFirst);
   end;
end;

// _unit_OrderMove
//
function _unit_OrderMove(goHnd : Integer; posX, posZ, dirX, dirZ : Float; mode : Integer; bDoFirst : Boolean) : Pointer;
begin
   var angle : Float;

   if VectorLength(dirX, 0, dirZ) > 0 then
   begin
      angle := VectorAngle(1, 0, 0, dirX, 0, dirZ);
      var x, y, z : Float;
      VectorCross(1, 0, 0, dirX, 0, dirZ, x, y, z);
      if y < 0 then
      angle := -angle;
   end;

   Result := _unit_AddOrder(goHnd, gc_obj_order_type_move, 0, posX, posZ, 0, 0, angle, 0, 0, 0, 0, 0, mode, False, bDoFirst);
end;

// _unit_OrderLeave
//
procedure _unit_OrderLeave(goHnd, trgHnd : Integer; ordX, ordZ : Float);
begin
   var pTrgObj : Pointer = _unit_GetTObj(trgHnd);
   if pTrgObj <> nil then
   begin
      var tcid : Integer = TObj(pTrgObj).cid;
      var tid : Integer = TObj(pTrgObj).id;
      var eCount : Integer = gCustomObjPoints[tcid, tid].exitCount;
      var pTrgObjProp : Pointer = _unit_GetObjProp(trgHnd);

      var x, y, z : Float;
      if TObjProp(pTrgObjProp).usage = gc_obj_usage_port then
      _misc_GetPortResourcePoint(trgHnd, x, z, true)
      else
      _unit_GetAbsLastExitPoint(trgHnd, true, x, y, z);

      SetGameObjectPositionByHandle(goHnd, x, y, z);

      var angle : Float;
      if (not TObj(pTrgObj).bRally) and (eCount > 1) then
      begin
         var x, y, z, x1, y1, z1 : Float;
         _unit_GetExitPoint(trgHnd, eCount-1, true, x, y, z);
         _unit_GetExitPoint(trgHnd, eCount-2, true, x1, y1, z1);
         angle := _misc_VectorAngle(1, 0, x-x1, z-z1);
      end;

      _unit_AddOrder(goHnd, gc_obj_order_type_leavebuilding, trgHnd, ordX, ordZ, 0, 0, angle, 0, 0, 0, 0, 0, 0, true, true);
   end;
end;

// _unit_GoOutside
//
procedure _unit_GoOutside(goHnd : Integer; bDead, bLan : Boolean);
begin
   var trgHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   if trgHnd<>0 then
   begin
      _unit_RemoveFromInside(goHnd);
      if (bDead) then
      begin
         _unit_SetTagStates(goHnd, gc_statetag_essential_death);
         //SetGameObjectStatesTagForceUpdateByHandle(goHnd); // TODO : GEC Check if this is needed
      end
      else
      begin
         var resHnd : Integer;
         var x, z : Float;
         _unit_GetBuildingExitData(goHnd, trgHnd, false, resHnd, x, z);
         _unit_OrderLeave(goHnd, trgHnd, x, z);
         _unit_SetTagStates(goHnd, gc_statetag_essential_birth);
      end;

      if (not bDead) then // if dead, that unit destroy is sync usind _death_ state tag
      begin
         if bLan and (_net_IsOnline or _net_IsRecord) then
         begin
            var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
            var pArg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
            if pArg <> nil then
            begin
               TPlayerArgs(pArg).fHandle := goHnd;
               TPlayerArgs(parg).fDead := bDead;
               PlayerExecuteStateByHandle(plHnd, 'WriteLeave');
            end;
         end;
      end;
   end;
end;

// _unit_OrderExitUnit
//
procedure _unit_OrderExitUnit(goHnd, trgHnd : Integer; bDoFirst : Boolean);
begin
   var addOrder : Boolean = true;
   var i, j : Integer;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var pObjInside : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_inside);
   if (pObj <> nil) and (pObjInside <> nil) then
   begin
      var ordCount : Integer = TObj(pObj).orders[i].info.amount;
      var insCount : Integer = TIntegerList(pObjInside).GetCount;
      var listInd : Integer = -1;

      for j := ordCount to insCount-1 do
      if TIntegerList(pObjInside).Get(j) = trgHnd then
      begin
         listInd := j;
         break;
      end;

      if listInd >= 0 then
      begin
         for i := gc_obj_MaxOrderCount-1 downto 0 do
         begin
            if TObj(pObj).orders[i].itype = gc_obj_order_type_exitunits then
            begin
               TIntegerList(pObjInside).Exchange(ordCount, listInd);
               TObj(pObj).orders[i].info.amount := TObj(pObj).orders[i].info.amount + 1;

               addOrder := false;
               break;
            end
         end;

         if addOrder then
         begin
            TIntegerList(pObjInside).Exchange(0, listInd);
            _unit_AddOrder(goHnd, gc_obj_order_type_exitunits, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, False, bDoFirst);
         end;
      end;
   end;
end;

// _unit_DoUnitsGoOutside
//
procedure _unit_DoUnitsGoOutside(var list : TIntegerList; bDead, blan : Boolean);
begin
   if list.GetCount > 0 then
   begin
      if {_net_IsOffline or }{test changes 161114}{_net_IsRecord}(_net_IsOffline and (not _net_IsReplay)) or _net_IsServer then
      begin
         var i : Integer;
         for i := list.GetCount-1 downto 0 do
         begin
            var goHnd : Integer = list.Get(i);
            if (GetGameObjectUniqueIdByHandle(goHnd)=0) then // TODO : GEC Check if this is needed
            begin
               ErrorLog('_unit_DoUnitsGoOutside : goUID=0');
               exit;
            end;

            if bDead then
            begin
               _unit_RemoveFromInside(goHnd);
               _unit_SetTagStates(goHnd, gc_statetag_essential_death);
               SetGameObjectStatesTagForceUpdateByHandle(goHnd); // TODO : GEC Check if this is needed
            end
            else
            begin
               var trgHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
               if trgHnd<>0 then
               _unit_OrderExitUnit(trgHnd, goHnd, true)
               else
               ErrorLog('_unit_DoUnitsGoOutside : trgHnd=0');
            end;
         end;
      end
      else
      begin
         if (not bDead) then
         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);
               TObj(pObj).bLeaveRequested := true;
            end;
         {end
         else
         begin
            var i : Integer;
            for i := list.GetCount-1 downto 0 do
            begin
               var goHnd : Integer = list.Get(i);
               _unit_RemoveFromInside(goHnd);
            end;}
         end;
      end;

      // network
      if (not bDead) then // if dead, that unit destroy is sync usind _death_ state tag
      begin
         if blan and (_net_IsOnline or _net_IsRecord) then begin
            var goHnd : Integer = list.Get(0);
            var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
            var pArg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
            if pArg<>nil then begin
               TPlayerArgs(pArg).fintlst := list;
               TPlayerArgs(parg).fdead := bDead;
               PlayerExecuteStateByHandle(plHnd, 'WriteLeaveOrder');
            end;
         end;
      end;
   end;
end;

// _unit_SearchProtectors
//
function _unit_SearchProtectors(const x, y, plmask : Integer) : Integer;
begin
   Result := 0;
   var count : Integer = gScanGrid[x,y].GetCount;
   if (count>0) then
   begin
      var rndind : Integer = floor(random*count);
      var i : Integer;
      for i:=0 to count-1 do
      begin
         var newind : Integer = (rndind+i) mod count;
         var goHnd : Integer = gScanGrid[x,y].Get(i);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bprotector) and (not (gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding)) and ((gPlayer[TObj(pobj).pl].myplmask and plmask)=0) then
         begin
            Result := goHnd;
            break;
         end;
      end;
   end;
end;

// _unit_SearchCapturers
//
function _unit_SearchCapturers(const x, y, plmask : Integer) : Integer;
begin
   Result := 0;
   var count : Integer = gScanGrid[x,y].GetCount;
   if (count>0) then
   begin
      var rndind : Integer = floor(random*count);
      var i : Integer;
      for i:=0 to count-1 do
      begin
         var newind : Integer = (rndind+i) mod count;
         var goHnd : Integer = gScanGrid[x,y].Get(newind);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         begin
            if (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].bcancapture) and ((gPlayer[TObj(pobj).pl].myplmask and plmask)<>0) and (TObj(pobj).pl<>gc_player_mercenaryind) then
            begin
               Result := goHnd;
               break;
            end;
         end;
      end;
   end;
end;

// _unit_SearchCapturersForWall
//
function _unit_SearchCapturersForWall(const x, y, plmask : Integer) : Integer;
begin
   Result := 0;
   var count : Integer = gScanGrid[x,y].GetCount;
   if (count>0) then
   begin
      var rndind : Integer = floor(random*count);
      var i : Integer;
      for i:=0 to count-1 do
      begin
         var newind : Integer = (rndind+i) mod count;
         var goHnd : Integer = gScanGrid[x,y].Get(newind);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         begin
            if (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) and ((gPlayer[TObj(pobj).pl].myplmask and plmask)<>0) and (TObj(pobj).pl<>gc_player_mercenaryind) then
            begin
               Result := goHnd;
               break;
            end;
         end;
      end;
   end;
end;

// _unit_CheckCapturersStopProduce
//
function _unit_CheckCapturersStopProduce(const goHnd : Integer) : Boolean;
begin
   //_misc_ProfilerBegin('_unit_CheckCapturersStopProduce');
   Result := False;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var scangridx : Integer = TObj(pobj).scangridx;
   var scangridy : Integer = TObj(pobj).scangridy;
   var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
   if (not ((gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) and (TObj(pobj).hp<gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].maxhp/3))) then
   begin
      var rx1 : Integer = 1;
      var cellx, celly, cellxmax, cellymax : Integer;
      _misc_CalcScanCellsMinMax(scangridx, scangridy, rx1, cellx, celly, cellxmax, cellymax);
      var trgHnd : Integer;
      var i, j : Integer;
      for [MAIN]i:=cellx to (cellxmax) do
      for j:=celly to (cellymax) do
      if ((enemyplmask and gScanGrid[i,j].fPlMask)<>0) then
      begin
         trgHnd := _unit_SearchCapturers(i, j, gPlayer[TObj(pobj).pl].enemyplmask);
         if (trgHnd<>0) then
         begin
            var pobjprop2 : Pointer = _unit_GetObjProp(trgHnd);
            if (TObjProp(pobjprop2).bprotector) then
            begin
               Result := True;
               break(MAIN);
            end;
         end;
      end;
   end;
   //_misc_ProfilerEnd('_unit_CheckCapturersStopProduce');
end;

// _unit_TestCapture
//
function _unit_TestCapture(const goHnd : Integer) : Boolean;
begin
   //_misc_ProfilerBegin('TestCapture');
   Result := True;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var scangridx : Integer = TObj(pobj).scangridx;
   var scangridy : Integer = TObj(pobj).scangridy;
   var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
   if (not ((gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) and (TObj(pobj).hp<gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].maxhp/3))) then
   begin
      var rx1 : Integer = 3;
      var cellx, celly, cellxmax, cellymax : Integer;
      _misc_CalcScanCellsMinMax(scangridx, scangridy, rx1, cellx, celly, cellxmax, cellymax);
      var trgHnd : Integer;
      var i, j : Integer;
      for [MAIN]i:=cellx to (cellxmax) do
      for j:=celly to (cellymax) do
      if ((gPlayer[TObj(pobj).pl].myplmask and gScanGrid[i,j].fPlMask)<>0) then
      begin
         trgHnd := _unit_SearchProtectors(i, j, enemyplmask);
         if (trgHnd<>0) then
         begin
            var pobjprop2 : Pointer = _unit_GetObjProp(trgHnd);
            if (TObjProp(pobjprop2).bprotector) then
            begin
               Result := False;
               break(MAIN);
            end;
         end;
      end;
   end;
   //_misc_ProfilerEnd('TestCapture');
end;

// _unit_Stop
//
procedure _unit_Stop(const goHnd : Integer);
begin
   _unit_ClearOrders(goHnd);
   //_unit_PathListRemove(goHnd);
   _unit_SetSTO(goHnd, 0);
   _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd));
   _unit_SetTagStates(goHnd, gc_statetag_move_idle or gc_statetag_action_none);
end;

// _unit_SearchEnemyInCellShips
//
function _unit_SearchEnemyInCellShips(const goHnd, cellx, celly : Integer; bsearchbuilding : Boolean) : Integer;
begin
   Result := 0;
   //var profhnd : Integer = _misc_ProfilerBeginHnd('eicship');
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var killmask : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask;
      var enemyplmask : Integer = gPlayer[TObj(pObj).pl].enemyplmask;
      var trgHnd, statetag, maxDam, newind, rndind : Integer;
      var bisenemy, bcankill : Boolean;
      var pobjbase2, pobjprop2, pobj2 : Pointer;
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var px : Float =  GetGameObjectPositionXByHandle(goHnd);
      var pz : Float =  GetGameObjectPositionZByHandle(goHnd);
      var count : Integer = gScanGrid[cellx, celly].GetCount;
      if (count>0) then
      begin
         rndind := floor(random*count);
         var i : Integer;
         for i:=0 to count-1 do
         begin
            newind := (rndind+i) mod count;
            trgHnd := gScanGrid[cellx, celly].Get(newind);
            statetag := GetGameObjectStatesTagByHandle(trgHnd);
            if (trgHnd<>0) and (trgHnd<>goHnd) and ((statetag and gc_statetag_visual_hide)=0) and ((statetag and gc_statetag_essential_none<>0)) then
            begin
               pobj2 := _unit_GetTObj(trgHnd);
               if (pobj2<>nil) then
               begin
                  pobjprop2 := gObjProp[TObj(pobj2).cid][TObj(pobj2).id];
                  bcankill := ((TObjProp(pobjprop2).mmask and killmask)<>0);
                  if (bcankill) then
                  begin
                     bisenemy := ((enemyplmask and gPlayer[TObj(pobj2).pl].myplmask)<>0);
                     if (bisenemy) then
                     begin
                        if (bsearchbuilding) or ((TObjProp(pobjprop2).material=gc_obj_material_body) or (TObjProp(pobjprop2).material=gc_obj_material_iron) or (TObjProp(pObjProp2).media=gc_obj_media_water)) then
                        begin
                           Result := trgHnd;
                           break;
                        end;
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;
   //_misc_ProfilerEndHnd(eicship);
end;

// _unit_SearchEnemyInCell
//
function _unit_SearchEnemyInCell(const goHnd, cellx, celly, scanmode : Integer) : Integer;
begin
   Result := 0;
   //var profhnd : Integer = _misc_ProfilerBeginHnd('eic');
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var plind : Integer;
      for [MAIN]plind:=0 to gc_MaxPlayerCount-1 do
      begin
         var enemyplmask : Integer = gPlayer[TObj(pObj).pl].enemyplmask;
         if scanmode<>1 then
         begin
            if (gScanGrid[cellx, celly].fplmask and enemyplmask and (1 shl plind))=0 then
            continue;
         end
         else
         begin
            if (gScanGrid[cellx, celly].fplmask and gPlayer[TObj(pObj).pl].myplmask and (1 shl plind))=0 then
            continue;
         end;

         var count : Integer = gScanGrid[cellx, celly].fPlCount[plind];
         if (count>0) then
         begin
            var myRegion : Integer = _unit_GetRegion(goHnd);
            var killmask : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask;
            var trgHnd, statetag, maxDam, newind, rndind : Integer;
            var bisenemy, bcankill : Boolean;
            var pobjbase2, pobjprop2, pobj2 : Pointer;

            var goX, goY, goZ : Float;
            GetGameObjectAbsolutePositionByHandle(goHnd, goX, goY, goZ);

            // check if this string lags
            var maxRad : Float = _unit_GetMaxAttackRadius(goHnd);
            var bmelee : Boolean = maxRad<=gc_unit_meleeattackradius;
            if (not bmelee) then
            maxRad := maxRad-gc_obj_maxattackradiusdisp*TObj(pobj).uniqRnd;

            rndind := floor(random*count);
            var i : Integer;
            for i:=0 to count-1 do
            begin
               newind := (rndind+i) mod count;
               trgHnd := gScanGridUnits[plind, cellx, celly].Get(newind);
               statetag := GetGameObjectStatesTagByHandle(trgHnd);
               if (trgHnd<>0) and (trgHnd<>goHnd) and ((statetag and gc_statetag_visual_hide)=0) and ((statetag and gc_statetag_essential_none<>0)) then
               begin
                  var bFlag : Boolean;
                  if (not bmelee) then
                  begin
                     var trgX, trgY, trgZ : Float;
                     GetGameObjectAbsolutePositionByHandle(trgHnd, trgX, trgY, trgZ);
                     bFlag := (VectorDistance(goX, 0, goZ, trgx, 0, trgZ))<=(maxRad+MaxFloat((goY - trgY)*2, 0));
                  end;

                  if (_unit_GetRegion(trgHnd) = myRegion) or (bFlag) then
                  begin
                     pobj2 := _unit_GetTObj(trgHnd);
                     if (pobj2<>nil) then
                     begin
                        if (scanmode=4) then
                        begin
                           // for ai sabotage
                           bisenemy := ((enemyplmask and gPlayer[TObj(pobj2).pl].myplmask)<>0);
                           pobjprop2 := gObjProp[TObj(pobj2).cid][TObj(pobj2).id];
                           bcankill := ((TObjProp(pobjprop2).mmask and killmask)<>0);
                           if (bisenemy) and (bcankill) then
                           begin
                              pobjbase2 := gPlayer[TObj(pobj2).pl].objbase[TObj(pobj2).cid][TObj(pobj2).id];
                              if (TObjBase(pobjbase2).weapon[0].damage>maxDam) then
                              begin
                                 Result := trgHnd;
                                 maxDam := TObjBase(pobjbase2).weapon[0].damage;
                              end;
                           end;
                        end
                        else
                        if (scanmode=3) then
                        begin // scan for capture only
                           if (TObjProp(pobjprop2).bcapture) and (_unit_TestCapture(trgHnd)) then
                           begin
                              Result := trgHnd;
                              break(MAIN);
                           end;
                        end
                        else
                        begin
                           pobjprop2 := gObjProp[TObj(pobj2).cid][TObj(pobj2).id];
                           bcankill := ((TObjProp(pobjprop2).mmask and killmask)<>0);
                           if (bcankill) then
                           begin
                              if (scanmode=1) then
                              begin
                                 if ((enemyplmask and gPlayer[TObj(pobj2).pl].myplmask)=0) then
                                 begin
                                    if (TObj(pobj2).hp<gPlayer[TObj(pobj2).pl].objbase[TObj(pobj2).cid][TObj(pobj2).id].maxhp) then
                                    begin
                                       Result := trgHnd;
                                       break(MAIN);
                                    end
                                    else
                                    break(MAIN);
                                 end
                              end
                              else
                              begin
                                 bisenemy := ((enemyplmask and gPlayer[TObj(pobj2).pl].myplmask)<>0);
                                 if (bisenemy)
                                 and (((TObjProp(pobjprop2).material=gc_obj_material_body) or (TObjProp(pobjprop2).material=gc_obj_material_iron))
                                 or ((gObjProp[TObj(pobj).cid][TObj(pobj).id].material=gc_obj_material_building) and (TObjProp(pobjprop2).material=gc_obj_material_wood))) then
                                 begin
                                    Result := trgHnd;
                                    break(MAIN);
                                 end;
                              end;
                           end;
                           if (Result=0) and (scanmode=2) then
                           begin
                              bisenemy := ((enemyplmask and gPlayer[TObj(pobj2).pl].myplmask)<>0);
                              if (bisenemy) and (TObjProp(pobjprop2).bcapture) and (_unit_TestCapture(trgHnd)) then
                              begin
                                 //if (bcankill) then
                                 //если я могу захватить этого юнита сейчас, то не важно, могу я его атаковать или нет, я выбираю его как цель.
                                 Result := trgHnd;
                                 break(MAIN);
                              end;
                           end;
                        end;
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;
   //_misc_ProfilerEndHnd(profhnd);
end;

// _unit_SearchEnemyScanCellsLongRange
//
function _unit_SearchEnemyScanCellsLongRange(const goHnd : Integer; const pX, pZ, minsearchdist : Float; const x1, y1, rx1, trynum : Integer; const scanmode : Integer) : Integer;
begin
   Result := 0;

   var distSqr : Float;
   var mindist : Float = gc_MaxInt;
   var trgHnd : Integer;
   var minTrgHnd : Integer;
   var minsearchdistSqr : Float = minsearchdist*minsearchdist;

   var pobj : Pointer = _unit_GetTObj(goHnd);
   var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
   var cellx, celly, cellxmax, cellymax : Integer;
   _misc_CalcScanCellsMinMax(x1, y1, rx1, cellx, celly, cellxmax, cellymax);

   var i : Integer;
   for i:=0 to trynum-1 do
   begin
      var dx : Integer = cellx+floor(1+random*(cellxmax-cellx));
      var dy : Integer = celly+floor(1+random*(cellymax-celly));

      var plmask : Integer = gScanGrid[dx,dy].fPlMask;
      var bisenemy : Boolean = (enemyplmask and plmask<>0);
      if _misc_IsCorrectScanCellOwner(TObj(pObj).pl, dx, dy) and ((bisenemy) and (scanmode<>1)) or ((scanmode=1) and (plmask<>0)) then
      begin
         trgHnd := _unit_SearchEnemyInCell(goHnd, dx, dy, scanmode);
         if (trgHnd<>0) then
         begin
            distSqr := Sqr(pX-GetGameObjectPositionXByHandle(trgHnd))+Sqr(pZ-GetGameObjectPositionZByHandle(trgHnd));
            if (distSqr>minsearchdistSqr) then
            begin
               if (distSqr<mindist) then
               begin
                  if (_misc_GetShotPointsCount(goHnd)=0) or (_misc_ShotPointsAllowShoot(goHnd, px, pz, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd))) then
                  begin
                     mindist := distSqr;
                     minTrgHnd := trgHnd;
                  end;
               end;
               {var pstolist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
               relativeDist := dist*(1+TIntegerList(pstolist).GetCount*0.1);
               if (relativeDist<minRelativeDist) then
               begin
                  minRelativeDist := relativeDist;
                  minRelativeTrgHnd := trgHnd;
               end;}
            end;
         end;
      end;
   end;
   //Result := minRelativeTrgHnd;
   Result := minTrgHnd;
end;

// _unit_SearchEnemyScanCellsBADOptimized
//
function _unit_SearchEnemyScanCellsBADOptimized(const goHnd : Integer; const pX, pZ, searchdist, minsearchdist : Float; const x1, y1, rx1, scanmode : Integer) : Integer;
begin
   Result := 0;
   var dist : Float;
   var mindist : Float = gc_MaxInt;
   var trgHnd : Integer;
   var minTrgHnd : Integer;
   var relativeDist : Float;
   var minRelativeDist : Float = gc_MaxInt;
   var minRelativeTrgHnd : Integer;

   var pobj : Pointer = _unit_GetTObj(goHnd);
   var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;

   gTmpIntListObjectsInRadius.Clear;
   _misc_MakeScanCellsList(pX, pZ, searchdist, TObj(pobj).pl, gTmpIntListObjectsInRadius);

   var i, gridx, gridy : Integer;
   for [MAIN]i:=gTmpIntListObjectsInRadius.GetCount-1 downto 0 do
   begin
      var gridInd : Integer = gTmpIntListObjectsInRadius.Get(i);
      gridx := gridInd mod gc_scangrid_county;
      gridy := gridInd div gc_scangrid_county;
      var plmask : Integer = gScanGrid[gridx,gridy].fPlMask;
      var bisenemy : Boolean = (enemyplmask and plmask<>0);
      if ((bisenemy) and (scanmode<>1)) or ((scanmode=1) and (plmask<>0)) then
      begin
         trgHnd := _unit_SearchEnemyInCell(goHnd, gridx, gridy, scanmode);
         if (trgHnd<>0) then
         begin
            dist := VectorDistance(pX, 0, pZ, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
            if (dist>minsearchdist) then
            begin
               if (dist<mindist) then
               begin
                  mindist := dist;
                  minTrgHnd := trgHnd;
               end;
               var pstolist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
               relativeDist := dist*(1+TIntegerList(pstolist).GetCount*0.1);
               if (relativeDist<minRelativeDist) then
               begin
                  minRelativeDist := relativeDist;
                  minRelativeTrgHnd := trgHnd;
               end;
            end;
         end;
      end;
   end;
   Result := minRelativeTrgHnd;
end;

// _unit_SearchEnemyScanCellsShips
//
function _unit_SearchEnemyScanCellsShips(const goHnd : Integer; const pX, pZ, minsearchdist : Float; const x1, y1, rx1, scanmode : Integer) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pObj <> nil) and _misc_IsCorrectScanCellOwner(TObj(pObj).pl, TObj(pObj).scanGridX, TObj(pObj).scanGridY) then
   begin
      var distSqr : Float;
      var minsearchdistSqr : Float = minsearchdist*minsearchdist;
      var mindist : Float = gc_MaxInt;
      var trgHnd : Integer;
      var minTrgHnd : Integer;
      var relativeDist : Float;
      var minRelativeDist : Float = gc_MaxInt;
      var minRelativeTrgHnd : Integer;

      var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
      var killmask : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask;

      var cellx, celly, cellxmax, cellymax : Integer;
      _misc_CalcScanCellsMinMax(x1, y1, rx1, cellx, celly, cellxmax, cellymax);
      var i, j : Integer;
      for i:=cellx to (cellxmax) do
      for j:=celly to (cellymax) do
      if _misc_IsCorrectScanCellOwner(TObj(pObj).pl, i, j) then
      begin
         var plmask : Integer = gScanGrid[i,j].fPlMask;
         var bisenemy : Boolean = (enemyplmask and plmask<>0);
         if ((bisenemy) and (scanmode<>1)) or ((scanmode=1) and (plmask<>0)) then
         begin
            trgHnd := _unit_SearchEnemyInCellShips(goHnd, i, j, gPlayer[TObj(pobj).pl].bai);
            if (trgHnd<>0) then
            begin
               distSqr := Sqr(pX-GetGameObjectPositionXByHandle(trgHnd))+Sqr(pZ-GetGameObjectPositionZByHandle(trgHnd));
               if (distSqr>minsearchdistSqr) then
               begin
                  if (_misc_IsBuildingInRay(goHnd, pX, GetGameObjectPositionYByHandle(goHnd)+0.5, pZ, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionYByHandle(trgHnd)+0.5, GetGameObjectPositionZByHandle(trgHnd))=0) then
                  begin
                     if (distSqr<mindist) then
                     begin
                        mindist := distSqr;
                        minTrgHnd := trgHnd;
                     end;
                     var pstolist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
                     relativeDist := distSqr*(1+TIntegerList(pstolist).GetCount*0.1);
                     if (relativeDist<minRelativeDist) then
                     begin
                        minRelativeDist := relativeDist;
                        minRelativeTrgHnd := trgHnd;
                     end;
                  end;
               end;
            end;
         end;
      end;
      Result := minRelativeTrgHnd;
   end;
end;

// _unit_SearchEnemyScanCells
//
function _unit_SearchEnemyScanCells(const goHnd : Integer; const pX, pZ, minsearchdist, maxsearchdist : Float; const x1, y1, rx1, scanmode : Integer) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if _misc_IsCorrectScanCellOwner(TObj(pObj).pl, TObj(pObj).scanGridX, TObj(pObj).scanGridY) then
   begin
      var bmelee : Boolean = gObjProp[TObj(pobj).cid][TObj(pobj).id].minattackradius<gc_unit_meleeattackradius;
      var minsearchdistSqr : Float = minsearchdist*minsearchdist;
      var maxsearchdistSqr : Float;
      if bmelee or (TObj(pobj).standtime>0.25) then
      maxsearchdistSqr := maxsearchdist*maxsearchdist
      else
      maxsearchdistSqr := Sqr(maxsearchdist-gc_obj_maxattackradiusdisp*TObj(pObj).uniqrnd);
      var distSqr, relativeDist : Float;
      var mindist, minRelativeDist : Float = gc_MaxInt;
      var trgHnd, minTrgHnd, minRelativeTrgHnd : Integer;

      var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
      var killmask : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask;

      var cellx, celly, cellxmax, cellymax : Integer;
      _misc_CalcScanCellsMinMax(x1, y1, rx1, cellx, celly, cellxmax, cellymax);
      var i, j : Integer;
      //_misc_ProfilerBegin('SESC');
      for [MAIN]i:=cellx to (cellxmax) do
      for j:=celly to (cellymax) do
      begin
         if _misc_IsCorrectScanCellOwner(TObj(pObj).pl, i, j) then
         begin
            if _misc_ScanGridCellDataCheckNeeded(gScanGrid[i,j], enemyplmask, killmask, scanmode) then
            begin
               trgHnd := _unit_SearchEnemyInCell(goHnd, i, j, scanmode);
               _misc_ScanGridCellDataUpdateResult(gScanGrid[i,j], trgHnd<>0);
               if (trgHnd<>0) then
               begin
                  distSqr := Sqr(pX-GetGameObjectPositionXByHandle(trgHnd))+Sqr(pZ-GetGameObjectPositionZByHandle(trgHnd));
                  //if (distSqr>minsearchdistSqr) then // to let pikeman choose close target that we can hurt
                  if (distSqr>minsearchdistSqr) and (distSqr<maxsearchdistSqr) then // shooters go and attack with standground mode wont shoot fix
                  begin
                     if (distSqr<mindist) then
                     begin
                        mindist := distSqr;
                        minTrgHnd := trgHnd;
                     end;
                     if bmelee then
                     begin
                        var pstolist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
                        relativeDist := distSqr*(1+TIntegerList(pstolist).GetCount*0.125);
                     end
                     else
                     relativeDist := distSqr;
                     if (relativeDist<minRelativeDist) then
                     begin
                        minRelativeDist := relativeDist;
                        minRelativeTrgHnd := trgHnd;

                        const cOkDist = (gc_scangrid_size/2)*(gc_scangrid_size/2);
                        if bmelee and (mindist<cOkDist) then
                        break(MAIN);
                     end;
                  end;
               end;
            end;
         end;
      end;
      //_misc_ProfilerEnd('SESC');
      Result := minRelativeTrgHnd; // no help from relative dist cause we choose 1 unit from each cell, and not several from one cell
   end;
end;

// _unit_SearchVictim
//
function _unit_SearchVictim(const goHnd : Integer; const r0, r1 : Float; const bCustom : Boolean; const custX, custZ : Float) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);

   if _misc_IsCorrectScanCellOwner(TObj(pObj).pl, TObj(pObj).scanGridX, TObj(pObj).scanGridY) then
   begin
      var scangridx, scangridy : Integer;
      var posX, posZ : Float;
      if (bCustom) then
      begin
         posX := custX;
         posZ := custZ;
         if not _misc_PosToScanGridIndices(posX, posZ, scangridx, scangridy) then
         begin
            ErrorLog('_unit_SearchVictim : pos out of bounds');
            exit;
         end;
      end
      else
      begin
         scangridx := TObj(pobj).scangridx;
         scangridy := TObj(pobj).scangridy;
         posX := GetGameObjectPositionXByHandle(goHnd);
         posZ := GetGameObjectPositionZByHandle(goHnd);
      end;

      var rx1 : Integer = floor(r1/gc_scangrid_size)+1;

      var scanmode : Integer;
      if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bpriest) then
      scanmode := 1;

      var trgHnd : Integer = _unit_SearchEnemyScanCells(goHnd, posX, posZ, r0, r1, scangridx, scangridy, rx1, scanmode);
      if (trgHnd<>0) then
      begin
         var dist : Float = VectorDistance(posX, 0, posZ, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
         if (dist<r1) then
         Result := trgHnd;
         {begin
            // TODO : C1 mechanic
            //if (_unit_GetTopDistance(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd))<20) then
            Result := trgHnd;
         end;}
      end;
   end;
end;

// _unit_OrderAttackPoint
//
function _unit_OrderAttackPoint(goHnd : Integer; trgX, trgZ : Float; bDoFirst, bClearOrders : Boolean) : Pointer;
begin
   Result := _unit_AddOrder(goHnd, gc_obj_order_type_attackpoint, 0, 0, 0, trgX, trgZ, 0, 0, 0, 0, 0, 0, 0, bClearOrders, bDoFirst);
end;

// _unit_OrderAttack
//
function _unit_OrderAttack(const goHnd, trgHnd : Integer; bDoFirst, bClearOrders, bLockTrg : Boolean) : Pointer;
begin
   var iLockTrg : Integer;
   if (bLockTrg) then
   iLockTrg := gc_obj_order_attack_mode_locktarget;
   if (trgHnd<>0) then
   Result := _unit_AddOrder(goHnd, gc_obj_order_type_attackobj, trgHnd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, iLockTrg, bClearOrders, bDoFirst)
   else
   begin
      Result := nil;
      Log('_unit_OrderAttack : trgHnd=0');
   end;
   //_unit_ClearOrders(goHnd);
end;

// _unit_CheckFriendlyFireExplosion
//
function _unit_CheckFriendlyFireExplosion(goHnd : Integer; tx, tz : Float) : Boolean;
begin
   Result := False;
   if (goHnd=0) then
   exit;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var enemyplmask : Integer = gPlayer[TObj(pObj).pl].enemyplmask;
      var enemycount, friendcount : Integer;
      var disp : Float = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].weapon[0].dispertion;
      SetRandomKey(floor(random*gc_MaxInt)); // needed to sync multiplayer arg.frnd
      var px : Float = GetGameObjectPositionXByHandle(goHnd);
      var pz : Float = GetGameObjectPositionZByHandle(goHnd);
      var dist : Float = VectorDistance(px, 0, pz, tx, 0, tz);
      var maxdisp : Float = dist*disp*gc_pixels_to_tile/2000;
      var dmgRad : Float;
      case _unit_GetUsage(goHnd) of
         gc_obj_usage_mortar : dmgRad := 3.75;
         else
         dmgRad := 1.25;
      end;
      gIntegerList.Clear;
      _misc_MakeListObjectsInRadius(tx, tz, maxdisp+dmgRad, True, (1 shl gc_obj_material_body), 0, False, gIntegerList);
      var i : Integer;
      for i:=0 to gIntegerList.GetCount-1 do
      begin
         var hnd : Integer = gIntegerList.Get(i);
         var pobjtrg : Pointer = _unit_GetTObj(hnd);
         if (pobjtrg<>nil) then
         begin
            var pl : Integer = TObj(pobjtrg).pl;
            var benemy : Boolean = ((enemyplmask and gPlayer[pl].myplmask)<>0);
            if (benemy) then
            enemycount := enemycount+1
            else
            friendcount := friendcount+1;
         end;
      end;
      Result := (enemycount>=(friendcount*4));
      //Log('enemy = '+IntToStr(enemycount)+' or needed = '+IntToStr(friendcount*4-enemycount)+' friend = '+IntToStr(friendcount));
   end;
end;

// _unit_CheckFriendlyFireCannister
//
function _unit_CheckFriendlyFireCannister(goHnd : Integer; bCheckInFront : Boolean; dx, dz : Float) : Boolean;
begin
   Result := False;
   if (goHnd=0) then
   exit;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var px : Float = GetGameObjectPositionXByHandle(goHnd);
      var pz : Float = GetGameObjectPositionZByHandle(goHnd);
      var angle : Float;
      if (bCheckInFront) then
      begin
         var dirX : Float = GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
         var dirY : Float;
         var dirZ : Float = GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
         angle := _misc_GetDirAngleToXVector(dirX, dirZ);
      end
      else
      begin
         angle := _misc_GetAngleToXVector(px, pz, dx, dz);
      end;
      var bmulticannon : Boolean = (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_mcannon);
      var enemyplmask : Integer = gPlayer[TObj(pObj).pl].enemyplmask;
      var enemycount, friendcount : Integer;
      var i, j : Integer;
      for i:=0 to 5 do
      begin
         var rad, dst : Float;
         if (bmulticannon) then
         begin
            case i of
               0 : rad := 0.47;
               1 : rad := 0.94;
               2 : rad := 1.41;
               3 : rad := 2.35;
               4 : rad := 2.82;
               5 : rad := 3.29;
            end;
            case i of
               0 : dst := 0.75;
               1 : dst := 1.5;
               2 : dst := 3;
               3 : dst := 4.875;
               4 : dst := 6.75;
               5 : dst := 8.625;
            end;
         end
         else
         begin
            case i of
               0 : rad := 0.47;
               1 : rad := 0.94;
               2 : rad := 1.41;
               3 : rad := 2.35;
               4 : rad := 2.82;
               5 : rad := 3.29;
            end;
            case i of
               0 : dst := 0.75;
               1 : dst := 1.5;
               2 : dst := 3;
               3 : dst := 4.875;
               4 : dst := 6.75;
               5 : dst := 8.625;
            end;
         end;
         var weight : Integer;
         if (bmulticannon) then
         begin
            if (i>2) then
            weight := 1
            else
            weight := 5-i;
         end
         else
         if (i<5) then
         weight := 4
         else
         weight := 2;
         var x : Float = dst;
         var y, z : Float;
         VectorRotateY(x, y, z, angle);
         var tx : Float = px+x;
         var tz : Float = pz+z;
         gIntegerList.Clear;
         _misc_MakeListObjectsInRadius(tx, tz, rad, True, (1 shl gc_obj_material_body), 0, False, gIntegerList);
         for j:=0 to gIntegerList.GetCount-1 do
         begin
            var hnd : Integer = gIntegerList.Get(j);
            var pobjtrg : Pointer = _unit_GetTObj(hnd);
            if (pobjtrg<>nil) then
            begin
               var pl : Integer = TObj(pobjtrg).pl;
               var benemy : Boolean = ((enemyplmask and gPlayer[pl].myplmask)<>0);
               if (benemy) then
               enemycount := enemycount+weight
               else
               friendcount := friendcount+weight;
            end;
         end;
      end;
      Result := (enemycount>=(friendcount*4));
      //Log('enemy = '+IntToStr(enemycount)+' or needed = '+IntToStr(friendcount*4-enemycount)+' friend = '+IntToStr(friendcount));
   end;
end;

// _unit_SearchVictimOnProgress
//
function _unit_SearchVictimOnProgress(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pObj <> nil) and _misc_IsCorrectScanCellOwner(TObj(pObj).pl, TObj(pObj).scanGridX, TObj(pObj).scanGridY) then
   begin
      const cNearNoseSearchDist = 140/gc_pixels_to_tile;
      const cNearNoseStandTime = gc_frames_to_time*16;
      const cNearNoseRandom = 6000;
      const cLongRangeTryNum = 6*3; // 3 multiplicator, cause cossacks make search every frame with 32 fps. C3 unit have 100ms statemachine progress time.

      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var searchdist : Float = TObjProp(pobjprop).searchradius;
      // todo : 170419 check if helps
      //if (TObj(pobj).standtime<0.25) then // improve shooters search enemy
      //searchdist := searchdist-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd;

      var minsearchdist : Float = TObjProp(pobjprop).minattackradius;

      if (minsearchdist>gc_unit_meleeattackradius) then
      //if (searchdist>gc_unit_meleeattackradius) then
      begin
         var goHeight : Float = GetGameObjectPositionYByHandle(goHnd);
         if (goHeight<0) then goHeight := 0;
         searchdist := searchdist+goHeight*2;
      end
      else
      begin
         if (TObj(pobj).orders[0].itype=gc_obj_order_type_guard) then
         searchdist := MinFloat(searchdist, gc_gameplay_meleeguardmaxsearchdist);
      end;

      var scangridx : Integer = TObj(pobj).scangridx;
      var scangridy : Integer = TObj(pobj).scangridy;
      var standtime : Float = TObj(pobj).standtime; // add standtime counter

      var posX : Float = GetGameObjectPositionXByHandle(goHnd);
      var posZ : Float = GetGameObjectPositionZByHandle(goHnd);

      var rx1 : Integer = floor(searchdist/gc_scangrid_size)+1;
      var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;

      var scanmode : Integer;
      if (TObjProp(pobjprop).bpriest) then
      scanmode := 1
      else
      if (not ((TObjProp(pobjprop).bcapture) or (TObjProp(pobjprop).media=gc_obj_media_water) or (TObjProp(pobjprop).bbuilding))) then
      scanmode := 2;

      var trgHnd : Integer;
      if (TObjProp(pobjprop).media=gc_obj_media_water) then
      trgHnd := _unit_SearchEnemyScanCellsShips(goHnd, posX, posZ, minsearchdist, scangridx, scangridy, rx1, scanmode)
      else
      begin
         if (_misc_GetShotPointsCount(goHnd)>0) then
         trgHnd := _unit_SearchEnemyScanCellsLongRange(goHnd, posX, posZ, minsearchdist, scangridx, scangridy, rx1, cLongRangeTryNum, scanmode)
         else
         if (rx1<=5) and (scanmode<>1) then
         trgHnd := _unit_SearchEnemyScanCells(goHnd, posX, posZ, minsearchdist, searchdist, scangridx, scangridy, rx1, scanmode)
         else
         begin
            // near nose search
            if (scanmode<>1) and (minsearchdist<cNearNoseSearchDist) and (standtime<cNearNoseStandTime) and (_misc_RandomInt>cNearNoseRandom) then
            trgHnd := _unit_SearchEnemyScanCells(goHnd, posX, posZ, minsearchdist, searchdist, scangridx, scangridy, 1, scanmode);
            if (trgHnd=0) then //long range search
            trgHnd := _unit_SearchEnemyScanCellsLongRange(goHnd, posX, posZ, minsearchdist, scangridx, scangridy, rx1, cLongRangeTryNum, scanmode);
         end;
      end;

      if (trgHnd<>0) and (VectorDistance(posX, 0, posZ, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd))<searchdist) then
      begin
         var bpeacetime : Boolean; // check for peacetime and don't search enemy on enemy territory;
         if (not bpeacetime) then
         begin
            var bvisible : Boolean = True; // CheckVisibility function in C1
            if (bvisible) then
            begin
               //_unit_OrderAttack(goHnd, trgHnd);
               //log('victim found');
               Result := trgHnd;
            end;
         end;
      end;
   end;
end;

// _unit_OrderGainResource
//
procedure _unit_OrderGainResource(goHnd, trgHnd : Integer; posX, posZ : Float);
begin
   if (trgHnd<>0) then
   begin
      var pres : Pointer = _res_GetTRes(trgHnd);
      if (GetMapCollisionTag(GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd), True)=0) then
      begin
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (TRes(pres).itype<>TObj(pobj).restype) then
         begin
            TObj(pobj).restype := TRes(pres).itype;
            TObj(pobj).resamount := 0;
         end;
         _unit_AddOrder(goHnd, gc_obj_order_type_gainres, 0, posX, posZ, 0, 0, 0, 0, 0, 0, TRes(pres).itype, 0, 0, False, False);
      end;
   end
   else
   Log('_unit_OrderGainResource : trgHnd=0');
   //_unit_ClearOrders(goHnd);
end;

// _unit_OrderGoInside
//
function _unit_OrderGoInside(goHnd, trgHnd : Integer) : Boolean;
begin
   Result := False;
   if (trgHnd<>0) then
   begin
      var order : Integer = gc_obj_order_type_none;
      var pobj2 : Pointer = _unit_GetTObj(trgHnd);
      if (pobj2<>nil) then
      begin
         var pobj2prop : Pointer = gObjProp[TObj(pobj2).cid][TObj(pobj2).id];
         var peasantabsorber : Integer = TObjProp(pobj2prop).peasantabsorber+TObj(pobj2).individual.addpeasantabsorber;
         var transport : Integer = TObjProp(pobj2prop).transport+TObj(pobj2).individual.addtransport;

         var pObjInside : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_inside);
         var inCount : Integer;
         if pObjInside <> nil then
         inCount := TIntegerList(pObjInside).GetCount;

         if ((inCount+TObj(pobj2).insidereserved)<(peasantabsorber+transport)) then
         begin
            TObj(pobj2).insidereserved := TObj(pobj2).insidereserved+1;
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (peasantabsorber>0) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_peasant) then
            order := gc_obj_order_type_gotomine
            else
            if (transport>0) then
            order := gc_obj_order_type_gototransport;
            _unit_AddOrder(goHnd, order, trgHnd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, False, False);
         end;
      end;
   end
   else
   Log('_unit_OrderGoInside : trgHnd=0');
   //_unit_ClearOrders(goHnd);
end;

//// _unit_OrderExtractResources
////
//procedure _unit_OrderExtractResources(goHnd, trgHnd : Integer; bDoFirst : Boolean);
//begin
   //
//end;

// _unit_OrderPatrol
//
procedure _unit_OrderPatrol(goHnd : Integer; posX, posY, trgX, trgY : Float);
begin
   _unit_AddOrder(goHnd, gc_obj_order_type_patrol, 0, posX, posY, trgX, trgY, 0, 0, 0, 0, 0, 0, 0, False, False);
end;

// _unit_OrderGuard
//
procedure _unit_OrderGuard(goHnd, trgHnd : Integer);
begin
   if (trgHnd<>0) then
   _unit_AddOrder(goHnd, gc_obj_order_type_guard, trgHnd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, False, False)
   else
   Log('_unit_OrderGuard : trgHnd=0');
   //_unit_ClearOrders(goHnd);
end;

//_unit_RemoveShipDummy
//
procedure _unit_RemoveShipDummy(const bldHnd : Integer);
begin
   if (bldHnd<>0) and (GetCountOfPlayers>=gc_MaxPlayerCount) then //preventing crash on new map/engine exit
   begin
      var plHnd : Integer = GetGameObjectPlayerHandleByHandle(bldHnd);
      if (plHnd<>0) then
      begin
         var posx, posz : Float;
         if (_misc_GetPortResourcePoint(bldHnd, posx, posz, True)) then
         begin
            GetGameObjectsInRadius(posx, posz, 2, false, true, plHnd, -1, 0, false, false, false, false, false, false);
            var i : Integer;
            for i := GetGameObjectListCount-1 downto 0 do
            begin
               var objHnd : Integer = GetGameObjectListByIndex(i);
               if (objHnd<>0) then
               begin
                  if (_misc_GetBaseObjID(objHnd)=gc_baseid_obj) and (_unit_IsWaterUnit(objHnd)) then
                  begin
                     var pobj : Pointer = _unit_GetTObj(objHnd);
                     if (not TObj(pobj).bbuilt) and (not TObj(pobj).bdead) then
                     _unit_SetTagStates(objHnd, gc_statetag_essential_death);
                     _unit_SetTagStates(bldHnd, gc_statetag_action_none);
                  end;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_GetCostByID
//
procedure _unit_GetCostByID(const cid, unitID, plInd : Integer; var food, wood, stone, gold, iron, coal : Integer);
begin
   var costpercent : Float = gObjProp[cid][unitID].costpercent;
   var costmodifier : Float = 1;
   if (costpercent<>100) and (costpercent<>0) then
   begin
      var count : Integer = gPlayer[plInd].counter.all[cid][unitID];
      var bmercenary : Boolean = gObjProp[cid][unitID].bmercenary;
      if (bmercenary) then
      begin
         var sid : String = gObjProp[cid][unitID].sid;
         var tmpsid : String;
         case sid of
            'archerdip' : tmpsid := 'archerturdip';
            'archerturdip' : tmpsid := 'archerdip';
            'dragoon18dip' : tmpsid := 'lightcavalrydip';
            'lightcavalrydip' : tmpsid := 'dragoon18dip';
         end;
         var tmpid : Integer = _unit_ConvertObjSIDToID(cid, tmpsid);
         count := count+gPlayer[plInd].counter.all[cid][tmpid];
      end;
      costmodifier := pow(costpercent/100, count);
      if bmercenary and (costmodifier>2) then
      costmodifier := 2
      else
      if (costmodifier>20000) then
      costmodifier := 20000;
   end;
   var i : Integer;
   for i:=0 to gc_ResCount-1 do
   case i of
      gc_resource_type_food : food := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
      gc_resource_type_wood : wood := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
      gc_resource_type_stone : stone := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
      gc_resource_type_gold : gold := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
      gc_resource_type_iron : iron := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
      gc_resource_type_coal : coal := floor(gPlayer[plInd].objbase[cid][unitID].price[i]*(costmodifier));
   end;
end;

// _unit_CanApplyCostByID
//
function _unit_CanApplyCountCostByID(const cid, unitID, plInd, count : Integer) : Boolean;
begin
   var food, wood, stone, gold, iron, coal : Integer;
   _unit_GetCostByID(cid, unitID, plInd, food, wood, stone, gold, iron, coal);
   food:=food*count;
   wood:=wood*count;
   stone:=stone*count;
   gold:=gold*count;
   iron:=iron*count;
   coal:=coal*count;
   if (gPlayer[plInd].bai) then
   begin
      if (food>0) then
      food := food+300;
      if (gold>0) then
      gold := gold+25;
   end;
   Result := ((gPlayer[plInd].res[gc_resource_type_food]>=food)
   and (gPlayer[plInd].res[gc_resource_type_wood]>=wood)
   and (gPlayer[plInd].res[gc_resource_type_stone]>=stone)
   and (gPlayer[plInd].res[gc_resource_type_gold]>=gold)
   and (gPlayer[plInd].res[gc_resource_type_iron]>=iron)
   and (gPlayer[plInd].res[gc_resource_type_coal]>=coal));
end;

// _unit_CanApplyCostByID
//
function _unit_CanApplyCostByID(const cid, unitID, plInd : Integer) : Boolean;
begin
   Result := _unit_CanApplyCountCostByID(cid, unitID, plInd, 1);
end;

// _unit_ApplyCostByID
//
procedure _unit_ApplyCostByID(const cid, unitID, plInd : Integer);
begin
   var bProcess : Boolean = not (_net_IsClient or _net_IsReplay);
   if (bProcess) then
   begin
      var food, wood, stone, gold, iron, coal : Integer;
      _unit_GetCostByID(cid, unitID, plInd, food, wood, stone, gold, iron, coal);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_food, -food);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_wood, -wood);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_stone, -stone);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_gold, -gold);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_iron, -iron);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_coal, -coal);
      _misc_LanForceSyncRes;

      if (gObjProp[cid][unitID].bbuilding) then
      begin
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_food] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_food]+food;
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_wood] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_wood]+wood;
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_stone] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_stone]+stone;
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_gold] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_gold]+gold;
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_iron] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_iron]+iron;
         gPlayer[plInd].stat.resonbuildings[gc_resource_type_coal] := gPlayer[plInd].stat.resonbuildings[gc_resource_type_coal]+coal;
      end
      else
      begin
         gPlayer[plInd].stat.resonunits[gc_resource_type_food] := gPlayer[plInd].stat.resonunits[gc_resource_type_food]+food;
         gPlayer[plInd].stat.resonunits[gc_resource_type_wood] := gPlayer[plInd].stat.resonunits[gc_resource_type_wood]+wood;
         gPlayer[plInd].stat.resonunits[gc_resource_type_stone] := gPlayer[plInd].stat.resonunits[gc_resource_type_stone]+stone;
         gPlayer[plInd].stat.resonunits[gc_resource_type_gold] := gPlayer[plInd].stat.resonunits[gc_resource_type_gold]+gold;
         gPlayer[plInd].stat.resonunits[gc_resource_type_iron] := gPlayer[plInd].stat.resonunits[gc_resource_type_iron]+iron;
         gPlayer[plInd].stat.resonunits[gc_resource_type_coal] := gPlayer[plInd].stat.resonunits[gc_resource_type_coal]+coal;
      end;
   end;
end;

// _unit_CanApplyUpgradeCostBySID
//
function _unit_CanApplyUpgradeCostBySID(const plHnd, cid : Integer; var upgsid : String) : Boolean;
begin
   var bProcess : Boolean = not (_net_IsClient or _net_IsReplay);
   if (bProcess) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var food, wood, stone, gold, iron, coal : Integer;
      _country_GetUpgradeCostBySID(cid, upgsid, food, wood, stone, gold, iron, coal);
      if (gPlayer[plInd].bai) then
      begin
         food := food+500;
         gold := gold+25;
      end;
      Result := ((gPlayer[plInd].res[gc_resource_type_food]>=food)
      and (gPlayer[plInd].res[gc_resource_type_wood]>=wood)
      and (gPlayer[plInd].res[gc_resource_type_stone]>=stone)
      and (gPlayer[plInd].res[gc_resource_type_gold]>=gold)
      and (gPlayer[plInd].res[gc_resource_type_iron]>=iron)
      and (gPlayer[plInd].res[gc_resource_type_coal]>=coal));
   end
   else
   Result := True;
end;

// _unit_ApplyUpgradeCostBySID
//
procedure _unit_ApplyUpgradeCostBySID(const plHnd, cid : Integer; var upgsid : String);
begin
   var bProcess : Boolean = not (_net_IsClient or _net_IsReplay);
   if (bProcess) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var food, wood, stone, gold, iron, coal : Integer;
      _country_GetUpgradeCostBySID(cid, upgsid, food, wood, stone, gold, iron, coal);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_food, -food);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_wood, -wood);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_stone, -stone);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_gold, -gold);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_iron, -iron);
      _res_AddResToPlayerByIndex(plInd, gc_resource_type_coal, -coal);
      _misc_LanForceSyncRes;

      var upgind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, upgsid, False);
      if (upgind>-1) then
      begin
         if (gCountry[cid].upgrade[upgind].itype=gc_upg_type_single_inside_mine) then
         begin
            //log('_unit_ApplyUpgradeCostBySID : resonmines stat increase : pl='+IntToStr(plInd)+' sid='+upgsid);
            gPlayer[plInd].stat.resonmines[gc_resource_type_food] := gPlayer[plInd].stat.resonmines[gc_resource_type_food]+food;
            gPlayer[plInd].stat.resonmines[gc_resource_type_wood] := gPlayer[plInd].stat.resonmines[gc_resource_type_wood]+wood;
            gPlayer[plInd].stat.resonmines[gc_resource_type_stone] := gPlayer[plInd].stat.resonmines[gc_resource_type_stone]+stone;
            gPlayer[plInd].stat.resonmines[gc_resource_type_gold] := gPlayer[plInd].stat.resonmines[gc_resource_type_gold]+gold;
            gPlayer[plInd].stat.resonmines[gc_resource_type_iron] := gPlayer[plInd].stat.resonmines[gc_resource_type_iron]+iron;
            gPlayer[plInd].stat.resonmines[gc_resource_type_coal] := gPlayer[plInd].stat.resonmines[gc_resource_type_coal]+coal;
         end
         else
         begin
            //log('_unit_ApplyUpgradeCostBySID : resonupgrade stat increase : pl='+IntToStr(plInd)+' sid='+upgsid);
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_food] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_food]+food;
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_wood] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_wood]+wood;
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_stone] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_stone]+stone;
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_gold] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_gold]+gold;
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_iron] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_iron]+iron;
            gPlayer[plInd].stat.resonupgrade[gc_resource_type_coal] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_coal]+coal;
         end;
      end;
   end;
end;

// _unit_CancelUpgradePerform
//
procedure _unit_CancelUpgradePerform(const goHnd : Integer; var upgsid : String);
begin
   // Add write/read states to cancel perform upgrade
   var bProcess : Boolean = not (_net_IsClient or _net_IsReplay);
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var plInd : Integer = GetPlayerIndexByHandle(GetGameObjectPlayerHandleByHandle(goHnd));
   var cid : Integer = TObj(pobj).cid;
   var food, wood, stone, gold, iron, coal : Integer;
   _country_GetUpgradeCostBySID(cid, upgsid, food, wood, stone, gold, iron, coal);
   var i : Integer;
   for i:=gc_obj_MaxOrderCount-1 downto 0 do
   begin
      if (TObj(pobj).orders[i].itype=gc_obj_order_type_performupgrade) then
      begin
         var upgind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, upgsid, False);
         if (upgind>0) and (TObj(pobj).orders[i].info.upgradeid=upgind) then
         begin
            _unit_RemoveOrder(goHnd, i);
            gPlayer[plInd].upgstate[cid][upgind].inprogress := False;
            gPlayer[plInd].upgstate[cid][upgind].timestart := 0;
            if (bProcess) then
            begin
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_food, food);
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_wood, wood);
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_stone, stone);
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_gold, gold);
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_iron, iron);
               _res_AddResToPlayerByIndex(plInd, gc_resource_type_coal, coal);
               _misc_LanForceSyncRes;

               if (gCountry[cid].upgrade[upgind].itype=gc_upg_type_single_inside_mine) then
               begin
                  gPlayer[plInd].stat.resonmines[gc_resource_type_food] := gPlayer[plInd].stat.resonmines[gc_resource_type_food]-food;
                  gPlayer[plInd].stat.resonmines[gc_resource_type_wood] := gPlayer[plInd].stat.resonmines[gc_resource_type_wood]-wood;
                  gPlayer[plInd].stat.resonmines[gc_resource_type_stone] := gPlayer[plInd].stat.resonmines[gc_resource_type_stone]-stone;
                  gPlayer[plInd].stat.resonmines[gc_resource_type_gold] := gPlayer[plInd].stat.resonmines[gc_resource_type_gold]-gold;
                  gPlayer[plInd].stat.resonmines[gc_resource_type_iron] := gPlayer[plInd].stat.resonmines[gc_resource_type_iron]-iron;
                  gPlayer[plInd].stat.resonmines[gc_resource_type_coal] := gPlayer[plInd].stat.resonmines[gc_resource_type_coal]-coal;
               end
               else
               begin
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_food] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_food]-food;
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_wood] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_wood]-wood;
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_stone] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_stone]-stone;
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_gold] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_gold]-gold;
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_iron] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_iron]-iron;
                  gPlayer[plInd].stat.resonupgrade[gc_resource_type_coal] := gPlayer[plInd].stat.resonupgrade[gc_resource_type_coal]-coal;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_CancelUnitProduction
//
procedure _unit_CancelUnitProduction(const goHnd, cid, unitID, amount : Integer; bConvertToInfinite : Boolean);
begin
   // Add write/read event to cancel unit production
   var bProcess : Boolean = not (_net_IsClient or _net_IsReplay);
   var i : Integer;
   var bConverted : Boolean;
   var bPayed : Boolean;
   for i:=0 to Abs(amount)-1 do
   begin
      var j : Integer;
      for j:=gc_obj_MaxOrderCount-1 downto 0 do
      begin
         var pobj : Pointer = _unit_GetTObj(goHnd);
         var porder : Pointer = TObj(pobj).orders[j];
         if (TOrder(porder).itype=gc_obj_order_type_produce) then
         begin
            var porderinfo : Pointer = TObj(pobj).orders[j].info;
            if (TOrderInfo(porderinfo).produceid=unitID) then
            begin
               if (TOrderInfo(porderinfo).amount>0) or (TOrderInfo(porderinfo).progress>0) then
               bPayed := True;
               if (bConvertToInfinite) and ((TOrderInfo(porderinfo).amount=0) or ((TOrderInfo(porderinfo).amount=1))) then
               begin
                  if (TOrderInfo(porderinfo).progress>0) then
                  bPayed := False;
                  bConverted := True;
               end;
               if (TOrderInfo(porderinfo).amount>0) then
               TOrderInfo(porderinfo).amount := TOrderInfo(porderinfo).amount-1;
               if (TOrderInfo(porderinfo).amount=0) then
               begin
                  if (bConvertToInfinite) then
                  TOrderInfo(porderinfo).amount := gc_obj_order_produce_infinite
                  else
                  TOrderInfo(porderinfo).progress := 0;
               end;
               if (bProcess) and (bPayed) then
               begin
                  var pl : Integer = TObj(pobj).pl;
                  var costpercent : Float = gObjProp[cid][unitID].costpercent;
                  var costmodifier : Float = 1;
                  if (costpercent<>100) and (costpercent<>0) then
                  begin
                     var count : Integer = TOrderInfo(porderinfo).restype;
                     var bmercenary : Boolean = gObjProp[cid][unitID].bmercenary;
                     if (bmercenary) then
                     begin
                        var sid : String = gObjProp[cid][unitID].sid;
                        var tmpsid : String;
                        case sid of
                           'archerdip' : tmpsid := 'archerturdip';
                           'archerturdip' : tmpsid := 'archerdip';
                           'dragoon18dip' : tmpsid := 'lightcavalrydip';
                           'lightcavalrydip' : tmpsid := 'dragoon18dip';
                        end;
                        var tmpid : Integer = _unit_ConvertObjSIDToID(cid, tmpsid);
                        count := count+gPlayer[pl].counter.all[cid][tmpid];
                     end;
                     costmodifier := pow(costpercent/100, count);
                     if (bmercenary) and (costmodifier>2) then
                     costmodifier := 2
                     else
                     if (costmodifier>20000) then
                     costmodifier := 20000;
                  end;

                  var k : Integer;
                  for k:=0 to gc_ResCount-1 do
                  case k of
                     gc_resource_type_food : _res_AddResToPlayerByIndex(pl, gc_resource_type_food, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                     gc_resource_type_wood : _res_AddResToPlayerByIndex(pl, gc_resource_type_wood, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                     gc_resource_type_stone : _res_AddResToPlayerByIndex(pl, gc_resource_type_stone, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                     gc_resource_type_gold : _res_AddResToPlayerByIndex(pl, gc_resource_type_gold, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                     gc_resource_type_iron : _res_AddResToPlayerByIndex(pl, gc_resource_type_iron, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                     gc_resource_type_coal : _res_AddResToPlayerByIndex(pl, gc_resource_type_coal, floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier)));
                  end;
                  for k:=0 to gc_ResCount-1 do
                  case k of
                     gc_resource_type_food : gPlayer[pl].stat.resonunits[gc_resource_type_food] := gPlayer[pl].stat.resonunits[gc_resource_type_food]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                     gc_resource_type_wood : gPlayer[pl].stat.resonunits[gc_resource_type_wood] := gPlayer[pl].stat.resonunits[gc_resource_type_wood]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                     gc_resource_type_stone : gPlayer[pl].stat.resonunits[gc_resource_type_stone] := gPlayer[pl].stat.resonunits[gc_resource_type_stone]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                     gc_resource_type_gold : gPlayer[pl].stat.resonunits[gc_resource_type_gold] := gPlayer[pl].stat.resonunits[gc_resource_type_gold]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                     gc_resource_type_iron : gPlayer[pl].stat.resonunits[gc_resource_type_iron] := gPlayer[pl].stat.resonunits[gc_resource_type_iron]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                     gc_resource_type_coal : gPlayer[pl].stat.resonunits[gc_resource_type_coal] := gPlayer[pl].stat.resonunits[gc_resource_type_coal]+floor(gPlayer[pl].objbase[cid][unitID].price[k]*(costmodifier));
                  end;
               end;
               if (not bConverted) and ((TOrderInfo(porderinfo).amount=0) or (TOrderInfo(porderinfo).amount=gc_obj_order_produce_infinite)) then
               begin
                  _unit_RemoveOrder(goHnd, j);
                  if (j=0) and (_unit_GetUsage(goHnd)=gc_obj_usage_port) then
                  _unit_RemoveShipDummy(goHnd);
               end;
            end;
         end;
      end;
   end;
   if (bProcess) then
   _misc_LanForceSyncRes;
end;

// _unit_OrderProduce
//
procedure _unit_OrderProduce(const goHnd, ordercid, orderid, amount : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pl : Integer = TObj(pobj).pl;
   var bMerged : Boolean;
   var bExit : Boolean;
   //var unitcount : Integer = gPlayer[pl].counter.all[cid][id];
   var unitcount : Integer = gPlayer[pl].counter.all[ordercid][orderid];
   var i : Integer;
   for i:=gc_obj_MaxOrderCount-1 downto 0 do
   begin
      var porderinfo : Pointer = TObj(pobj).orders[i].info; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(porderinfo);
      if (TObj(pobj).orders[i].itype=gc_obj_order_type_produce) then
      begin
         if (TOrderInfo(porderinfo).produceid=orderid) then
         begin
            if (amount=gc_obj_order_produce_infinite) and (TOrderInfo(porderinfo).amount>0) then
            begin
               _unit_CancelUnitProduction(goHnd, ordercid, orderid, TOrderInfo(porderinfo).amount, True);
               bExit := True;
            end
            else
            if (gObjProp[ordercid][orderid].costpercent=100) or (gObjProp[ordercid][orderid].costpercent=0) or (unitcount=TObj(pobj).orders[i].info.restype) then // restype stores amount of units, that we had at the moment of ordering produce. it is needed to properly give player moneyback, when unit costpercent is different from 100%, when player cancel unit production.
            begin
               if (not ((TOrderInfo(porderinfo).amount=gc_obj_order_produce_infinite) and (amount>0))) then
               begin
                  if (amount=gc_obj_order_produce_infinite) then
                  TOrderInfo(porderinfo).amount := gc_obj_order_produce_infinite
                  else
                  TOrderInfo(porderinfo).amount := TOrderInfo(porderinfo).amount+amount;

                  if (amount<>gc_obj_order_produce_infinite) then
                  begin
                     var j : Integer;
                     for j:=0 to amount-1 do
                     _unit_ApplyCostByID(ordercid, orderid, pl)
                  end;
                  bMerged := True;
               end
               else
               begin
                  bExit := True;
                  break;
               end;
            end;
         end;
      end;
   end;
   if (not bExit) and (not bMerged) then
   begin
      if (amount<>gc_obj_order_produce_infinite) and (gObjProp[ordercid][orderid].usage<>gc_obj_usage_field) then
      begin
         var j : Integer;
         for j:=0 to amount-1 do
         _unit_ApplyCostByID(ordercid, orderid, pl)
      end;
      _unit_AddOrder(goHnd, gc_obj_order_type_produce, 0, 0, 0, 0, 0, 0, 0, ordercid, orderid, unitcount, amount, 0, False, False);
   end;
end;

// _unit_OrderUpgrade
//
procedure _unit_OrderUpgrade(const goHnd : Integer; var upgsid : String);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pl : Integer = TObj(pobj).pl;
   var cid : Integer = TObj(pobj).cid;
   var upgind : Integer = _country_GetUpgradeIndexByUpgradeID(cid, upgsid, False);
   if (upgind>-1) then
   begin
      if (gPlayer[pl].upgstate[cid][upgind].sid=upgsid) and ((not gPlayer[pl].upgstate[cid][upgind].inprogress) or (gCountry[cid].upgrade[upgind].bindividual)) then
      begin
         var plHnd : Integer = GetPlayerHandleByIndex(pl);
         if (_unit_CanApplyUpgradeCostBySID(plHnd, cid, upgsid)) then
         _unit_AddOrder(goHnd, gc_obj_order_type_performupgrade, 0, 0, 0, 0, 0, 0, 0, upgind, 0, 0, 0, 0, False, True); // bDoFirst=True
      end;
   end
   else
   ErrorLog('_unit_OrderUpgrade : Unknown upgrade');
end;

// _unit_IsStand
//
function _unit_IsStand(const goHnd : Integer) : Boolean;
begin
   Result := True;
   var tpCount : Integer = GetGameObjectTrackPointCountByHandle(goHnd);
   if (tpCount>0) then
   begin
      var goX : Float = GetGameObjectPositionXByHandle(goHnd);
      var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
      var tpX, tpY, tpZ : Float;
      GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, tpCount-1, tpX, tpY, tpZ);
      var dist : Float = VectorDistance(goX, 0, goZ, tpX, 0, tpZ);
      if (dist>gc_objectEpsilonDist) then
      begin
         //var mode : String = GetGameObjectTrackPointMovementModeByHandle(goHnd);
         //if (mode<>'mmNone') then
         if (GetGameObjectTrackPointMovementModeIntByHandle(goHnd)<>0) then
         Result := False;
      end;
   end;
end;

// _unit_IsAttack
//
function _unit_IsAttack(const goHnd : Integer) : Boolean;
begin
   var anim : String = GetGameObjectAnimationCycleNameByHandle(goHnd);
   Result := (StrExists(anim, 'attack'));
   //Result := (anim=gc_anim_attack0) or (anim=gc_anim_attack1) or (anim=gc_anim_attack2);
end;

// _unit_IsBuild
//
function _unit_IsBuild(const goHnd : Integer) : Boolean;
begin
   var anim : String = GetGameObjectAnimationCycleNameByHandle(goHnd);
   Result := (anim=gc_anim_construct);
end;

// _unit_IsIdle
//
function _unit_IsIdle(const goHnd : Integer) : Boolean;
begin
   Result := (_unit_IsStand(goHnd)) and (not (_unit_IsAttack(goHnd)));
end;

// _unit_IsMovingNonAttack
//
function _unit_IsMovingNonAttack(const goHnd : Integer) : Boolean;
begin
   Result := ((GetGameObjectSTOHandleByHandle(goHnd)=0) and (not _unit_IsStand(goHnd)));
end;

// _unit_GetDistance
//
function _unit_GetDistance(const goHnd, trgHnd : Integer) : Float;
begin
   Result := VectorDistance(GetGameObjectPositionXByHandle(goHnd), 0, GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
end;

// _unit_GetPathDistance
//
function _unit_GetPathDistance(const goHnd, trgHnd : Integer; const bIncludeBuildings : Boolean) : Float;
begin
   // TODO : calculate path distance, if unit would goes there
   Result := TopologyGetPathDistance(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd), bIncludeBuildings);
end;

// _unit_GetPathDistanceBetweenPoints
//
function _unit_GetPathDistanceByCoord(const px, py, dx, dy : Float; const bIncludeBuildings : Boolean) : Float;
begin
   // TODO : calculate path distance, if unit would goes there
   Result := TopologyGetPathDistance(px, py, dx, dy, bIncludeBuildings);
end;

// _unit_FindNearestUnit
//
function _unit_FindNearestUnit(px, py, rad : Float; cid, id, plHnd : Integer; bCheckNotInSquad : Boolean) : Integer;
begin
   Result := 0;
   var minTrgDist : Float = -1;
   var minTrgHnd : Integer;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   _misc_MakeListObjectsInRadius(px, py, rad, true, gObjProp[cid][id].mmask, 0, True, gIntegerList);
   var i : Integer;
   for i:=gIntegerList.GetCount-1 downto 0 do
   begin
      var trgHnd : Integer = gIntegerList.Get(i);
      if (trgHnd<>0) then
      begin
         var pobj : Pointer = _unit_GetTObj(trgHnd);
         if (pobj<>nil) and (TObj(pobj).pl=plInd) and (TObj(pobj).id=id) and (TObj(pobj).cid=cid) and ((GetGameObjectStatesTagByHandle(trgHnd) and gc_statetag_essential_none)<>0) then
         begin
            if (not bCheckNotInSquad) or (TObj(pobj).squad<0) then
            begin
               var tmpDist : Float = VectorDistance(px, 0, py, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
               if (tmpDist<rad) and ((minTrgHnd=0) or (tmpDist<minTrgDist)) then
               begin
                  minTrgDist := tmpDist;
                  minTrgHnd := trgHnd;
               end;
            end;
         end;
      end;
   end;
   if (minTrgHnd<>0) then
   Result := minTrgHnd;
end;

// _unit_FindUnitsAndFillList
//
function _unit_FindUnitsAndFillList(px, py, rad : Float; cid, id : Integer; plHnd, needcount, armySpec : Integer; bSearchNearest, bSearchSquad, bClearLess : Boolean; precise : Float; var list : TIntegerList) : Integer;
begin
   Result := 0;
   list.Clear;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var neededmaterial : Integer;
   if (cid>-1) and (id>-1) then
   neededmaterial := gObjProp[cid][id].mmask;
   var count : Integer;
   var itry : Integer;
   var trycount : Integer;
   var tmpprecise : Float = precise;
   if (tmpprecise<0.1) then
   begin
      tmpprecise := 0.1;
      ErrorLog('_unit_FindUnitsAndFillList : too low precise setting');
   end;
   if (bSearchNearest) then
   trycount := floor(1+(rad/precise))
   else
   trycount := 1;
   if (trycount>100) then
   ErrorLog('_unit_FindUnitsAndFillList : too low precise setting. trycount='+IntToStr(trycount));
   gTmpIntListFindUnits.Clear;
   for [MAIN]itry:=1 to trycount do
   begin
      _misc_MakeListObjectsInRadius(px, py, (itry/trycount)*rad, True, neededmaterial, 0, True, gTmpIntListFindUnits);
      var i : Integer;
      for i:=gTmpIntListFindUnits.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = gTmpIntListFindUnits.Get(i);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) and (TObj(pobj).pl=plInd) and ((cid=-1) or (TObj(pobj).cid=cid)) and ((id=-1) or (TObj(pobj).id=id)) and (_unit_GetTagStateByType(goHnd, gc_statetag_essential_none)<>0) and (not _unit_IsGuard(goHnd)) then
         begin
            var bContinue : Boolean = (not bSearchNearest);
            if (not bContinue) then
            begin
               var dist : Float = VectorDistance(px, 0, py, GetGameObjectPositionXByHandle(goHnd), 0, GetGameObjectPositionZByHandle(goHnd));
               bContinue := ((dist<=(itry/trycount)*rad) and (dist>((itry-1)/trycount)*rad)) or ((itry=1) and (dist=0));
            end;
            //if (not bSearchNearest) or ((dist<=(itry/trycount)*rad) and (dist>((itry-1)/trycount)*rad)) then
            if (bContinue) then
            begin
               var add : Boolean;
               if TObj(pobj).squad < 0 then
               add := true
               else
               if bSearchSquad then
               begin
                  if armySpec < 0 then
                  add := true
                  else
                  begin
                     var pSquad : Pointer = gPlayer[plInd].squads.Get(TObj(pobj).squad);
                     if (pSquad<>nil) and (TSquad(pSquad).fArmy >= 0) then
                     begin
                        var pArmy : Pointer = gPlayer[plInd].aiData.armyList.Get(TSquad(pSquad).fArmy);
                        if (pArmy<>nil) and (TArmy(pArmy).fSpec = armySpec) then
                        add := true;
                     end;
                  end;
               end;

               if add then
               begin
                  list.Add(goHnd);
                  if (needcount>0) then
                  begin
                     count := count+1;
                     if (count>=needcount) then
                     begin
                        Result := count;
                        break(MAIN);
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;
   if bClearLess then
   begin
      if (Result=0) then
      list.Clear;
   end
   else
   Result := list.GetCount;
end;

// _unit_MakeSquadList
//
function _unit_MakeSquadList(officerhnd, formInd, armySpec : Integer; rad : Float; var unitsid : String; var list : TIntegerList; offDrum : Boolean) : Boolean;
begin
   Result := False;
   list.Clear;
   var pobj : Pointer = _unit_GetTObj(officerhnd);
   var cid : Integer = TObj(pobj).cid;
   var officersid : String = GetGameObjectBaseNameByHandle(officerhnd);
   var ind : Integer = _country_GetOfficersIndexByOfficerSID(cid, officersid);
   var plHnd : Integer = GetGameObjectPlayerHandleByHandle(officerhnd);
   var drummerid : Integer = _unit_ConvertObjSIDToID(cid, gCountry[cid].officers[ind].drummersid);
   var drummerhnd : Integer = _unit_FindNearestUnit(GetGameObjectPositionXByHandle(officerhnd), GetGameObjectPositionZByHandle(officerhnd), rad, cid, drummerid, plHnd, True);
   if (drummerhnd<>0) then
   begin
      var reqcount : Integer = gFormation[formInd].countunits;
      var id : Integer = _unit_ConvertObjSIDToID(cid, unitsid);

      if armySpec < 0 then
      _unit_FindUnitsAndFillList(GetGameObjectPositionXByHandle(officerhnd), GetGameObjectPositionZByHandle(officerhnd), rad, cid, id, plHnd, reqcount, -1, True, False, True, 1, list)
      else
      _unit_FindUnitsAndFillList(GetGameObjectPositionXByHandle(officerhnd), GetGameObjectPositionZByHandle(officerhnd), rad, cid, id, plHnd, reqcount, armySpec, True, True, True, 4.825, list);

      var count : Integer = list.GetCount;
      Result := (count>=reqcount);
      if offDrum then
      begin
         if (_unit_IsOfficer(officerhnd)) then
         list.Add(officerhnd);
         if (_unit_IsDrummer(drummerhnd)) then
         list.Add(drummerhnd);
      end;
   end;
   //else
   //ErrorLog('MakeSquad, no drummer nearby. but it was on element click');
   if (not Result) then
   list.clear;
end;

// _unit_PredictShot
//
//function _unit_PredictShot(weapon : TWeapon; posX, posY, posZ, trgX, trgY, trgZ : Float) : Integer;
//begin
   //   // remove weapon : TWeapon, add goHnd, weapInd instead
   //   Result := 1;
//end;

// _unit_GetWeaponToAttackIndexByPoint
//
function _unit_GetWeaponToAttackIndexByPoint(goHnd : Integer; tx, tz : Float) : Integer;
begin
   Result := -1;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var px, py, pz : Float;
      GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
      var dstHeight : Float;
      if (not RayCastWater(tx, tz, dstHeight)) then // is water, then ty is water height
      dstHeight := RayCastHeight(tx, tz);
      var dist : Float = VectorDistance(px, 0, pz, tx, 0, tz);
      var bonusrng : Float = (py-dstHeight)*2;
      var nearestWeaponInd : Integer = -1;
      var nearestWeaponDist : Float;
      var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var rbonus : Float;
      var i : Integer;
      for i:=0 to gc_obj_MaxWeapon-1 do
      begin
         if (TObjBase(pobjbase).weapon[i].radiusmax>gc_unit_meleeattackradius) then
         rbonus := bonusrng
         else
         rbonus := 0;
         if (TObjProp(pobjprop).weapon[i].enabled) then
         begin
            if (dist>=TObjProp(pobjprop).weapon[i].detectradiusmin) and (dist<=(TObjProp(pobjprop).weapon[i].detectradiusmax+rbonus)) then
            begin
               var bDistanceOk : Boolean = (dist>=TObjProp(pobjprop).weapon[i].radiusmin) and (dist<(TObjBase(pobjbase).weapon[i].radiusmax+rbonus));
               var tmpNearestWeaponDist : Float = _misc_MinFloat(abs(TObjProp(pobjprop).weapon[i].radiusmin-dist), abs((TObjBase(pobjbase).weapon[i].radiusmax+rbonus)-dist));
               if (nearestWeaponInd=-1) or (tmpNearestWeaponDist<nearestWeaponDist) or (bDistanceOk) then
               begin
                  nearestWeaponInd := i;
                  nearestWeaponDist := tmpNearestWeaponDist;
                  if (bDistanceOk) then
                  break;
               end;
            end;
         end;
      end;
      Result := nearestWeaponInd;
   end
   else
   ErrorLog('_unit_GetWeaponToAttackIndexByPoint : pobj=nil');
end;

// _unit_GetWeaponToAttackIndex
//
function _unit_GetWeaponToAttackIndex(goHnd, trgHnd : Integer) : Integer; // c1:~CheckDamageAbility
begin
   Result := -1;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pobj2 : Pointer = _unit_GetTObj(trgHnd);
   if (pobj<>nil) and (pobj2<>nil) then
   begin
      var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var px, py, pz, tx, ty, tz : Float;
      GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
      GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz);
      var dist : Float = VectorDistance(px, 0, pz, tx, 0, tz);
      var bonusrng : Float = (py-ty)*2;
      var nearestWeaponInd : Integer = -1;
      var nearestWeaponDist : Float;
      var enemymat : Integer = gObjProp[TObj(pobj2).cid][TObj(pobj2).id].material;
      var bCanTarget : Boolean = TObjProp(pobjprop).cankill[enemymat];
      var AttMaskInd : Integer = -1;

      var i : Integer;
      for i:=0 to gc_obj_MaxWeapon-1 do
      if (TObjProp(pobjprop).weapon[i].enabled) and ((TObjProp(pobjprop).weapon[i].attmask and gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask)<>0) then
      begin
         AttMaskInd := i;
         break;
      end;

      var rbonus : Float;
      if (attMaskInd>-1) then
      begin
         if (TObjBase(pobjbase).weapon[attMaskInd].radiusmax>gc_unit_meleeattackradius) then
         rbonus := bonusrng
         else
         rbonus := 0;
         if (dist>=TObjProp(pobjprop).weapon[attMaskInd].detectradiusmin) and (dist<=(TObjProp(pobjprop).weapon[attMaskInd].detectradiusmax+rbonus)) then
         begin
            var tmpNearestWeaponDist : Float = _misc_MinFloat(abs(TObjProp(pobjprop).weapon[attMaskInd].radiusmin-dist), abs((TObjBase(pobjbase).weapon[attMaskInd].radiusmax+rbonus)-dist));
            if (nearestWeaponInd=-1) or (tmpNearestWeaponDist<nearestWeaponDist) then
            begin
               nearestWeaponInd := attMaskInd;
               nearestWeaponDist := tmpNearestWeaponDist;
            end;
         end;
      end;

      if (bCanTarget) then
      for i:=0 to gc_obj_MaxWeapon-1 do
      begin
         if (TObjBase(pobjbase).weapon[i].radiusmax>gc_unit_meleeattackradius) then
         rbonus := bonusrng
         else
         rbonus := 0;
         if (TObjProp(pobjprop).weapon[i].enabled) and (i<>AttMaskInd) then
         begin
            if (dist>=TObjProp(pobjprop).weapon[i].detectradiusmin) and (dist<=(TObjProp(pobjprop).weapon[i].detectradiusmax+rbonus)) then
            begin
               var bDistanceOk : Boolean = (dist>=TObjProp(pobjprop).weapon[i].radiusmin) and (dist<(TObjBase(pobjbase).weapon[i].radiusmax+rbonus));

               var tmpNearestWeaponDist : Float = _misc_MinFloat(abs(TObjProp(pobjprop).weapon[i].radiusmin-dist), abs((TObjBase(pobjbase).weapon[i].radiusmax+rbonus)-dist));
               if (nearestWeaponInd=-1) or (tmpNearestWeaponDist<nearestWeaponDist) or (bDistanceOk) then
               begin
                  nearestWeaponInd := i;
                  nearestWeaponDist := tmpNearestWeaponDist;
                  if (bDistanceOk) then
                  break;
               end
            end;
         end;
      end;
      //var res : Integer = _unit_PredictShot(weapon, GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionYByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionYByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd));
      Result := nearestWeaponInd;
   end
   else
   ErrorLog('_unit_GetWeaponToAttackIndex : pobj=nil or pobj2=nil');
end;

// _unit_GetGameObjectPos
//
procedure _unit_GetGameObjectPos(goHnd : Integer; var pos : TPos3f);
begin
   GetGameObjectAbsolutePositionByHandle(goHnd, pos.x, pos.y, pos.z);
end;

// _unit_GetGameObjectPos2f
//
procedure _unit_GetGameObjectPos2f(goHnd : Integer; var pos : TPos2f);
begin
   pos.x := GetGameObjectPositionXByHandle(goHnd);
   pos.y := GetGameObjectPositionZByHandle(goHnd);
end;

// _unit_DoSeedWheat
//
procedure _unit_DoSeedWheat(const goHnd : Integer; const bAuto : Boolean);
begin
   //if (bAuto) then
   //seed less wheat then when we press doseed manually. Done same way as in original Cossacks.

   const cRow = 7;
   const cCount = cRow*cRow;
   const size = 1.6981;//1.5;//_misc_PixelsToTiles(80); //90, 64
   const cScale = 1.09245;//0.965;
   const cRollAngle = 0;//30;

   var sid : String = gc_basename_field;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var id : Integer = _unit_ConvertObjSIDToID(TObj(pobj).cid, sid);

   var maphalfwidth : Integer = GetMapWidth div 2;
   var maphalfheight : Integer = GetMapHeight div 2;

   var i : Integer;
   for i:=0 to cCount-1 do
   begin
      var dirX : Float = -((cRow-0.5)*size/2)+(i mod cRow)*size;
      var dirY : Float;
      var dirZ : Float = -((cRow-0.5)*size/2)+(i div cRow)*size;
      VectorRotateY(dirX, dirY, dirZ, 45);
      var px : Float = GetGameObjectPositionXByHandle(goHnd);
      var pz : Float = GetGameObjectPositionZByHandle(goHnd);
      var size2 : Float = sqrt(2*size*size);// _misc_PixelsToTiles(114);// 90
      px := floor((px+size)/size2)*size2; // making discreat and adjustment for better view
      pz := floor((pz+size*0.5)/size2)*size2; // making discreat and adjustment for better view

      var xOff : Float = px+dirX;
      var yOff : Float = pz+dirZ;

      var bCollision : Boolean;
      var j : Integer;
      for j:=0 to 25-1 do // size of wheat field to scan is 5x5 collision blocks rotated at 45 degree
      begin
         var dirx : Float = (-2+j div 5)*gc_collision_size;
         var diry : Float;
         var dirz : Float = (-2+j mod 5)*gc_collision_size;
         VectorRotateY(dirx, diry, dirz, 45);

         var cx : Float = xOff+dirx;
         var cy : Float = yOff+dirz;

         if (GetMapCollisionTag(cx, cy, True)>0) or (Abs(cx)>=maphalfwidth) or (Abs(cy)>=maphalfheight) then
         begin
            bCollision := True;
            break;
         end;
      end;

      if (not bCollision) then
      begin
         var bStaticObjects : Boolean;

         var rad : Float = size*0.9*3;
         _misc_MakeListEnvironmentInRadius(xOff, yOff, rad, true, true, true, true, true, gIntegerList);
         for j:=gIntegerList.GetCount-1 downto 0 do
         begin
            var envHnd : Integer = gIntegerList.Get(j);
            var allowdst : Float;
            var pRes : Pointer = _res_GetTRes(envHnd);
            if (pRes<>nil) then
            begin
               case TRes(pRes).itype of
                  gc_resource_type_food, gc_resource_type_wood, gc_resource_type_stone : allowdst := rad/3;
                  gc_resource_type_gold, gc_resource_type_iron, gc_resource_type_coal : allowdst := rad;
               end;

               var tmpDist : Float = VectorDistance(xOff, 0, yOff, GetGameObjectPositionXByHandle(envHnd), 0, GetGameObjectPositionZByHandle(envHnd));
               if (tmpDist<allowdst) then
               begin
                  bStaticObjects := True;
                  break;
               end;
            end;
         end;

         if (not bStaticObjects) then
         begin
            if (GetCountOfPlayers>=gc_playerind_env) and ((_unit_CanApplyCostByID(TObj(pobj).cid, id, TObj(pobj).pl) or (_net_IsClient or _net_IsReplay))) then
            begin
               var roll : Float;
               var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
               var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
               TPlayerArgs(parg).fposx:=xOff;
               TPlayerArgs(parg).fposz:=yOff;
               TPlayerArgs(parg).fdirx:=roll;
               TPlayerArgs(parg).fracename:=gc_racename_env;
               TPlayerArgs(parg).fbasename:=sid;
               TPlayerArgs(parg).fplayer:=gc_playerind_env;
               PlayerExecuteStateByHandle(plHnd, 'WriteNewP');
               var fieldHnd: Integer = TPlayerArgs(parg).fresult;
               _unit_ApplyCostByID(TObj(pobj).cid, id, TObj(pobj).pl);
            end;
         end;
      end;
   end;
end;

// _unit_ControlBuildProgress
//
procedure _unit_ControlBuildProgress(const hnd: Integer);
begin
   // Обновления прогресса строительства недостроенного здания (изменение стадий постройки и пересчет progress'а). Регистрация завершенного здания в счетчиках (число построенных здания, количество жилых мест, мельница садит пшеницу, выдача сообщения игроку, что здание построено.
   var pobj: Pointer=_unit_GetTObj(hnd);
   if(pobj<>nil) and (not TObj(pobj).bbuilt) then begin
      var pobjbase: Pointer=gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id]; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(pobjbase);
      if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) and (TObj(pobj).individual.upglevel>0) then
      TObj(pobj).hp := gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].maxhp;
      var newprogress: Float;
      if TObjBase(pobjbase).maxhp>0 then
      newprogress:=TObj(pobj).hp/TObjBase(pobjbase).maxhp
      else begin
         newprogress:=1;
         ErrorLog('_unit_ControlBuildProgress: maxhp=0');
      end;
      if newprogress>TObj(pobj).buildprogress then
      TObj(pobj).buildprogress:=newprogress;
      if TObj(pobj).hp>=TObjBase(pobjbase).maxhp then
      _unit_SetTagStates(hnd, gc_statetag_essential_none or gc_statetag_visual_none)
      else begin
         if TObj(pobj).buildprogress>1 then
         TObj(pobj).buildprogress:=1;
         var stage: Integer=floor(TObj(pobj).buildprogress*4);
         var visualtag: Integer=gc_statetag_visual_none;
         case stage of
            0: visualtag:=gc_statetag_visual_stage_0;
            1: visualtag:=gc_statetag_visual_stage_1;
            2: visualtag:=gc_statetag_visual_stage_2;
            3: visualtag:=gc_statetag_visual_stage_3;
         end;
         if (_unit_GetTagStateByType(hnd, gc_statetag_essential_death)=0) and (_unit_GetTagStateByType(hnd, visualtag)=0) then
         _unit_SetTagStates(hnd, gc_statetag_essential_birth or visualtag);
      end;
   end;
end;

// _unit_CanSetRallyPoint
//
function _unit_CanSetRallyPoint(const goHnd : Integer) : Boolean;
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   Result := (TObjProp(pobjprop).peasantabsorber>0);
   if (not Result) and (TObjProp(pobjprop).usage<>gc_obj_usage_mill) then
   begin
      var member : String = GetGameObjectBaseNameByHandle(goHnd);
      if (member<>'') then
      begin
         var fixedproduceInd : Integer = _country_GetFixedProduceIndexBySID(TObj(pobj).cid, member, False);
         if (fixedproduceInd>=0) and (TObjProp(pobjprop).bbuilding) then
         Result := True;
      end;
   end;
end;

// _unit_FindMinePositionAndType
//
procedure _unit_FindMinePositionAndType(const aproxpx, aproxpy, rad : Float; var restype, reshnd : Integer; var posx, posy : Float);
begin
   restype := gc_resource_type_none;
   resHnd := 0;
   posx := 0;
   posy := 0;
   if (GetCountOfPlayers>=gc_playerind_env) then
   begin
      var envPlHnd : Integer = GetPlayerHandleByIndex(gc_playerind_env);
      var minDist : Float = -1;
      var minHnd : Integer;
      var minResType : Integer;
      _misc_MakeListEnvironmentInRadius(aproxpx, aproxpy, rad, true, false, false, false, true, gIntegerList);
      var i : Integer;
      for i:=gIntegerList.GetCount-1 downto 0 do
      begin
         var tmpHnd : Integer = gIntegerList.Get(i);
         var tmpDist : Float = VectorDistance(aproxpx, 0, aproxpy, GetGameObjectPositionXByHandle(tmpHnd), 0, GetGameObjectPositionZByHandle(tmpHnd));
         if (minHnd=0) or (tmpDist<minDist) then
         begin
            var pres : Pointer = _res_GetTRes(tmpHnd);
            if (pres<>nil) then
            begin
               var itype : Integer = TRes(pres).itype;
               if (itype=gc_resource_type_gold) or (itype=gc_resource_type_iron) or (itype=gc_resource_type_coal) then
               begin
                  minResType := itype;
                  minDist := tmpDist;
                  minHnd := tmpHnd;
               end;
            end;
         end;
      end;
      if (minHnd<>0) then
      begin
         reshnd := minHnd;
         restype := minResType;
         posx := GetGameObjectPositionXByHandle(reshnd);
         posy := GetGameObjectPositionZByHandle(reshnd);
      end;
   end;
end;

// _unit_DoConstructionBlinkGameObject
//
procedure _unit_DoConstructionBlinkGameObject(goHnd, mode : Integer; bUseColor, bJustUpdate : Boolean);
begin
   var effHnd : Integer;
   var totalTime : Float = 0.525*1.25;
   if (GetTimeSpeedFactor>0) then
   totalTime := totalTime*(GetTimeSpeedFactor/10);
   var cr, cg, cb : Float = 1;
   var cr0, cg0, cb0 : Float = 1;
   var minalpha, maxalpha : Float = 1;
   var bemission : Boolean;
   case mode of
      gc_gui_blinkmode_canbuild : begin
         cr0 := 0.7;
         cg0 := 0.7;
         cb0 := 0.7;
         cr := 0.7;
         cg := 0.7;
         cb := 0.7;
         minalpha := 0.35;
         maxalpha := 0.5;
         bemission := False;
      end;
      gc_gui_blinkmode_cannotbuild : begin
         cr0 := 0.9;
         cg0 := 0.2;
         cb0 := 0.1;
         cr := 0.9;
         cg := 0.2;
         cb := 0.1;
         minalpha := 0.45;
         maxalpha := 0.5;
         bemission := True;
      end;
   end;

   if (bJustUpdate) then
   begin
      effHnd := GetEffectByKey(goHnd, 'blink');
      if (effHnd<>0) then
      begin
         if (bUseColor) then
         begin
            SetBehaviourVectorProperty(effHnd, 'Color[0]', cr, cg, cb, maxalpha);
            SetBehaviourVectorProperty(effHnd, 'Color[1]', cr0, cg0, cb0, minalpha);
            SetBehaviourVectorProperty(effHnd, 'Color[2]', cr, cg, cb, maxalpha);
         end
         else
         begin
            SetBehaviourFloatProperty(effHnd, 'AlphaChannel[0]', maxalpha);
            SetBehaviourFloatProperty(effHnd, 'AlphaChannel[1]', minalpha);
            SetBehaviourFloatProperty(effHnd, 'Time[0]', totalTime);
            SetBehaviourFloatProperty(effHnd, 'Time[1]', totalTime);
            SetBehaviourBoolProperty(effHnd, 'UseEmission', bemission);
         end;
      end
      else
      ErrorLog('DoBlinkGameObject (bJustUpdate=True) : effHnd=0');
   end
   else
   begin
      BehaviourDestroy(GetEffectByKey(goHnd, 'blink'));
      effHnd := EffectCreateWithKey(goHnd, 'TXLifeColorEffect', 'blink', false);
      SetBehaviourBoolProperty(effHnd, 'UseAmbient', False);
      SetBehaviourBoolProperty(effHnd, 'UseDiffuse', False);
      SetBehaviourBoolProperty(effHnd, 'UseEmission', bemission);
      SetBehaviourBoolProperty(effHnd, 'UseSpecular', False);
      SetBehaviourBoolProperty(effHnd, 'UseShininess', False);
      SetBehaviourBoolProperty(effHnd, 'UseTexOffset', False);
      SetBehaviourBoolProperty(effHnd, 'UseTexScale', False);
      SetBehaviourBoolProperty(effHnd, 'UseShaderID', False);
      SetBehaviourBoolProperty(effHnd, 'UseJustColorIfNoLight', True);
      SetBehaviourBoolProperty(effHnd, 'UseFrontFace', True);
      SetBehaviourBoolProperty(effHnd, 'UseBackFace', True);
      SetBehaviourBoolProperty(effHnd, 'UseIgnoreFog', False);
      SetBehaviourBoolProperty(effHnd, 'UseNoLighting', False);
      SetBehaviourIntProperty(effHnd,  'CurrIndex', 0);
      SetBehaviourBoolProperty(effHnd, 'Enable', True);
      SetBehaviourBoolProperty(effHnd, 'Loop', True); // False
      SetBehaviourBoolProperty(effHnd, 'LoopBackward', False);
      SetBehaviourBoolProperty(effHnd, 'BounceBackward', False);
      SetBehaviourBoolProperty(effHnd, 'ZWrite', True);
      SetBehaviourBoolProperty(effHnd, 'ZTest', True);
      SetBehaviourBoolProperty(effHnd, 'UseBlendMode', True);
      SetBehaviourBoolProperty(effHnd, 'UseCullFace', True);
      SetBehaviourIntProperty(effHnd, 'BlendingMode', 1); // bmTransparency
      SetBehaviourIntProperty(effHnd, 'FaceCulling', 0);  // fcCull

      if (bUseColor) then
      begin
         SetBehaviourBoolProperty(effHnd, 'UseAlphaChannel', False);
         SetBehaviourBoolProperty(effHnd, 'UseColor', True);
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[0]', 0);
         SetBehaviourVectorProperty(effHnd, 'Color[0]', cr, cg, cb, maxalpha);
         SetBehaviourFloatProperty(effHnd, 'Time[0]', totalTime);
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[1]', 1);
         SetBehaviourVectorProperty(effHnd, 'Color[1]', cr0, cg0, cb0, minalpha);
         SetBehaviourFloatProperty(effHnd, 'Time[1]', totalTime);
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[2]', 1);
         SetBehaviourVectorProperty(effHnd, 'Color[2]', cr, cg, cb, maxalpha);
         SetBehaviourFloatProperty(effHnd, 'Time[2]', 0);
      end
      else
      begin
         SetBehaviourBoolProperty(effHnd, 'UseAlphaChannel', True);
         SetBehaviourBoolProperty(effHnd, 'UseColor', False);
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourStringProperty(effHnd, 'Add', '');
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[0]', maxalpha);
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[1]', minalpha);
         SetBehaviourFloatProperty(effHnd, 'AlphaChannel[2]', maxalpha);
         SetBehaviourFloatProperty(effHnd, 'Time[0]', totalTime);
         SetBehaviourFloatProperty(effHnd, 'Time[1]', totalTime);
         SetBehaviourFloatProperty(effHnd, 'Time[2]', totalTime);
         SetBehaviourVectorProperty(effHnd, 'Emission[0]', 0.8, 0, 0, 1);
         SetBehaviourVectorProperty(effHnd, 'Emission[1]', 0.6, 0, 0, 1);
         SetBehaviourVectorProperty(effHnd, 'Emission[2]', 0.8, 0, 0, 1);
         var i : Integer;
         for i:=GetGameObjectCountChildByHandle(goHnd)-1 downto 0 do
         _unit_DoConstructionBlinkGameObject(GetGameObjectGOHandleChildByHandle(goHnd, i), 0, bUseColor, bJustUpdate);
      end;
   end;
end;

// _unit_IdleGridClear
//
procedure _unit_IdleGridClear();
begin
   var i, j : Integer;
   for i:=0 to gc_idlegrid_countx-1 do
   for j:=0 to gc_idlegrid_county-1 do
   gIdleGrid[i,j] := 0;
end;

// _unit_IdleGridResetUnits
//
procedure _unit_IdleGridResetUnits();
begin
   var plHnd : Integer;
   var plInd : Integer;
   var goInd : Integer;
   var goHnd : Integer;
   for plInd:=0 to gc_MaxPlayerCount-1 do begin
      plHnd := GetPlayerHandleByIndex(plInd);
      for goInd:=GetPlayerGameObjectsCountByHandle(plHnd)-1 downto 0 do begin
         goHnd := GetGameObjectHandleByIndex(goInd, plHnd);
         if (GetGameObjectPlayableObjectByHandle(goHnd)) then begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) then
            begin
               TObj(pobj).idlegridx := -1;
               TObj(pobj).idlegridy := -1;
            end;
         end;
      end;
   end;
end;

// _unit_ScanGridClear
//
procedure _unit_ScanGridClear();
begin
   var i, j, k : Integer;
   for i:=0 to gc_scangrid_countx-1 do
   for j:=0 to gc_scangrid_county-1 do begin
      for k:=0 to gc_MaxPlayerCount-1 do
      gScanGridUnits[k,i,j].Clear;
      gScanGrid[i,j].Clear;
      for k:=0 to gc_obj_material_count-1 do
      gScanGrid[i,j].fMatCount[k] := 0;
      for k:=0 to gc_MaxPlayerCount-1 do
      gScanGrid[i,j].fPlCount[k] := 0;
      gScanGrid[i,j].fMask := 0;
      gScanGrid[i,j].fPlMask := 0;
   end;
end;

// _unit_IdleGridProgress
//
procedure _unit_IdleGridProgress(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if (TObj(pobj).standtime<=3) and (TObj(pobj).idlegridx>-1) and (TObj(pobj).idlegridy>-1) then
      _unit_IdleGridRemoveUnit(goHnd)
      else
      if (TObj(pobj).standtime>3) then
      begin
         var gridX, gridY : Integer;
         if (_misc_PosToIdleGridIndices(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), gridX, gridY)) then
         begin
            if (gridX<>TObj(pobj).idlegridx) or (gridY<>TObj(pobj).idlegridy) or (TObj(pobj).idlegridx=-1) or (TObj(pobj).idlegridy=-1) then
            begin
               if (TObj(pobj).idlegridx>-1) and (TObj(pobj).idlegridy>-1) then
               gIdleGrid[TObj(pobj).idlegridx,TObj(pobj).idlegridy] := gIdleGrid[TObj(pobj).idlegridx,TObj(pobj).idlegridy]-1;
               gidleGrid[gridX,gridY] := gidleGrid[gridX,gridY]+1;
               TObj(pobj).idlegridx := gridX;
               TObj(pobj).idlegridy := gridY;
            end;
         end;
         //else
         //ErrorLog('_unit_IdleGridProgress : position out of range');
      end;
   end;
end;

// _unit_ScanGridProgress
//
procedure _unit_ScanGridProgress(const goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);
      if (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) {and (TObj(pobj).scangridx>-1) and (TObj(pobj).scangridy>-1)} and (((statetag and gc_statetag_essential_birth)<>0) or ((statetag and gc_statetag_visual_hide)<>0)) then
      _unit_ScanGridRemoveUnit(goHnd)
      else
      begin
         var gridX, gridY : Integer;
         if (_misc_PosToScanGridIndices(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), gridX, gridY)) then
         begin
            if (gridX<>TObj(pobj).scangridx) or (gridY<>TObj(pobj).scangridy) or (TObj(pobj).scangridx=-1) or (TObj(pobj).scangridy=-1) then
            begin
               if TObj(pobj).bdead then // DEBUG
               begin
                  ErrorLog('ACHTUNG _unit_ScanGridProgress bdead unit progressed in scangrid');
                  _unit_ScanGridRemoveUnit(goHnd);
                  ErrorLog('ACHTUNG THIS IS BAD FIX. TRY TO UNDERSTAND WHAT HAPPENED BEFORE');
               end
               else
               begin
                  var pl : Integer = TObj(pobj).pl;
                  var mat : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].material;
                  var prevX : Integer = TObj(pobj).scangridx;
                  var prevY : Integer = TObj(pobj).scangridy;
                  if (prevX>-1) and (prevY>-1) then
                  begin
                     gScanGrid[prevX,prevY].Remove(goHnd, mat, pl);
                     gScanGridUnits[pl,prevX,prevY].Remove(goHnd);
                  end;
                  gScanGrid[gridX,gridY].Add(goHnd, mat, pl);
                  gScanGridUnits[pl,gridX,gridY].Add(goHnd);
                  TObj(pobj).scangridx := gridX;
                  TObj(pobj).scangridy := gridY;
               end;
            end;
         end;
         //else
         //ErrorLog('_unit_ScanGridProgress : position out of range');
      end;
   end;
end;

// _unit_ScanGridResetUnits
//
procedure _unit_ScanGridResetUnits();
begin
   var plHnd : Integer;
   var plInd : Integer;
   var goInd : Integer;
   var goHnd : Integer;
   for plInd:=0 to gc_MaxPlayerCount-1 do begin
      plHnd := GetPlayerHandleByIndex(plInd);
      for goInd:=GetPlayerGameObjectsCountByHandle(plHnd)-1 downto 0 do begin
         goHnd := GetGameObjectHandleByIndex(goInd, plHnd);
         if (GetGameObjectPlayableObjectByHandle(goHnd)) then begin
            var pobj : Pointer = _unit_GetTObj(goHnd);
            if (pobj<>nil) then begin
               TObj(pobj).scangridx := -1;
               TObj(pobj).scangridy := -1;
               _unit_ScanGridProgress(goHnd);
            end;
         end;
      end;
   end;
end;

// _unit_TopologyProgress
//
procedure _unit_TopologyProgress(const hnd: Integer);
begin
   var pobj: Pointer = _unit_GetTObj(hnd);
   if pobj<>nil then begin
      if (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) and
      ((GetGameObjectStatesTagByHandle(hnd) and gc_statetag_essential_birth)<>0) and (TObj(pobj).topzone >= 0) then
      _unit_TopologyRemove(hnd)
      else
      begin
         var posx: Float=GetGameObjectPositionXByHandle(hnd);
         var posz: Float=GetGameObjectPositionZByHandle(hnd);
         var curzone: Integer=TopologyGetZoneIndex(posx, posz);
         if curZone<>TObj(pobj).topzone then begin
            _unit_TopologyRemove(hnd);
            _unit_TopologyAdd(hnd);
         end;
      end;
   end;
end;

// _unit_ManagePeasantAttach
//
procedure _unit_ManagePeasantAttach(const goHnd : Integer; var anim : String);
begin
   if (goHnd<>0) and (_misc_GetBaseObjID(goHnd)=gc_baseid_obj) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_peasant) then
      begin
         var childHnd : Integer = GetGameObjectGOHandleChildByHandle(goHnd, 0);
         var bVisible : Boolean = True;
         var actorInd : Integer;
         case anim of
            '' : bVisible := False;
            gc_anim_walk : bVisible := False;
            gc_anim_walkfood : actorInd := gc_actor_peasant_bag;
            gc_anim_walkwood : actorInd := gc_actor_peasant_lumber;
            gc_anim_walkstone : actorInd := gc_actor_peasant_cart;

            gc_anim_idle : bVisible := False;
            gc_anim_idlefood : actorInd := gc_actor_peasant_bag;
            gc_anim_idlewood : actorInd := gc_actor_peasant_lumber;
            gc_anim_idlestone : actorInd := gc_actor_peasant_cart;

            gc_anim_workfood : actorInd := gc_actor_peasant_hoe;
            gc_anim_workwood : actorInd := gc_actor_peasant_axe;
            gc_anim_workstone : actorInd := gc_actor_peasant_pick;

            gc_anim_attack0 : actorInd := gc_actor_peasant_axe;
            gc_anim_construct : actorInd := gc_actor_peasant_hammer;
            gc_anim_death : bVisible := False;
            else
            bVisible := False;
         end;

         if (bVisible) then
         SetGameObjectActorIndexByHandle(childHnd, actorInd);
         SetGameObjectVisibleByHandle(childHnd, bVisible);
      end;
   end;
end;

// _unit_AddDecorationUnscaled
//
procedure _unit_AddDecorationUnscaled(goHnd : Integer; var decalname : String; xoff, yoff, scale, angle : Float);
begin
   if (decalname<>'') then
   begin
      var rollangle : Float = GetGameObjectRollAngleByHandle(goHnd)+angle;
      var w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps : Float;
      var texRot, texScale, texOff, texOffRot : Boolean;
      var libMat, decName : String;
      //var goScale : Float = GetGameObjectScaleXByHandle(goHnd);
      var lib, name : String = decalname;
      var decHnd : Integer = AddNewDecalByLibName(goHnd, x, z, rollangle, lib, name);
      GetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);

      var txoff : Float = xoff;
      var t : Float;
      var tyoff : Float = yoff;
      VectorRotateY(txoff, t, tyoff, rollangle);

      const texscalefix = 1.3;
      w := scale{*goScale};
      h := scale{*goScale};
      texOff0 := -txoff{*goScale}/w;
      texOff1 := -tyoff{*goScale}/h;
      texscale0 := texscale0*texscalefix;
      texscale1 := texscale1*texscalefix;
      w := w*texscalefix;
      h := h*texscalefix;
      texOff0 := -(texscalefix-1)/2;
      texOff1 := -(texscalefix-1)/2;
      SetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);
   end;
end;

// _unit_AddDecoration
//
procedure _unit_AddDecoration(goHnd : Integer; var decalname : String; xoff, yoff, scale, angle : Float);
begin
   if (decalname<>'') then
   begin
      var rollangle : Float = GetGameObjectRollAngleByHandle(goHnd)+angle;
      var w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps : Float;
      var texRot, texScale, texOff, texOffRot : Boolean;
      var libMat, decName : String;
      var goScale : Float = GetGameObjectScaleXByHandle(goHnd);
      var lib, name : String = decalname;
      var decHnd : Integer = AddNewDecalByLibName(goHnd, x, z, rollangle, lib, name);
      GetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);

      var txoff : Float = xoff;
      var t : Float;
      var tyoff : Float = yoff;
      VectorRotateY(txoff, t, tyoff, rollangle);

      const texscalefix = 1.3;
      w := scale*goScale;
      h := scale*goScale;
      texOff0 := -txoff*goScale/w;
      texOff1 := -tyoff*goScale/h;
      texscale0 := texscale0*texscalefix;
      texscale1 := texscale1*texscalefix;
      w := w*texscalefix;
      h := h*texscalefix;
      texOff0 := -(texscalefix-1)/2;
      texOff1 := -(texscalefix-1)/2;
      SetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);
   end;
end;

// _unit_DebugModifyDecoration
//
procedure _unit_DebugModifyDecoration(goHnd : Integer; scale, xoff, yoff, angle : Float);
begin
   var decHnd : Integer = GetGameObjectDecalHandleByHandle(goHnd);
   if (decHnd<>0) then
   begin
      var rollangle : Float = GetGameObjectRollAngleByHandle(goHnd)+angle;
      var w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps : Float;
      var texRot, texScale, texOff, texOffRot : Boolean;
      var libMat, decName : String;
      var goScale : Float = GetGameObjectScaleXByHandle(goHnd);
      GetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);
      var txoff : Float = xoff;
      var t : Float;
      var tyoff : Float = yoff;
      VectorRotateY(txoff, t, tyoff, rollangle);
      texAng := rollangle;

      const texscalefix = 1.3;
      w := scale*goScale;
      h := scale*goScale;
      texOff0 := -txoff*goScale/w;
      texOff1 := -tyoff*goScale/h;
      //texscale0 := texscale0*texscalefix;
      //texscale1 := texscale1*texscalefix;
      w := w*texscalefix;
      h := h*texscalefix;
      texOff0 := -(texscalefix-1)/2;
      texOff1 := -(texscalefix-1)/2;
      SetDecalDataByHandle(decHnd, w, h, x, z, texOff0, texOff1, scale0, scale1, texScale0, texScale1, texAng, eps, texRot, texScale, texOff, texOffRot, libMat, decName);
   end
   else
   begin
      var decalname : String = GetGameObjectBaseNameByHandle(goHnd)+'d';
      _unit_AddDecoration(goHnd, decalname, xoff, yoff, scale, GetGameObjectRollAngleByHandle(goHnd)+angle);
   end;
end;

// _unit_GetSquadTarget
//
function _unit_GetSquadTarget(goHnd : Integer) : Integer;
begin
   Result := 0;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if TObj(pObj).squad >= 0 then
   begin
      var plInd : Integer = TObj(pObj).pl;
      var pSquad : Pointer = gPlayer[plInd].squads.Get(TObj(pObj).squad);
      var i : Integer;
      var myX : Float = GetGameObjectPositionXByHandle(goHnd);
      var myZ : Float = GetGameObjectPositionZByHandle(goHnd);
      var minDist : Float = gc_MaxInt;
      var trgHnd : Integer;
      for i := TSquad(pSquad).GetCount-1 downto 0 do
      begin
         var goHnd1 : Integer = TSquad(pSquad).Get(i);
         var pObj1 : Pointer = _unit_GetTObj(goHnd1);
         if (TObj(pObj1).orders[0].iType = gc_obj_order_type_attackobj) and (not gObjProp[TObj(pObj1).cid][TObj(pObj1).id].bpriest) then
         begin
            var tHnd : Integer = TObj(pObj1).orders[0].info.trg;
            var trgX : Float = GetGameObjectPositionXByHandle(tHnd);
            var trgZ : Float = GetGameObjectPositionZByHandle(tHnd);
            var dist : Float = Sqr(myX-trgX) + Sqr(myZ-trgZ);
            if dist < minDist then
            begin
               minDist := dist;
               trgHnd := tHnd;
            end;
         end;
      end;

      if trgHnd <> 0 then
      begin
         _unit_OrderAttack(goHnd, trgHnd, true, false, false);
         Result := trgHnd;
      end
      else
      TSquad(pSquad).fAgressive := false;
   end;
end;

// _unit_IsRunAway
//
function _unit_IsRunAway(const goHnd : Integer) : Boolean;
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   Result := (TObj(pobj).orders[0].itype=gc_obj_order_type_move) and (TObj(pobj).orders[0].info.progress=gc_obj_order_move_mode_runaway);
end;

// _unit_DoRunAway
//
procedure _unit_DoRunAway(const goHnd : Integer; const tx, tz : Float);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var minsearchdist : Float = gObjProp[TObj(pobj).cid][TObj(pobj).id].minattackradius;
      var deltadst : Float;
      if (minsearchdist>gc_unit_runawaydist) then
      deltadst := gc_unit_runawaydist
      else
      begin
         var dst : Float = VectorDistance(GetGameObjectPositionXByHandle(goHnd), 0, GetGameObjectPositionZByHandle(goHnd), tx, 0, tz);
         deltadst := Clamp((minsearchdist-dst), gc_objectEpsilonDist*3, gc_unit_runawaydist);
      end;
      var dstx, dsty : Float;
      _misc_GetPositionOnLine(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), tx, tz, -deltadst, dstx, dsty);

      var bRemove : Boolean = _unit_IsRunAway(goHnd);
      if (bRemove) then
      _unit_RemoveOrder(goHnd, 0);

      var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);

      // check, do we need this for net
      // network
      {var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
      if parg<>nil then begin
         TPlayerArgs(parg).fposx:=grX;
         TPlayerArgs(parg).fposz:=grY;
         TPlayerArgs(parg).fdirx:=dirX;
         TPlayerArgs(parg).fdirz:=dirZ;
         TPlayerArgs(parg).faddord:=True;
         TPlayerArgs(parg).fdofirst:=False;
         TPlayerArgs(parg).frebuild:=(i=0);
         TPlayerArgs(parg).fcenter:=false;
         TPlayerArgs(parg).fmode:=mode;
         TPlayerArgs(parg).fgroup:=grHnd;
         TPlayerArgs(parg).fsquad:=squadInd;
         PlayerExecuteStateByHandle(plHnd, 'WriteMove');
      end else _misc_ErrorLog('parg = nil');}
      var hmw : Float = GetMapWidth div 2;
      var hmh : Float = GetMapHeight div 2;
      if dstx>hmw then
      dstx := hmw-0.25
      else
      if dstx<-hmw then
      dstx := -hmw+0.25;

      if dsty>hmh then
      dsty := hmh-0.25
      else
      if dsty<-hmh then
      dsty := -hmh+0.25;
      _unit_OrderMove(goHnd, dstx, dsty, 0, 0, gc_obj_order_move_mode_runaway, True);
   end;
end;

// _unit_DoSearchVictim
//
function _unit_DoSearchVictim(const goHnd : Integer; bClearOrders : Boolean) : Integer;
begin
   Result := 0;
   const cMinCaptureOrdListCount = 2;
   if (not gbool_editor_peacemode) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pObj <> nil) and _misc_IsCorrectScanCellOwner(TObj(pObj).pl, TObj(pObj).scanGridX, TObj(pObj).scanGridY) then
      begin
         var trgHnd : Integer;
         //if (TObj(pobj).bsearchenemy) then
         begin
            var bidle : Boolean = GetGameObjectTrackPointMovementModeIntByHandle(goHnd)=0;
            var minsearchdist : Float = gObjProp[TObj(pobj).cid][TObj(pobj).id].minattackradius;
            var bpriest : Boolean = gObjProp[TObj(pobj).cid][TObj(pobj).id].bpriest;
            var pobjprop : Pointer = _unit_GetObjProp(goHnd);
            var standground : Boolean = TObj(pobj).bstandground and (TObj(pobj).orders[0].itype<>gc_obj_order_type_move) and (TObj(pobj).orders[1].itype<>gc_obj_order_type_move); // shooters go and attack with standground mode wont shoot fix
            if (not bpriest) {and (_misc_GetShotPointsCount(goHnd)=0)} then
            begin
               var maxsearchdist : Float;
               if (standground) {and (TObj(pobj).standtime>0)}{(TObj(pobj).orders[0].itype<>gc_obj_order_type_move)} then  // shooters go and attack with standground mode wont shoot fix
               maxsearchdist := MinFloat(TObjProp(pobjprop).searchradius, _unit_GetMaxAttackRadius(goHnd))
               else
               maxsearchdist := minsearchdist+0.375;

               if (maxsearchdist>gc_unit_meleeattackradius) then
               begin
                  var goHeight : Float = GetGameObjectPositionYByHandle(goHnd);
                  if (goHeight<0) then goHeight := 0;
                  maxsearchdist := maxsearchdist+goHeight*2;
               end;

               if (minsearchdist<gc_unit_meleeattackradius) then
               trgHnd := _unit_SearchVictim(goHnd, minsearchdist, maxsearchdist, False, 0, 0)
               else
               //if (TObj(pobj).bstandground) and (TObj(pobj).standtime>0) and (maxsearchdist>0){(TObj(pobj).orders[0].itype<>gc_obj_order_type_move)} then
               //trgHnd := _unit_SearchVictim(goHnd, 0, maxsearchdist, False, 0, 0);
               // shooters go and attack with standground mode wont shoot fix
               if (standground) and (maxsearchdist>0) {and (TObj(pobj).orders[0].itype<>gc_obj_order_type_move)} then
               trgHnd := _unit_SearchVictim(goHnd, minsearchdist{0}, maxsearchdist, False, 0, 0);
            end;

            var tmpHnd : Integer;
            if (trgHnd=0) and (not standground) then // shooters go and attack with standground mode wont shoot fix
            tmpHnd := _unit_SearchVictimOnProgress(goHnd);

            if (tmpHnd<>0) and (not bpriest) and (not _unit_CanKill(goHnd, tmpHnd)) and (TObj(pobj).pl<gc_MaxPlayerCount-1) and (random<0.05) then
            begin
               var pobjbase : Pointer = _unit_GetObjBase(goHnd);
               var pobjprop2 : Pointer = _unit_GetObjProp(tmpHnd);
               if (TObjProp(pobjprop2).bcapture) and (not TObjProp(pobjprop2).bbuilding) and (not ((TObjProp(pobjprop).bcapture) or (TObjProp(pobjprop).media=gc_obj_media_water) or (TObjProp(pobjprop).bbuilding))) then
               begin
                  var ptrgordlist : Pointer = _misc_GetObjectArgData(tmpHnd, gc_argunit_ordlist);
                  if (ptrgordlist<>nil) and (TIntegerList(ptrgordlist).GetCount<cMinCaptureOrdListCount) then
                  begin
                     if (_unit_GetPathDistance(goHnd, tmpHnd, False)<=TObjProp(pobjprop).searchradius) then
                     begin
                        _unit_OrderMove(goHnd, GetGameObjectPositionXByHandle(tmpHnd), GetGameObjectPositionZByHandle(tmpHnd), 0, 0, gc_obj_order_move_mode_default, True);
                        _unit_SetOrderTrg(goHnd, 0, tmpHnd, True);
                     end
                     else
                     tmpHnd := 0;
                  end;
               end;
            end;

            // optimization
            var px, py, pz, dx, dy, dz, oldDst : Float;
            var bcomparedonce : Boolean;
            function FastCompareDistanceFirstNearThenLast(goHnd, goTrg1, goTrg2 : Integer) : Boolean;
            begin
               var tx, ty, tz : Float;
               if (not bcomparedonce) then
               begin
                  GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
                  GetGameObjectAbsolutePositionByHandle(goTrg2, dx, dy, dz);
                  oldDst := (Sqr(dx-px)+Sqr(dz-pz));
               end;
               GetGameObjectAbsolutePositionByHandle(goTrg1, tx, ty, tz);
               bcomparedonce := True;
               Result := (Sqr(tx-px)+Sqr(tz-pz))<oldDst;
            end;

            var oldHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
            if (tmpHnd<>0) and ((oldHnd=0) or (oldHnd=tmpHnd) or (FastCompareDistanceFirstNearThenLast(goHnd, tmpHnd, oldHnd) or (TObj(pobj).orders[0].itype=gc_obj_order_type_guard))) then
            trgHnd := tmpHnd;

            if (trgHnd<>0) then
            begin
               if (oldHnd<>trgHnd) and (_unit_CanKill(goHnd, trgHnd)) and ((oldHnd=0) or (FastCompareDistanceFirstNearThenLast(goHnd, trgHnd, oldHnd) or (TObj(pobj).orders[0].itype=gc_obj_order_type_guard))) then
               begin
                  var bSmartSearch : Boolean = (gProfile.bsearchenemyinfront) and (TObj(pobj).orders[0].itype=gc_obj_order_type_move) and (TObj(pobj).orders[0].info.progress=gc_obj_order_move_mode_default);
                  if (not bSmartSearch) then
                  begin
                     var bRemove : Boolean = (TObj(pobj).orders[0].itype=gc_obj_order_type_attackobj) and (not _unit_IsGuard(goHnd));
                     if (bRemove) then
                     _unit_RemoveOrder(goHnd, 0);
                     _unit_OrderAttack(goHnd, trgHnd, True, bClearOrders, False);
                  end
                  else
                  if (TObj(pobj).orders[0].bexecute) then
                  begin
                     var tpCount : Integer = GetGameObjectTrackPointCountByHandle(goHnd);
                     if (tpCount>0) then
                     begin
                        var tpCur : Integer = GetGameObjectTrackPointCurrentPointIndexByHandle(goHnd);
                        var tpX, tpY, tpZ : Float;
                        GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, tpCur, tpX, tpY, tpZ);
                        const cMinAngle = 30;
                        var dirx : Float = tpx-GetGameObjectPositionXByHandle(goHnd);
                        var dirz : Float = tpz-GetGameObjectPositionZByHandle(goHnd);
                        var dirx2 : Float = GetGameObjectPositionXByHandle(trgHnd)-GetGameObjectPositionXByHandle(goHnd);
                        var dirz2 : Float = GetGameObjectPositionZByHandle(trgHnd)-GetGameObjectPositionZByHandle(goHnd);
                        var angle : Float = VectorAngle(dirx, 0, dirz, dirx2, 0, dirz2);
                        if (angle<cMinAngle) then
                        _unit_OrderAttack(goHnd, trgHnd, True, False, False);
                     end;
                  end;
               end;
            end
            else
            if (minsearchdist>0) then
            begin
               trgHnd := _unit_SearchVictim(goHnd, 0, minsearchdist, False, 0, 0);
               if (trgHnd<>0) and (not TObj(pobj).bstandground) and ((TObj(pobj).standtime=0) or (TObj(pobj).standtime>gc_unit_runawaydelay) or ((not gPlayer[TObj(pobj).pl].bai) and (gPlayer[TObj(pobj).pl].difficulty<=gc_player_difficulty_normal))) then // standtime checked to make shooters less annoying when chasing
               _unit_DoRunAway(goHnd, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd));
            end;
         end;
         Result := trgHnd;
      end;
   end;
end;

// _unit_GetCurrentOrder
//
function _unit_GetCurrentOrder(goHnd : Integer) : Integer;
begin
   // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   // This code is duplicated in unit.aix, DoProgressOrders!
   // Don't forget to change it there when change function!
   // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   if (gPlayer[plInd].bAI) then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      var pObjProp : Pointer = gObjProp[TObj(pObj).cid][TObj(pObj).id];

      if (pObj<>nil) and TObjProp(pObjProp).bArtillery and (TObj(pObj).attackDelay <> 0) and
      (TObj(pObj).orders[1].iType <> gc_obj_order_type_none) and ((TObj(pObj).orders[0].iType = gc_obj_order_type_attackobj) or (TObj(pObj).orders[0].iType = gc_obj_order_type_attackpoint)) then
      Result := 1
      else
      Result := 0;
   end
   else
   Result := 0;
end;

// _unit_EnoughResForShot
//
function _unit_EnoughResForShot(goHnd, weapon : Integer) : Boolean;
begin
   Result := false;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   begin
      var pObjProp : Pointer = gObjProp[TObj(pObj).cid][TObj(pObj).id];
      if pObjProp <> nil then
      begin
         Result := true;
         var plInd : Integer = TObj(pObj).pl;
         var i : Integer;
         for i := 0 to gc_ResCount-1 do
         begin
            if TObjProp(pObjProp).weapon[weapon].cost[i] > 0 then
            if gPlayer[plInd].res[i] < TObjProp(pObjProp).weapon[weapon].cost[i] then
            begin
               Result := false;
               break;
            end;
         end;
      end;
   end;
end;

// _unit_ApplyWeaponCost
//
procedure _unit_ApplyWeaponCost(goHnd, weapon : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if pObj <> nil then
   begin
      var pObjProp : Pointer = gObjProp[TObj(pObj).cid][TObj(pObj).id];
      if pObjProp <> nil then
      begin
         var plInd : Integer = TObj(pObj).pl;
         var i : Integer;
         for i := 0 to gc_ResCount-1 do
         if TObjProp(pObjProp).weapon[weapon].cost[i] > 0 then
         begin
            if gPlayer[plInd].res[i] > TObjProp(pObjProp).weapon[weapon].cost[i] then
            _res_AddResToPlayerByIndex(plInd, i, -TObjProp(pobjprop).weapon[weapon].cost[i])
            else
            _res_AddResToPlayerByIndex(plInd, i, -gPlayer[plInd].res[i]);
         end;
      end;
   end;
end;

// _unit_NeedTurnToTrg
//
function _unit_NeedTurnToTrg(const goHnd, trgHnd : Integer) : Boolean;
begin
   var DirX, DirY, DirZ : Float;
   var DirX1, DirZ1 : Float;
   var pobjprop : Pointer = _unit_GetObjProp(goHnd);
   if (trgHnd<>0) and ((pobjprop=nil) or (not TObjProp(pobjprop).bshotdirection)) then
   begin
      dirX := GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
      dirZ := GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
      dirX1 := GetGameObjectPositionXByHandle(trgHnd)-GetGameObjectPositionXByHandle(goHnd);
      dirZ1 := GetGameObjectPositionZByHandle(trgHnd)-GetGameObjectPositionZByHandle(goHnd);
   end
   else
   begin
      var angle : Float = GetGameObjectSTArrowAngleByHandle(goHnd);
      DirX := 1;
      DirY := 0;
      DirZ := 0;
      VectorRotateY(dirX, dirY, dirZ, angle);

      DirX1 := GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
      DirZ1 := GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
   end;
   var angle : Float = VectorAngle(DirX1, 0, DirZ1, dirX, 0, dirZ);
   Result := (angle>GetGameObjectEpsilonAngleByHandle(goHnd));
end;

// _unit_NeedTurn
//
function _unit_NeedTurn(const goHnd : Integer) : Boolean;
begin
   var DirX, DirY, DirZ : Float;
   var DirX1, DirZ1 : Float;
   var pobjprop : Pointer = _unit_GetObjProp(goHnd);
   var stoHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   if (stoHnd<>0) and ((pobjprop=nil) or (not TObjProp(pobjprop).bshotdirection)) then
   begin
      dirX := GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
      dirZ := GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
      dirX1 := GetGameObjectPositionXByHandle(stoHnd)-GetGameObjectPositionXByHandle(goHnd);
      dirZ1 := GetGameObjectPositionZByHandle(stoHnd)-GetGameObjectPositionZByHandle(goHnd);
   end
   else
   begin
      var angle : Float = GetGameObjectSTArrowAngleByHandle(goHnd);
      DirX := 1;
      DirY := 0;
      DirZ := 0;
      VectorRotateY(dirX, dirY, dirZ, angle);

      DirX1 := GetGameObjectTransformedVirtualDirectionXByHandle(goHnd);
      DirZ1 := GetGameObjectTransformedVirtualDirectionZByHandle(goHnd);
   end;
   var angle : Float = VectorAngle(DirX1, 0, DirZ1, dirX, 0, dirZ);
   Result := (angle>GetGameObjectEpsilonAngleByHandle(goHnd));
end;

// _unit_TryAttackPoint
//
function _unit_TryAttackPoint(const goHnd : Integer; const tx, tz : Float; const bOnAnimationControl : Boolean) : Integer;
begin
   var res : Integer = 2;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) and (not TObj(pobj).bdead) then
   begin
      var curOrder : Integer = _unit_GetCurrentOrder(goHnd);
      if (TObj(pobj).bsearchenemy) then
      begin
         var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);
         var bAttacking : Boolean = ((statetag and gc_statetag_action_attack)<>0);
         if (bAttacking) then res := 0;
         if (bOnAnimationControl) or (not bAttacking) or (_unit_IsWaterUnit(goHnd)) then
         begin
            var weapon : Integer = _unit_GetWeaponToAttackIndexByPoint(goHnd, tx, tz);

            var movetag : Integer;
            var weapontag : Integer;
            var actiontag : Integer;
            var executetag : Integer;
            var visualtag : Integer;
            var wtag : Integer;
            if (weapon>-1) then
            begin
               case weapon of
                  0 : wtag := gc_statetag_weapon_0;
                  1 : wtag := gc_statetag_weapon_1;
                  2 : wtag := gc_statetag_weapon_2;
               end;

               if (GetGameObjectStateTargetPositionXByHandle(goHnd)<>tx) or (GetGameObjectStateTargetPositionZByHandle(goHnd)<>tz) then
               _unit_SetSTP(goHnd, tx, tz);

               var px, py, pz : Float;
               GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
               var dstHeight : Float;
               if (not RayCastWater(tx, tz, dstHeight)) then // is water, then ty is water height
               dstHeight := RayCastHeight(tx, tz);
               var bonusrng : Float = MaxFloat((py-dstHeight)*2, 0);
               var rbonus : Float;
               var dist : Float = VectorDistance(px, 0, pz, tx, 0, tz);

               var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
               var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];

               var standtime : Float = TObj(pobj).standtime;
               var radiusmax : Float = TObjBase(pobjbase).weapon[weapon].radiusmax;
               if (radiusmax>gc_unit_meleeattackradius) then
               begin
                  rbonus := bonusrng;
                  if (standtime<0.25) then
                  radiusmax := radiusmax-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd;
               end;

               var bDistanceFar : Boolean = (dist>(radiusmax+rbonus));
               var bDistanceOk : Boolean = (not bDistanceFar) and (dist>=TObjProp(pobjprop).weapon[weapon].radiusmin);
               var bStandGround : Boolean = TObj(pobj).bstandground and (standtime>0);
               if (not bDistanceOk) then
               begin
                  if (not bStandGround) then
                  begin
                     res := 1;
                     if (bDistanceFar) then
                     begin
                        var tpx, tpy, tpz : Float;
                        var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
                        var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
                        var tpCount : Integer = GetGameObjectTrackPointCountByHandle(goHnd);
                        if tpCount > 0 then
                        GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, tpCount-1, tpx, tpy, tpz)
                        else
                        begin
                           tpX := stX;
                           tpZ := stZ;
                        end;

                        if (bAttacking) then
                        actiontag := gc_statetag_action_none;

                        if (VectorDistance(tpx, 0, tpz, stx, 0, stz)>gc_objectEpsilonDist*0.5) or
                        (((statetag and gc_statetag_move_idle)<>0) and ((statetag and gc_statetag_execute_none)<>0)) then
                        begin
                           actiontag := gc_statetag_action_none;
                           weapontag := wtag;
                           executetag := gc_statetag_execute_move;
                        end;
                     end
                     else
                     //if (bDistanceTooClose) then
                     _unit_DoRunAway(goHnd, tx, tz);
                  end
                  else
                  begin
                     res := 2;
                     movetag := gc_statetag_move_idle;
                     actiontag := gc_statetag_action_none;
                     executetag := gc_statetag_execute_none;

                     //_unit_ClearOrders(goHnd);
                     _unit_RemoveOrder(goHnd, curOrder);
                  end;
               end
               else
               begin
                  res := 0;
                  var angle : Float = _misc_GetAngleToXVector(px, pz, tx, tz);
                  if (TObjProp(pobjprop).bshotdirection) then
                  begin
                     var sides : Integer;
                     if (TObjProp(pobjprop).bshotforward) then
                     sides := sides+1;
                     if (TObjProp(pobjprop).bshotleftflank) then
                     sides := sides+1;
                     if (TObjProp(pobjprop).bshotrightflank) then
                     sides := sides+1;
                     if (TObjProp(pobjprop).bshotback) then
                     sides := sides+1;
                     if (sides>0) then
                     begin
                        var anglestep : Float = (360/sides);
                        var upx, upy, upz, dirx, diry, dirz : Float;
                        GetGameObjectOrientationByHandle(goHnd, upx, upy, upz, dirx, diry, dirz);
                        dirx := tx-px;
                        dirz := tz-pz;
                        var vecangle : Float = _misc_VectorAngle(upx, upz, dirx, dirz);

                        if (TObjProp(pobjprop).bshotleftflank) and (vecangle>=180-anglestep) then
                        angle := angle+90
                        else
                        if (TObjProp(pobjprop).bshotrightflank) and (vecangle<=180-anglestep) then
                        angle := angle-90
                        else
                        if (TObjProp(pobjprop).bshotback) and (vecangle>-anglestep/2) and (vecangle<anglestep/2) then
                        angle := angle+180;
                     end;
                  end;
                  SetGameObjectSTArrowAngleByHandle(goHnd, angle);
                  var bTurn : Boolean = _unit_NeedTurn(goHnd);
                  var bAttackPauseOk : Boolean = (TObj(pobj).attackdelay=0);

                  var bEnoughRes : Boolean = _unit_EnoughResForShot(goHnd, weapon);
                  if (not bEnoughRes) and (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=TObj(pobj).pl) then
                  begin
                     var s1, s2 : String;
                     var sid : String;
                     if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                     _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                     else
                     sid := GetGameObjectBaseNameByHandle(goHnd);
                     _misc_GetUnitNameBySID(sid, s1);
                     if (s1<>'') then
                     _misc_ShowLogMessage(gc_logmessagesid_cantshootnocoaloriron, s1, s2, False);
                  end;

                  if (bTurn) then
                  begin
                     res := 1;
                     movetag := gc_statetag_move_turn;
                     weapontag := wtag;
                     actiontag := gc_statetag_action_none;
                  end
                  else
                  if (not bAttackPauseOk) or (not bEnoughRes) then
                  begin
                     if (_unit_IsWaterUnit(goHnd)) then
                     actiontag := gc_statetag_action_none; //else moved to animationreachedend
                     if ((statetag and gc_statetag_move_idle)=0) and ((statetag and gc_statetag_execute_move)=0) then
                     movetag := gc_statetag_move_idle;
                  end
                  else
                  if ((statetag and gc_statetag_action_attack)=0) then
                  begin
                     var FriendOnLine : Integer;
                     if (TObjProp(pobjprop).weapon[weapon].weaponid=0) or (not gWeapons[TObjProp(pobjprop).weapon[weapon].weaponid].bcheckfriendonline) then
                     FriendOnLine := 0
                     else
                     FriendOnLine := _misc_IsBuildingInRay(goHnd, px, py+0.5, pz, tx, dstHeight+0.5, tz);
                     if (FriendOnLine<>0) then
                     begin
                        if _unit_IsBuilding(FriendOnLine) then // either it is ship
                        begin
                           res := 2;
                           _unit_RemoveOrder(goHnd, curOrder);
                        end;
                        movetag := gc_statetag_move_idle;
                        actiontag := gc_statetag_action_none;
                        executetag := gc_statetag_execute_none;
                        TObj(pobj).attackdelay := MaxFloat(TObj(pobj).attackdelay, 1);
                        TObj(pobj).attackmaxdelay := MaxFloat(TObj(pobj).attackmaxdelay, 1);

                        if (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=TObj(pobj).pl) then
                        begin
                           var s1, s2 : String;
                           var sid : String;
                           if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                           _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                           else
                           sid := GetGameObjectBaseNameByHandle(goHnd);
                           _misc_GetUnitNameBySID(sid, s1);

                           var pobjtrg : Pointer = _unit_GetTObj(FriendOnLine);
                           if (gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id].bwall) then
                           _unit_ConvertObjIDToSID(TObj(pobjtrg).cid, TObj(pobjtrg).id, sid)
                           else
                           sid := GetGameObjectBaseNameByHandle(FriendOnLine);
                           _misc_GetUnitNameBySID(sid, s2);
                           if (s1<>'') then
                           _misc_ShowLogMessage(gc_logmessagesid_obstaclepreventshot, s1, s2, False);
                        end;
                     end
                     else
                     begin
                        res := 0;
                        movetag := gc_statetag_move_idle;
                        weapontag := wtag;
                        actiontag := gc_statetag_action_attack;
                        if (TObj(pobj).attackmaxdelay>0) then
                        visualtag := gc_statetag_visual_stage_0
                        else
                        visualtag := gc_statetag_visual_none;
                     end;
                  end;
               end;
            end
            else
            begin
               res := 2;
               movetag := gc_statetag_move_idle;
               actiontag := gc_statetag_action_none;
               executetag := gc_statetag_execute_none;

               //_unit_ClearOrders(goHnd);
               _unit_RemoveOrder(goHnd, curOrder);
            end;
            if (movetag<>0) or (actiontag<>0) or (executetag<>0) or (weapontag<>0) or (visualtag<>0) then
            _unit_SetTagStates(goHnd, movetag or weapontag or actiontag or executetag or visualtag);
         end;
      end
      else
      begin
         if (TObj(pobj).orders[curOrder].itype=gc_obj_order_type_attackpoint) then
         begin
            _unit_RemoveOrder(goHnd, curOrder);
            _unit_SetTagStates(goHnd, gc_statetag_action_none);
         end;
      end;
   end;
   Result := res;
end;

// _unit_TryAttack
//
function _unit_TryAttack(goHnd, trgHnd : Integer; const bOnAnimationControl : Boolean) : Integer;
begin
   var newSTO : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   var bSetSTP : Boolean;
   var stpx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
   var stpz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);

   function FindSTP(goHnd, trgHnd, weapon, curorder, ordertype : Integer) : Boolean;
   begin
      var pObjBase : Pointer = _unit_GetObjBase(goHnd);
      var pObjProp : Pointer = _unit_GetObjProp(goHnd);
      var myReg : Integer = _unit_GetRegion(goHnd);
      var curOrder : Integer = _unit_GetCurrentOrder(goHnd);
      var tx, ty, tz : Float;
      GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz);

      var allow : Boolean = (TObjProp(pObjProp).media = gc_obj_media_water) or _unit_IsArtillery(goHnd) or (_unit_IsWaterUnit(trgHnd));
      var rad : Integer;
      if allow then
      rad := -1
      else
      rad := floor(TObjBase(pObjBase).weapon[weapon].radiusMax)+6;

      var trgX, trgZ : Float;
      Result := TopologyGetPositionInRegion(tx, tz, myReg, rad, trgX, trgZ);
      if Result and allow then
      begin
         var attRad : Float = TObjBase(pObjBase).weapon[weapon].radiusMax;
         if VectorDistance(tx, 0, tz, trgX, 0, trgZ) > attRad then
         Result := false;
      end;

      if Result then
      begin
         bSetSTP := True;
         stpx := trgX;
         stpz := trgZ;
         //_unit_SetSTP(goHnd, trgX, trgZ)
      end
      else
      begin
         if ordertype=gc_obj_order_type_attackobj then
         _unit_RemoveOrder(goHnd, curOrder);
         _unit_SetSTO(goHnd, 0); // set STO directly, cause there is exit after.
         //newSTO := 0;
      end;
   end;
   //var profHnd : Integer = _misc_ProfilerBeginHnd('tryat');
   var res : Integer = gc_result_tryattack_targetnotvalid;
   var curOrder : Integer = _unit_GetCurrentOrder(goHnd);
   var ordertype : Integer;
   var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);
   var bAttacking : Boolean = ((statetag and gc_statetag_action_attack)<>0);
   var bLockTarget : Boolean;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (TObj(pobj).bpathrequested) {and (trgHnd<>0)} then
   begin
      Result := gc_result_tryattack_skipped;
      exit;
   end;

   if (pobj<>nil) {and (TObj(pobj).bsearchenemy)} and (trgHnd<>0) {and (_misc_GetBaseObjID(trgHnd)=gc_baseid_obj)} then
   begin
      if (not TObj(pobj).bdead) then
      begin
         var pobj2 : Pointer = _unit_GetTObj(trgHnd);
         if (pobj2<>nil) and (not TObj(pobj2).bdead) then
         begin
            if (bAttacking) then res := gc_result_tryattack_ok;
            if (bOnAnimationControl) or (not bAttacking) or (gObjProp[TObj(pobj).cid][TObj(pobj).id].explmedia=gc_obj_explmedia_ship) then
            begin
               bLockTarget := (TObj(pobj).orders[curOrder].info.progress=gc_obj_order_attack_mode_locktarget);
               ordertype := TObj(pobj).orders[curOrder].itype;
               var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
               var pTrgObjProp : Pointer = gObjProp[TObj(pobj2).cid][TObj(pobj2).id];
               var pl : Integer = TObj(pobj).pl;
               if (not TObjProp(pobjprop).bpriest) or (((gPlayer[pl].enemyPlMask) and (gPlayer[TObj(pobj2).pl].myplmask)=0) and (TObj(pobj2).hp<gPlayer[TObj(pobj2).pl].objbase[TObj(pobj2).cid][TObj(pobj2).id].maxhp)) then
               begin
                  var pobjbase : Pointer = gPlayer[pl].objbase[TObj(pobj).cid][TObj(pobj).id];

                  var px, py, pz, tx, ty, tz : Float;
                  GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
                  GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz);
                  var dist : Float = VectorDistance(px, 0, pz, tx, 0, tz);
                  var myReg : Integer = _misc_GetRegionByPos(px, pz);

                  var weapon : Integer = -1;
                  var nearestWeaponDist : Float = gc_MaxInt;
                  var AttMaskInd : Integer = -1;
                  var bonusrng : Float;
                  if (TObjProp(pobjprop).bArtillery) then
                  bonusrng := MaxFloat((py-ty)*4, 0)
                  else
                  bonusrng := MaxFloat((py-ty)*2, 0);
                  if (bonusrng<0.25) then bonusrng := 0;
                  var rbonus : Float;
                  var i : Integer;
                  for i:=0 to gc_obj_MaxWeapon-1 do
                  if (TObjProp(pobjprop).weapon[i].enabled) and (TObjProp(pobjprop).weapon[i].attmask<>0) then
                  begin
                     AttMaskInd := i;
                     if ((TObjProp(pobjprop).weapon[i].attmask and gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask)<>0) then
                     begin
                        if (TObjBase(pobjbase).weapon[attMaskInd].radiusmax>gc_unit_meleeattackradius) then
                        rbonus := bonusrng
                        else
                        rbonus := 0;
                        if (dist>=TObjProp(pobjprop).weapon[attMaskInd].detectradiusmin) and (dist<=(TObjProp(pobjprop).weapon[attMaskInd].detectradiusmax+rbonus)) then
                        begin
                           var tmpNearestWeaponDist : Float = _misc_MinFloat(abs(TObjProp(pobjprop).weapon[attMaskInd].radiusmin-dist), abs((TObjBase(pobjbase).weapon[attMaskInd].radiusmax+rbonus)-dist));
                           if (tmpNearestWeaponDist<nearestWeaponDist) then
                           begin
                              weapon := attMaskInd;
                              nearestWeaponDist := tmpNearestWeaponDist;
                           end;
                        end;
                     end;
                     break;
                  end;

                  var tmpNearestWeaponDist : Float;
                  var bCanTarget : Boolean = ((TObjProp(pobjprop).kmask and gObjProp[TObj(pobj2).cid][TObj(pobj2).id].mmask)<>0);
                  if (bCanTarget) then
                  for i:=0 to gc_obj_MaxWeapon-1 do
                  if (TObjProp(pobjprop).weapon[i].enabled) and (i<>AttMaskInd) then
                  begin
                     if (TObjBase(pobjbase).weapon[i].radiusmax>gc_unit_meleeattackradius) then
                     rbonus := bonusrng
                     else
                     rbonus := 0;
                     if (dist>=TObjProp(pobjprop).weapon[i].detectradiusmin) and (dist<=(TObjProp(pobjprop).weapon[i].detectradiusmax+rbonus)) then
                     begin
                        if (dist<TObjProp(pobjprop).weapon[i].radiusmin) then
                        tmpNearestWeaponDist := TObjProp(pobjprop).weapon[i].radiusmin-dist
                        else
                        if (dist>TObjBase(pobjbase).weapon[i].radiusmax+rbonus) then
                        tmpNearestWeaponDist := dist-(TObjBase(pobjbase).weapon[i].radiusmax+rbonus)
                        else
                        begin
                           weapon := i;
                           break;
                        end;
                        if (tmpNearestWeaponDist<nearestWeaponDist) then
                        begin
                           weapon := i;
                           nearestWeaponDist := tmpNearestWeaponDist;
                        end
                     end;
                  end;
                  if (weapon>-1) then
                  begin
                     var movetag, weapontag, actiontag, executetag, visualtag, wtag : Integer;
                     case weapon of
                        0 : wtag := gc_statetag_weapon_0;
                        1 : wtag := gc_statetag_weapon_1;
                        2 : wtag := gc_statetag_weapon_2;
                     end;

                     var bNewSTO : Boolean = (newSTO<>trgHnd);
                     var bNewSTP : Boolean = bNewSTO;
                     if (bNewSTO) then
                     begin
                        newSTO := trgHnd;
                        //_unit_SetSTO(goHnd, trgHnd);

                        if (myReg < 0) or (_misc_GetRegionByPos(tx, tz) = myReg) then
                        begin
                           bSetSTP := True;
                           stpx := tx;
                           stpz := tz;
                        end
                        else
                        begin
                           var modified : Boolean;

                           if TObjProp(pTrgObjProp).bBuilding then
                           begin
                              var tcid : Integer = TObj(pObj2).cid;
                              var tid : Integer = TObj(pObj2).id;
                              var bCount : Integer;
                              var isWall : Boolean = TObjProp(pTrgObjProp).bWall;
                              var variation : Integer = TObj(pObj2).wallVariation;
                              if isWall then
                              bCount := gCustomBuildPointsWall[variation].builderCount
                              else
                              bCount := gCustomObjPoints[tcid, tid].builderCount;

                              var ii : Integer;
                              var minDist : Float = gc_MaxFloat;
                              var trgX, trgZ : Float;
                              for ii := 0 to bCount-1 do
                              begin
                                 var bx, bz : Float;
                                 if isWall then
                                 begin
                                    bx := gCustomBuildPointsWall[variation].builderPoints[ii].x;
                                    bz := gCustomBuildPointsWall[variation].builderPoints[ii].y;
                                 end
                                 else
                                 begin
                                    bx := gCustomObjPoints[tcid, tid].builderPoints[ii].x;
                                    bz := gCustomObjPoints[tcid, tid].builderPoints[ii].y;
                                 end;

                                 bx := bx + tx;
                                 bz := bz + tz;

                                 if _misc_GetRegionByPos(bx, bz) = myReg then
                                 begin
                                    var dist : Float = VectorDistance(px, 0, pz, bx, 0, bz);
                                    if dist < minDist then
                                    begin
                                       trgX := bx;
                                       trgZ := bz;
                                       minDist := dist;
                                    end;
                                 end;
                              end;

                              if minDist < gc_MaxFloat then
                              begin
                                 bSetSTP := True;
                                 stpx := trgX;
                                 stpz := trgZ;
                                 //SetGameObjectStateTargetPositionByHandle(goHnd, trgX, 0, trgZ);
                                 modified := true;
                              end;
                           end;

                           if not modified then
                           begin
                              if not FindSTP(goHnd, trgHnd, weapon, curorder, ordertype) then
                              exit;
                           end;
                        end;
                     end;

                     var trgmovedst : Float = -1;
                     if (not bNewSTP) then
                     begin
                        //trgmovedst := VectorDistance(GetGameObjectStateTargetPositionXByHandle(goHnd), 0, GetGameObjectStateTargetPositionZByHandle(goHnd), tx, 0, tz);
                        trgmovedst := VectorDistance(stpx, 0, stpz, tx, 0, tz);
                        if (trgmovedst>gc_objectEpsilonDist) then
                        bNewSTP := True;
                     end;
                     var standtime : Float = TObj(pobj).standtime;
                     var radiusmax : Float = TObjBase(pobjbase).weapon[weapon].radiusmax;
                     var melee : Boolean = radiusmax<=gc_unit_meleeattackradius;
                     if (not melee) then
                     begin
                        rbonus := bonusrng;
                        if (TObjProp(pobjprop).weapon[weapon].kind<>gc_obj_weapon_kind_cannister) and ((standtime<0.25) and (not gPlayer[pl].bai or (TObjProp(pobjprop).usage<>gc_obj_usage_supermortar))) then
                        begin
                           if not TObjProp(pobjprop).bArtillery then
                           radiusmax := radiusmax-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd
                           else
                           radiusmax := radiusmax-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd*0.5;
                        end;
                     end;
                     //if (bAttacking) then
                     var bIdle : Boolean = ((statetag and gc_statetag_move_idle)<>0);
                     if (bIdle) then
                     rbonus := rbonus+TObjProp(pobjprop).weapon[weapon].addradius;

                     if (TObjProp(pTrgObjProp).usage=gc_obj_usage_weakwall) then
                     rbonus := rbonus+0.36;

                     var bDistanceFar : Boolean = (dist>(radiusmax+rbonus));
                     var bDistanceOk : Boolean = (not bDistanceFar) and (dist>=TObjProp(pobjprop).weapon[weapon].radiusmin);
                     var bStandGround : Boolean = TObj(pobj).bstandground {and TObj(pobj).standtime>0} and (TObj(pobj).orders[0].itype<>gc_obj_order_type_move) and (TObj(pobj).orders[1].itype<>gc_obj_order_type_move); // shooters go and attack with standground mode wont shoot fix

                     //bStandGround := (TObj(pobj).bstandground) and (standtime>0);
                     if ((not bNewSTO) and (not bStandGround)) then
                     begin
                        if (not bDistanceOk) then
                        begin
                           var bChangeSTP : Boolean;
                           if not TObjProp(pTrgObjProp).bBuilding then
                           begin
                              if (bIdle) then
                              bChangeSTP := True
                              else
                              begin
                                 // start optimization : recalculate path only if target moved far enough to annoy. Larger dist, less executes.
                                 if (trgmovedst<0) then
                                 trgmovedst := VectorDistance(stpx, 0, stpz, tx, 0, tz);
                                 //trgmovedst := VectorDistance(GetGameObjectStateTargetPositionXByHandle(goHnd), 0, GetGameObjectStateTargetPositionZByHandle(goHnd), tx, 0, tz);
                                 if (trgmovedst>gc_objectEpsilonDist) then
                                 begin
                                    var allowDst : Float;
                                    if (dist>10) then allowDst := dist/5 else if (dist>5) then allowDst := dist/4 else if (dist>3) then allowDst := dist/7 else allowDst := gc_objectEpsilonDist*2;
                                    if (allowDst>4) then allowDst := 4;
                                    // finish optimization

                                    if (trgmovedst>allowDst) then
                                    bChangeSTP := True;
                                 end;
                              end;
                              if (bChangeSTP) and (not TObj(pobj).bstandground) then // shooters go and attack with standground mode wont shoot fix
                              begin
                                 if not FindSTP(goHnd, trgHnd, weapon, curorder, ordertype) then
                                 exit;
                              end;
                           end;
                        end;
                     end;

                     if (not bDistanceOk) then
                     begin
                        if (not bStandGround) then
                        begin
                           res := gc_result_tryattack_outofrange;
                           if (bDistanceFar) then
                           begin
                              var searchradius : Float = TObjProp(pobjprop).searchradius;
                              // todo : 170419 check if helps
                              //if (TObj(pobj).standtime<0.25) then
                              //searchradius := searchradius-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd; // improve shooters search enemy
      
                              if (TObjProp(pobjprop).minattackradius<gc_unit_meleeattackradius) and (TObj(pobj).orders[1].itype=gc_obj_order_type_guard) then
                              searchradius := MinFloat(searchradius, gc_gameplay_meleeguardmaxsearchdist*1.5);

                              var allowChaseTarget : Boolean = (dist<=searchradius) or (bLockTarget);
                              var trgMovedDist : Float = -1;
                              if (not allowChaseTarget) and (not bNewSTO) then
                              begin
                                 var tpx, tpy, tpz : Float;
                                 //var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
                                 //var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
                                 GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, GetGameObjectTrackPointCountByHandle(goHnd)-1, tpx, tpy, tpz);
                                 //trgMovedDist := VectorDistance(tpx, 0, tpz, stx, 0, stz);
                                 trgMovedDist := VectorDistance(tpx, 0, tpz, stpx, 0, stpz);
                              end;
                              if (not allowChaseTarget) and ((bNewSTO) or (trgMovedDist<=gc_objectEpsilonDist*0.5)) then
                              allowChaseTarget := True;

                              if (not allowChaseTarget) then
                              begin
                                 var squad : Integer = TObj(pObj).squad;
                                 if (squad>-1) then
                                 begin
                                    var pSquad : Pointer = gPlayer[pl].squads.Get(TObj(pObj).squad);
                                    allowChaseTarget := TSquad(pSquad).fAgressive;
                                 end;
                                 res := gc_result_tryattack_targetnotvalid;
                                 movetag := gc_statetag_move_idle;
                                 actiontag := gc_statetag_action_none;
                                 executetag := gc_statetag_execute_none;

                                 //_unit_ClearOrders(goHnd);
                                 if ordertype=gc_obj_order_type_attackobj then
                                 _unit_RemoveOrder(goHnd, curOrder);
                              end
                              else
                              begin
                                 if (bAttacking) then
                                 actiontag := gc_statetag_action_none;

                                 var bMove : Boolean;
                                 if (bNewSTO) or (((bIdle) and ((statetag and gc_statetag_execute_none)<>0))) then
                                 bMove := True
                                 else
                                 begin
                                    if (trgMovedDist<0) then
                                    begin
                                       var tpx, tpy, tpz : Float;
                                       //var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
                                       //var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
                                       GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, GetGameObjectTrackPointCountByHandle(goHnd)-1, tpx, tpy, tpz);
                                       //trgMovedDist := VectorDistance(tpx, 0, tpz, stx, 0, stz);
                                       trgMovedDist := VectorDistance(tpx, 0, tpz, stpx, 0, stpz);
                                    end;
                                    if (trgMovedDist>gc_objectEpsilonDist*0.5) then
                                    bMove := True;
                                 end;
                                 if (bMove) then
                                 begin
                                    //log('move');
                                    actiontag := gc_statetag_action_none;
                                    weapontag := wtag;
                                    executetag := gc_statetag_execute_move;
                                 end;
                              end;
                           end
                           else
                           // shooters go and attack with standground mode wont shoot fix
                           if (not bStandGround) and {not TObj(pobj).bstandground and} ((standtime=0) or (standtime>gc_unit_runawaydelay)) then // make shooters less annoying when chasing with melee
                           _unit_DoRunAway(goHnd, tx, tz)
                           else
                           if bStandGround{(TObj(pobj).bstandground)} then
                           begin
                              res := gc_result_tryattack_targetnotvalid;
                              movetag := gc_statetag_move_idle;
                              actiontag := gc_statetag_action_none;
                              executetag := gc_statetag_execute_none;

                              if ordertype=gc_obj_order_type_attackobj then
                              _unit_RemoveOrder(goHnd, curOrder);
                           end;
                        end
                        else
                        begin
                           res := gc_result_tryattack_targetnotvalid;
                           movetag := gc_statetag_move_idle;
                           actiontag := gc_statetag_action_none;
                           executetag := gc_statetag_execute_none;

                           //_unit_ClearOrders(goHnd);
                           if ordertype=gc_obj_order_type_attackobj then
                           _unit_RemoveOrder(goHnd, curOrder);
                        end;
                     end
                     else
                     begin
                        res := gc_result_tryattack_ok;
                        var bAttackPauseOk : Boolean = melee or TObj(pobj).attackdelay=0;
                        var bEnoughRes : Boolean = True;
                        if (bAttackPauseOk) then
                        begin
                           if (TObjProp(pobjprop).bturnoff) and (gPlayer[pl].res[gc_resource_type_gold]<4) then
                           begin
                              TObj(pobj).attackmaxdelay := MaxFloat(TObj(pobj).attackmaxdelay, 100*gc_frames_to_time);
                              TObj(pobj).attackdelay := MaxFloat(TObj(pobj).attackdelay, 100*gc_frames_to_time);
                              bEnoughRes := False;
                              if (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=pl) then
                              begin
                                 var s1, s2 : String;
                                 var sid : String;
                                 if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                                 _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                                 else
                                 sid := GetGameObjectBaseNameByHandle(goHnd);
                                 _misc_GetUnitNameBySID(sid, s1);
                                 if (s1<>'') then
                                 _misc_ShowLogMessage(gc_logmessagesid_cantshootnogold, s1, s2, False);
                              end;
                           end
                           else
                           begin
                              bEnoughRes := _unit_EnoughResForShot(goHnd, weapon);
                              if (not bEnoughRes) then
                              begin
                                 TObj(pobj).attackmaxdelay := MaxFloat(TObj(pobj).attackmaxdelay, 64*gc_frames_to_time);
                                 TObj(pobj).attackdelay := MaxFloat(TObj(pobj).attackdelay, 64*gc_frames_to_time);
                                 if (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=pl) then
                                 begin
                                    var s1, s2 : String;
                                    var sid : String;
                                    if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                                    _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                                    else
                                    sid := GetGameObjectBaseNameByHandle(goHnd);
                                    _misc_GetUnitNameBySID(sid, s1);
                                    if (s1<>'') then
                                    _misc_ShowLogMessage(gc_logmessagesid_cantshootnocoaloriron, s1, s2, False);
                                 end;
                              end;
                           end;
                        end;

                        if (TObjProp(pobjprop).bshotdirection) then
                        begin
                           var angle : Float = _misc_GetAngleToXVector(px, pz, tx, tz);
                           var sides : Integer;
                           if (TObjProp(pobjprop).bshotforward) then
                           sides := sides+1;
                           if (TObjProp(pobjprop).bshotleftflank) then
                           sides := sides+1;
                           if (TObjProp(pobjprop).bshotrightflank) then
                           sides := sides+1;
                           if (TObjProp(pobjprop).bshotback) then
                           sides := sides+1;
                           if (sides>0) then
                           begin
                              var anglestep : Float = (360/sides);
                              var upx, upy, upz, dirx, diry, dirz : Float;
                              GetGameObjectOrientationByHandle(goHnd, upx, upy, upz, dirx, diry, dirz);
                              dirx := tx-px;
                              dirz := tz-pz;
                              var vecangle : Float = _misc_VectorAngle(upx, upz, dirx, dirz);

                              if (TObjProp(pobjprop).bshotleftflank) and (vecangle>=180-anglestep) then
                              angle := angle+90
                              else
                              if (TObjProp(pobjprop).bshotrightflank) and (vecangle<=180-anglestep) then
                              angle := angle-90
                              else
                              if (TObjProp(pobjprop).bshotback) and (vecangle>-anglestep/2) and (vecangle<anglestep/2) then
                              angle := angle+180;
                           end;
                           if Abs(angle-GetGameObjectSTArrowAngleByHandle(goHnd))>GetGameObjectEpsilonAngleByHandle(goHnd) then
                           SetGameObjectSTArrowAngleByHandle(goHnd, angle);
                        end;

                        var bTurn : Boolean = ((statetag and gc_statetag_move_turn)<>0) or (((bNewSTP) or (TObjProp(pobjprop).bshotdirection)) and (_unit_NeedTurnToTrg(goHnd, newSTO)){(_unit_NeedTurn(goHnd))});
                        if (bTurn) or ((bAttackPauseOk and bEnoughRes and ((statetag and gc_statetag_action_attack)=0))) then
                        begin
                           var FriendOnLine : Integer;
                           if (TObjProp(pobjprop).weapon[weapon].weaponid=0) or (not gWeapons[TObjProp(pobjprop).weapon[weapon].weaponid].bcheckfriendonline) then
                           FriendOnLine := 0
                           else
                           FriendOnLine := _misc_IsBuildingInRay(goHnd, px, py+0.5, pz, tx, ty+0.5, tz);
                           if (FriendOnLine<>0)
                           or ((TObjProp(pobjprop).weapon[weapon].kind=gc_obj_weapon_kind_cannister) and ((not bLockTarget) and (not _unit_CheckFriendlyFireCannister(goHnd, not bTurn, tx, tz))))
                           or ((TObjProp(pobjprop).weapon[weapon].kind=gc_obj_weapon_kind_cannonball) and ((not bLockTarget) and (not _unit_CheckFriendlyFireExplosion(goHnd, tx, tz)))) then
                           begin
                              res := gc_result_tryattack_targetnotvalid;
                              movetag := gc_statetag_move_idle;
                              actiontag := gc_statetag_action_none;
                              executetag := gc_statetag_execute_none;
                              if ordertype=gc_obj_order_type_attackobj then
                              _unit_RemoveOrder(goHnd, curOrder);
                              newSTO := 0;
                              //_unit_SetSTO(goHnd, 0);
                              TObj(pobj).attackdelay := MaxFloat(TObj(pobj).attackdelay, 1);
                              TObj(pobj).attackmaxdelay := MaxFloat(TObj(pobj).attackmaxdelay, 1);

                              if (FriendOnLine<>0) and (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=TObj(pobj).pl) then
                              begin
                                 var s1, s2 : String;
                                 var sid : String;
                                 if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                                 _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                                 else
                                 sid := GetGameObjectBaseNameByHandle(goHnd);
                                 _misc_GetUnitNameBySID(sid, s1);

                                 var pobjtrg : Pointer = _unit_GetTObj(FriendOnLine);
                                 if (gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id].bwall) then
                                 _unit_ConvertObjIDToSID(TObj(pobjtrg).cid, TObj(pobjtrg).id, sid)
                                 else
                                 sid := GetGameObjectBaseNameByHandle(FriendOnLine);
                                 _misc_GetUnitNameBySID(sid, s2);
                                 if (s1<>'') then
                                 _misc_ShowLogMessage(gc_logmessagesid_obstaclepreventshot, s1, s2, False);
                              end;
                           end
                        end;
                        if (res<gc_result_tryattack_targetnotvalid) then
                        begin
                           if (bTurn) then
                           begin
                              res := gc_result_tryattack_outofrange;
                              movetag := gc_statetag_move_turn;
                              weapontag := wtag;
                              actiontag := gc_statetag_action_none;

                              if (not TObjProp(pobjprop).bshotdirection) then
                              begin
                                 var angle : Float = _misc_GetAngleToXVector(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(newSTO), GetGameObjectPositionZByHandle(newSTO));
                                 if (Abs(GetGameObjectSTArrowAngleByHandle(goHnd)-angle)>GetGameObjectEpsilonAngleByHandle(goHnd)) then
                                 SetGameObjectSTArrowAngleByHandle(goHnd, angle);
                              end;
                           end
                           else
                           if (not bAttackPauseOk) or (not bEnoughRes) then
                           begin
                              actiontag := gc_statetag_action_none;

                              if (not gPlayer[pl].bAI) or (not _unit_IsArtillery(goHnd)) then
                              begin
                                 if (not bIdle) and ((statetag and gc_statetag_execute_move)=0) then
                                 movetag := gc_statetag_move_idle;
                              end;
                           end
                           else
                           if ((statetag and gc_statetag_action_attack)=0) then
                           begin
                              res := gc_result_tryattack_ok;
                              movetag := gc_statetag_move_idle;
                              weapontag := wtag;
                              actiontag := gc_statetag_action_attack;
                              bSetSTP := True;
                              stpx := GetGameObjectPositionXByHandle(goHnd);
                              stpz := GetGameObjectPositionZByHandle(goHnd);

                              if (TObj(pobj).attackmaxdelay>0) then
                              visualtag := gc_statetag_visual_stage_0
                              else
                              visualtag := gc_statetag_visual_none;
                              //log('OnAclAnimationReachedAttackEnd : actiontag = attack movetag = idle');
                           end
                           else
                           if (bAttackPauseOk) and ((statetag and gc_statetag_visual_stage_0)<>0) then
                           begin
                              actiontag := gc_statetag_action_none;
                              if (not bIdle) and ((statetag and gc_statetag_execute_move)=0) then
                              movetag := gc_statetag_move_idle;
                              visualtag := gc_statetag_visual_none;
                           end;
                        end;
                     end;
                     if (movetag<>0) or (actiontag<>0) or (executetag<>0) or (weapontag<>0) or (visualtag<>0) then
                     _unit_SetTagStates(goHnd, movetag or weapontag or actiontag or executetag or visualtag);
                  end
                  else
                  begin
                     res := gc_result_tryattack_targetnotvalid;
                     if ordertype=gc_obj_order_type_attackobj then
                     _unit_RemoveOrder(goHnd, curOrder);
                     _unit_SetTagStates(goHnd, gc_statetag_move_idle or gc_statetag_action_none or gc_statetag_execute_none);
                  end;
               end
               else
               begin
                  res := gc_result_tryattack_targetnotvalid;
                  if ordertype=gc_obj_order_type_attackobj then
                  _unit_RemoveOrder(goHnd, curOrder);
                  _unit_SetTagStates(goHnd, gc_statetag_move_idle or gc_statetag_action_none or gc_statetag_execute_none);
               end;
            end;
         end;
      end;
   end;
   if ((res=gc_result_tryattack_ok) and (newSTO=0)) then
   begin
      // fix units that freeze in action_attack but already finished animation
      res := gc_result_tryattack_targetnotvalid;
      bAttacking := False;
   end;

   if (bOnAnimationControl) or (not bAttacking) then
   begin
      if (res=gc_result_tryattack_targetnotvalid) then
      begin
         //_unit_SetSTO(goHnd, 0);
         if (not TObj(pobj).bsearchenemy) then
         begin
            if (ordertype=gc_obj_order_type_attackobj) then
            _unit_RemoveOrder(goHnd, curOrder);
            _unit_SetTagStates(goHnd, gc_statetag_execute_none or gc_statetag_move_idle or gc_statetag_action_none);
         end
         else
         begin
            var newHnd : Integer;
            if (pobj<>nil) and (not TObj(pobj).bdead) then
            begin
               if ((statetag and gc_statetag_move_walk)<>0) and ((not ((TObj(pobj).orders[0].itype=gc_obj_order_type_move) or (TObj(pobj).orders[0].info.progress<>gc_obj_order_move_mode_attack)))) then
               begin
                  newHnd := _unit_SearchVictimOnProgress(goHnd);
                  if (newHnd<>0) then
                  begin
                     //_unit_RemoveOrder(goHnd, curOrder); // remove order twice, when set res:=2 and here. this is not needed.
                     _unit_OrderAttack(goHnd, newHnd, False, False, False);
                  end;
               end;
               if (newHnd=0) and (trgHnd<>0) and (bLockTarget) and (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bartillery) then
               begin
                  var tx, ty, tz : Float;
                  GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz);
                  if (TObj(pobj).bstandground) then
                  begin
                     var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
                     var maxsearchdist : Float = MinFloat(TObjProp(pobjprop).searchradius, _unit_GetMaxAttackRadius(goHnd));
                     // todo : 170419 check if helps
                     //if (TObj(pobj).standtime<0.25) then
                     //maxsearchdist := maxsearchdist-gc_obj_maxattackradiusdisp*TObj(pobj).uniqrnd; // improve shooters search enemy
                              
                     newHnd := _unit_SearchVictim(goHnd, 0, maxsearchdist, False, 0, 0);
                  end
                  else
                  newHnd := _unit_SearchVictim(goHnd, 0, gc_unit_searchvictimattargetdist, True, tx, tz);
                  if (newHnd<>0) then
                  begin
                     //_unit_RemoveOrder(goHnd, curOrder); // remove order twice, when set res:=2 and here. this is not needed.
                     //_unit_OrderAttack(goHnd, newHnd, True, False, True);
                     _unit_OrderAttack(goHnd, newHnd, True, False, False);
                  end;
               end;
               //else
               if (newHnd=0) and ((statetag and gc_statetag_action_none)=0) then
               _unit_SetTagStates(goHnd, gc_statetag_action_none);
            end;
         end;
      end;
   end;

   if (res=gc_result_tryattack_targetnotvalid) then
   newSTO := 0;
   //else
   if (bSetSTP) then
   _unit_SetSTP(goHnd, stpx, stpz);
   _unit_SetSTO(goHnd, newSTO);

   Result := res;
   //_misc_ProfilerEndHnd(profHnd);
end;

// _unit_BuildingTryAttack
//
function _unit_BuildingTryAttack(const goHnd, trgHnd : Integer; bpoint : Boolean; tx, tz : Float) : Integer;
begin
   var res : Integer = 2;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) and (not TObj(pobj).bdead) and ((trgHnd<>0) or bpoint) then
   begin
      var bOk : Boolean = bpoint;
      if (not bOk) then
      begin
         var pobj2 : Pointer = _unit_GetTObj(trgHnd);
         bOk := (pobj2<>nil) and (not TObj(pobj2).bdead)
      end;
      if (bOk) and (TObj(pobj).bsearchenemy) then
      begin
         var weapon : Integer;
         if (not bpoint) then
         weapon := _unit_GetWeaponToAttackIndex(goHnd, trgHnd)
         else
         weapon := _unit_GetWeaponToAttackIndexByPoint(goHnd, tx, tz);
         if (weapon>-1) then
         begin
            var weapontag : Integer;
            var actiontag : Integer;
            var wtag : Integer;
            case weapon of
               0 : wtag := gc_statetag_weapon_0;
               1 : wtag := gc_statetag_weapon_1;
               2 : wtag := gc_statetag_weapon_2;
            end;

            var bNewSTO : Boolean = (GetGameObjectSTOHandleByHandle(goHnd)<>trgHnd);
            if (bNewSTO) then
            _unit_SetSTO(goHnd, trgHnd);

            var px, py, pz, ty : Float;
            GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
            if (not bpoint) then
            GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz)
            else
            if (not RayCastWater(tx, tz, ty)) then // is water, then ty is water height
            ty := RayCastHeight(tx, tz);
            var bonusrng : Float = (py-ty)*3;
            var rbonus : Float;
            var dst : Float = VectorDistance(px, 0, pz, tx, 0, tz);

            var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
            if (TObjBase(pobjbase).weapon[weapon].radiusmax>gc_unit_meleeattackradius) then
            rbonus := bonusrng
            else
            rbonus := 0;
            var bDistanceTooClose : Boolean = (dst<gObjProp[TObj(pobj).cid][TObj(pobj).id].weapon[weapon].radiusmin);
            var bDistanceFar : Boolean = (dst>(TObjBase(pobjbase).weapon[weapon].radiusmax+rbonus));
            var bDistanceOk : Boolean = (not bDistanceTooClose) and (not bDistanceFar);

            if (not bDistanceOk) then
            begin
               res := 2;
               actiontag := gc_statetag_action_none;
               _unit_ClearOrders(goHnd);
            end
            else
            begin
               res := 0;
               var bAttackPauseOk : Boolean = (TObj(pobj).attackdelay=0);
               actiontag := gc_statetag_action_none;
               if (bAttackPauseOk) and ((_unit_GetTagStateByType(goHnd, gc_statetag_action) and gc_statetag_action_attack)=0) then
               begin
                  var pobjprop : Pointer = _unit_GetObjProp(goHnd);
                  var bEnoughRes : Boolean = _unit_EnoughResForShot(goHnd, weapon);
                  if bEnoughRes then
                  begin
                     var FriendOnLine : Integer;
                     if (pobjprop<>nil) and (TObjProp(pobjprop).weapon[weapon].weaponid=0) or (not gWeapons[TObjProp(pobjprop).weapon[weapon].weaponid].bcheckfriendonline) then
                     FriendOnLine := 0
                     else
                     FriendOnLine := _misc_IsBuildingInRay(goHnd, px, py+0.5, pz, tx, ty+0.5, tz);
                     if (FriendOnLine<>0)
                     or ((TObjProp(pobjprop).weapon[weapon].kind=gc_obj_weapon_kind_cannonball) and (((TObj(pobj).orders[0].itype<>gc_obj_order_type_attackpoint) and (TObj(pobj).orders[0].info.progress<>gc_obj_order_attack_mode_locktarget)) and (not _unit_CheckFriendlyFireExplosion(goHnd, tx, tz)))) then
                     begin
                        res := 2;
                        TObj(pobj).attackdelay := MaxFloat(TObj(pobj).attackdelay, 1);
                        TObj(pobj).attackmaxdelay := MaxFloat(TObj(pobj).attackmaxdelay, 1);
                        if (FriendOnLine<>0) and (GetPlayerIndexByHandle(GetPlayerHandleInterfaceIO)=TObj(pobj).pl) then
                        begin
                           var s1, s2 : String;
                           var sid : String;
                           if (gObjProp[TObj(pobj).cid][TObj(pobj).id].bwall) then
                           _unit_ConvertObjIDToSID(TObj(pobj).cid, TObj(pobj).id, sid)
                           else
                           sid := GetGameObjectBaseNameByHandle(goHnd);
                           _misc_GetUnitNameBySID(sid, s1);
                           var pobjtrg : Pointer = _unit_GetTObj(FriendOnLine);
                           if (gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id].bwall) then
                           _unit_ConvertObjIDToSID(TObj(pobjtrg).cid, TObj(pobjtrg).id, sid)
                           else
                           sid := GetGameObjectBaseNameByHandle(FriendOnLine);
                           _misc_GetUnitNameBySID(sid, s2);
                           if (s1<>'') then
                           _misc_ShowLogMessage(gc_logmessagesid_obstaclepreventshot, s1, s2, False);
                        end;
                     end
                     else
                     begin
                        _unit_ApplyWeaponCost(goHnd, weapon);
                        res := 0;
                        weapontag := wtag;
                        actiontag := gc_statetag_action_attack;
                     end;
                  end;
               end;
            end;
            if (actiontag<>0) or (weapontag<>0) then
            _unit_SetTagStates(goHnd, weapontag or actiontag);
         end
         else
         res := 2;
      end;
   end;
   if (res=2) then
   begin
      _unit_SetSTO(goHnd, 0);
      _unit_RemoveOrder(goHnd, 0);
      if (_unit_GetTagStateByType(goHnd, gc_statetag_action)<>gc_statetag_action_none) then
      _unit_SetTagStates(goHnd, gc_statetag_action_none);
   end;
   Result := res;
end;

// _unit_GetAlignedPosition
//
procedure _unit_GetAlignedPosition(const goHnd : Integer; var posX, posZ : Float);
begin
   var cols : Integer = GetGameObjectCollisionMaskWidthByHandle(goHnd);
   var rows : Integer = GetGameObjectCollisionMaskHeightByHandle(goHnd);

   posX := GetGameObjectPositionXByHandle(goHnd);
   posZ := GetGameObjectPositionZByHandle(goHnd);
   if cols mod 2 = 1 then
   begin
      if GetGameObjectCollisionMaskReflectByHandle(goHnd) then
      posX := Floor(posX*2)/2-0.25
      else
      posX := Floor(posX*2)/2+0.25;
   end
   else
   posX := Floor(posX*2)/2;

   if rows mod 2 = 1 then
   posZ := Floor(posZ*2)/2+0.25
   else
   posZ := Floor(posZ*2)/2;
end;

// _unit_GetFreeBuilderPoint
//
function _unit_GetFreeBuilderPoint(const goHnd, buildHnd : Integer; var resX, resY : Float) : Boolean;
begin
   Result := false;
   if _unit_IsBuilding(buildHnd) then
   begin
      var goX : Float = GetGameObjectPositionXByHandle(goHnd);
      var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
      var region : Integer = _misc_GetRegionByPos(goX, goZ);

      var pObj : Pointer = _unit_GetTObj(buildHnd);
      var id : Integer = TObj(pObj).id;
      var cid : Integer = TObj(pObj).cid;
      var pSTOList : Pointer = _misc_GetObjectArgData(buildHnd, gc_argunit_stolist);
      var trgX, trgZ : Float;
      _unit_GetAlignedPosition(buildHnd, trgX, trgZ);
      var i, j : Integer;
      var minDist : Float = -1;

      var bwall : Boolean = gObjProp[cid][id].bwall;
      var variation : Integer = TObj(pObj).wallvariation;
      var bCount : Integer;
      if (bwall) then
      bCount := gCustomBuildPointsWall[variation].builderCount
      else
      bCount := gCustomObjPoints[cid, id].builderCount;
      for i := 0 to bCount-1 do
      begin
         var free : Boolean = true;

         var bx, bz : Float;
         if (bwall) then
         begin
            bx := gCustomBuildPointsWall[variation].builderPoints[i].x;
            bz := gCustomBuildPointsWall[variation].builderPoints[i].y;
         end
         else
         begin
            bx := gCustomObjPoints[cid, id].builderPoints[i].x;
            bz := gCustomObjPoints[cid, id].builderPoints[i].y;
         end;

         var buildX : Float = trgX+bx;
         var buildZ : Float = trgZ+bz;
         if _misc_GetRegionByPos(buildX, buildZ) = region then
         begin
            for j := TIntegerList(pSTOList).GetCount-1 downto 0 do
            begin
               var goHnd1 : Integer = TIntegerList(pSTOList).Get(j);
               if (GetGameObjectStatesTagByHandle(goHnd1) and gc_statetag_action_build)<>0 then
               begin
                  var x : Float = GetGameObjectStateTargetPositionXByHandle(goHnd1);
                  var z : Float = GetGameObjectStateTargetPositionZByHandle(goHnd1);
                  if VectorDistance(x, 0, z, buildX, 0, buildZ) < GetGameObjectEpsilonDistanceByHandle(goHnd) then
                  begin
                     free := false;
                     break;
                  end;
               end;
            end;

            if free then
            begin
               var dist : Float = VectorDistance(goX, 0, goZ, buildX, 0, buildZ);
               if (minDist < 0) or (dist < minDist) then
               begin
                  resX := trgX+bx;
                  resY := trgZ+bz;
                  minDist := dist;
                  Result := true;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_AddBuilderPoint
//
procedure _unit_AddBuilderPoint(cid, id : Integer; x, y : Float);
begin
   var bCount : Integer = gCustomObjPoints[cid, id].builderCount;
   if bCount < gc_MaxBuilderCount then
   begin
      gCustomObjPoints[cid, id].builderPoints[bCount].x := x;
      gCustomObjPoints[cid, id].builderPoints[bCount].y := y;
      gCustomObjPoints[cid, id].builderCount := bCount+1;
   end;
end;

// _unit_CalcBuilderPoints
//
procedure _unit_CalcBuilderPoints(buildHnd : Integer; dist : Float);
begin
   if _unit_IsBuilding(buildHnd) then
   begin
      var pObj : Pointer = _unit_GetTObj(buildHnd);
      var cid : Integer = TObj(pObj).cid;
      var id : Integer = TObj(pObj).id;

      if gCustomObjPoints[cid, id].builderCount = 0 then
      begin
         var cellSize : Float = 0.5;

         var cols : Integer = GetGameObjectCollisionMaskWidthByHandle(buildHnd);
         var rows : Integer = GetGameObjectCollisionMaskHeightByHandle(buildHnd);

         var goX, goZ : Float;
         _unit_GetAlignedPosition(buildHnd, goX, goZ);

         var i, j, k : Integer;
         var curRow, curCol : Integer = -1;
         for [MAIN]i := 0 to rows-1 do
         for j := 0 to cols-1 do
         if GetGameObjectCollisionMaskValueByHandle(buildHnd, i, j) then
         begin
            curRow := i;
            curCol := j;
            break(MAIN);
         end;

         if (curRow >= 0) and (curCol >= 0) then
         begin
            var stRow : Integer = curRow;
            var stCol : Integer = curCol;
            var stX : Float = curCol;
            var stY : Float = curRow;
            var curX : Float = curCol+1;
            var curY : Float = curRow;
            var stDirX : Integer = -1;
            var stDirY : Integer;
            var dirX : Integer = stDirX;
            var dirY : Integer = stDirY;
            var counter : Integer;

            var dLen : Float;

            repeat
               counter := counter+1;
               var x : Float = (curX-cols/2)/2;
               var z : Float = (curY-rows/2)/2;
               var len : Float = cellSize;

               while dLen+len >= dist do
               begin
                  len := len-(dist-dLen);
                  var pX : Float = x + dirX*(cellSize-len);
                  var pZ : Float = z + dirY*(cellSize-len);
                  _unit_AddBuilderPoint(cid, id, pX, pZ);
                  dLen := 0;
               end;

               dLen := dLen+len;

               curX := curX + dirX;
               curY := curY + dirY;

               if (dirX = -1) and (dirY = 0) then
               begin
                  if curY = curRow then
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol-1) then
                     begin
                        curRow := curRow-1;
                        curCol := curCol-1;
                        dirX := 0;
                        dirY := -1;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow, curCol-1) then
                        begin
                           curRow := curRow;
                           curCol := curCol-1;
                           dirX := -1;
                           dirY := 0;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 0;
                           dirY := 1;
                        end;
                     end;
                  end
                  else
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol-1) then
                     begin
                        curRow := curRow+1;
                        curCol := curCol-1;
                        dirX := 0;
                        dirY := 1;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow, curCol-1) then
                        begin
                           curRow := curRow;
                           curCol := curCol-1;
                           dirX := -1;
                           dirY := 0;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 0;
                           dirY := -1;
                        end;
                     end;
                  end;
               end
               else
               if (dirX = 1) and (dirY = 0) then
               begin
                  if curY = curRow then
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol+1) then
                     begin
                        curRow := curRow-1;
                        curCol := curCol+1;
                        dirX := 0;
                        dirY := -1;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow, curCol+1) then
                        begin
                           curRow := curRow;
                           curCol := curCol+1;
                           dirX := 1;
                           dirY := 0;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 0;
                           dirY := 1;
                        end;
                     end;
                  end
                  else
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol+1) then
                     begin
                        curRow := curRow+1;
                        curCol := curCol+1;
                        dirX := 0;
                        dirY := 1;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow, curCol+1) then
                        begin
                           curRow := curRow;
                           curCol := curCol+1;
                           dirX := 1;
                           dirY := 0;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 0;
                           dirY := -1;
                        end;
                     end;
                  end;
               end
               else
               if (dirX = 0) and (dirY = -1) then
               begin
                  if curX = curCol then
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol-1) then
                     begin
                        curRow := curRow-1;
                        curCol := curCol-1;
                        dirX := -1;
                        dirY := 0;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol) then
                        begin
                           curRow := curRow-1;
                           curCol := curCol;
                           dirX := 0;
                           dirY := -1;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 1;
                           dirY := 0;
                        end;
                     end;
                  end
                  else
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol+1) then
                     begin
                        curRow := curRow-1;
                        curCol := curCol+1;
                        dirX := 1;
                        dirY := 0;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow-1, curCol) then
                        begin
                           curRow := curRow-1;
                           curCol := curCol;
                           dirX := 0;
                           dirY := -1;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := -1;
                           dirY := 0;
                        end;
                     end;
                  end;
               end
               else
               if (dirX = 0) and (dirY = 1) then
               begin
                  if curX = curCol then
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol-1) then
                     begin
                        curRow := curRow+1;
                        curCol := curCol-1;
                        dirX := -1;
                        dirY := 0;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol) then
                        begin
                           curRow := curRow+1;
                           curCol := curCol;
                           dirX := 0;
                           dirY := 1;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := 1;
                           dirY := 0;
                        end;
                     end;
                  end
                  else
                  begin
                     if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol+1) then
                     begin
                        curRow := curRow+1;
                        curCol := curCol+1;
                        dirX := 1;
                        dirY := 0;
                     end
                     else
                     begin
                        if GetGameObjectCollisionMaskValueByHandle(buildHnd, curRow+1, curCol) then
                        begin
                           curRow := curRow+1;
                           curCol := curCol;
                           dirX := 0;
                           dirY := 1;
                        end
                        else
                        begin
                           curRow := curRow;
                           curCol := curCol;
                           dirX := -1;
                           dirY := 0;
                        end;
                     end;
                  end;
               end;
            until (curRow = stRow) and (curCol = stCol) and (dirX = stDirX) and (dirY = stDirY);

            if dLen > dist/2 then
            begin
               var x : Float = (curX-cols/2)/2;
               var z : Float = (curY-rows/2)/2;
               _unit_AddBuilderPoint(cid, id, x, z);
            end;

            for i := 0 to gCustomObjPoints[cid, id].builderCount-1 do
            begin
               var x : Float = gCustomObjPoints[cid, id].builderPoints[i].x;
               var z : Float = gCustomObjPoints[cid, id].builderPoints[i].y;
               // check for collision
               for [MAIN]j := 0 to 1 do
               for k := 0 to 1 do
               begin
                  var delta : Float = 0.001;
                  var dx : Float = delta*(2*j-1);
                  var dz : Float = delta*(2*k-1);

                  if GetMapCollisionTag(x+goX+dx, z+goZ+dz, true) = 0 then
                  begin
                     gCustomObjPoints[cid, id].builderPoints[i].x := x+dx;
                     gCustomObjPoints[cid, id].builderPoints[i].y := z+dz;
                     break(MAIN);
                  end;
               end;
            end;
         end
         else
         begin
            const rad = 2.5;
            const count = 20;
            const deltaangle = 360/count;
            for i:=0 to count-1 do
            begin
               var px : Float = rad;
               var py, pz : Float;
               VectorRotateY(px, py, pz, i*deltaangle);
               _unit_AddBuilderPoint(cid, id, px, pz);
            end;
         end;

         for i := 0 to gCustomObjPoints[cid, id].builderCount-1 do
         begin
            var dirX : Float = gCustomObjPoints[cid, id].builderPoints[i].x;
            var dirZ : Float = gCustomObjPoints[cid, id].builderPoints[i].y;
            var norm : Float = VectorLength(dirX, 0, dirZ);
            if norm > 0 then
            begin
               dirX := dirX/norm * gc_BuilderShift;
               dirZ := dirZ/norm * gc_BuilderShift;

               gCustomObjPoints[cid, id].builderPoints[i].x := gCustomObjPoints[cid, id].builderPoints[i].x + dirX;
               gCustomObjPoints[cid, id].builderPoints[i].y := gCustomObjPoints[cid, id].builderPoints[i].y + dirZ;
            end;
         end;
      end;
   end;
end;

// _unit_AIAddToBuildProjects
//
procedure _unit_AIAddToBuildProjects(plInd, goHnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var index : Integer = -1;
      var i: Integer;
      for i:=0 to gc_ai_max_construction_sites-1 do
      begin
         if (not gPlayer[plind].aidata.buildprojects[i].used) then
         begin
            index := i;
            break;
         end;
      end;
      if (index>=0) then
      begin
         var cid : Integer = TObj(pobj).cid;
         var id : Integer = TObj(pobj).id;
         var pbuildproject : Pointer = gPlayer[plind].aidata.buildprojects[index];
         TAIBuildingProject(pbuildproject).gohnd := goHnd;
         TAIBuildingProject(pbuildproject).used := true;
         TAIBuildingProject(pbuildproject).sid := gObjProp[TObj(pobj).cid, TObj(pobj).id].sid;
         TAIBuildingProject(pbuildproject).cid := cid;
         TAIBuildingProject(pbuildproject).id := id;
         TAIBuildingProject(pbuildproject).placefound := True;
         var pordlist : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_ordlist);
         if (pordlist<>nil) then
         TAIBuildingProject(pbuildproject).npeasantscalled := TIntegerList(pordlist).GetCount;

         if (gCustomObjPoints[cid, id].builderCount=0) then
         _unit_CalcBuilderPoints(goHnd, gc_BuilderDist);
         var bCount : Integer = gCustomObjPoints[cid, id].builderCount;
         if (gObjProp[cid][id].usage=gc_obj_usage_mine) then
         begin
            gPlayer[plind].aidata.buildprojects[index].minpeasants := 5;
            gPlayer[plind].aidata.buildprojects[index].maxpeasants := 5;
         end
         else
         if (gObjProp[cid][id].usage=gc_obj_usage_farm) then
         begin
            gPlayer[plind].aidata.buildprojects[index].minpeasants := 2;
            gPlayer[plind].aidata.buildprojects[index].maxpeasants := 2;
         end
         else
         begin
            gPlayer[plind].aidata.buildprojects[index].minpeasants := bcount div 2;
            var btime : Integer = Round(gPlayer[plind].objbase[cid][id].buildtime*gc_time_to_frames/gc_buildtime_modifier);

            if (btime <= 100) then
            bCount := bCount div 2
            else
            if (btime < 1000) then
            bCount := bCount*2 div 3
            else
            if (btime < 2000) then
            bCount := bCount*3 div 4;
            gPlayer[plind].aidata.buildprojects[index].maxpeasants := bCount;
         end;

         TAIBuildingProject(pbuildproject).peasantscalled := (TAIBuildingProject(pbuildproject).npeasantscalled>=gPlayer[plind].aidata.buildprojects[index].maxpeasants);
         gPlayer[plind].aidata.unbuildhouses.Add(goHnd);
      end;
   end;
end;

// _unit_GetNextWallForWork
//
function _unit_GetNextWallForWork(var objList : TIntegerList; const curHnd : Integer) : Integer;
begin
   Result := 0;

   gRegionsList.Clear;
   var i : Integer;
   for i := objList.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = objList.Get(i);
      var region : Integer = _unit_GetRegion(goHnd);
      if gRegionsList.IndexOf(region) < 0 then
      gRegionsList.Add(region);
   end;

   var pObj : Pointer = _unit_GetTObj(curHnd);
   var bCount : Integer = gCustomBuildPointsWall[TObj(pObj).wallvariation].builderCount;
   if (bCount=0) then
   ErrorLog('_unit_GetNextWallForWork : builderCount=0');

   function TestWallToConstruct(const wallHnd : Integer) : Boolean;
   begin
      Result := False;
      var pObj : Pointer = _unit_GetTObj(wallHnd);
      if (pObj<>nil) then
      begin
         var maxhp : Integer = gPlayer[TObj(pObj).pl].objbase[TObj(pObj).cid][TObj(pObj).id].maxhp;
         if (not TObj(pObj).bbuilt) or (maxhp < TObj(pObj).hp) then
         begin
            var pordlist : Pointer = _misc_GetObjectArgData(wallHnd, gc_argunit_ordlist);
            var wallFree : Boolean;
            var i, j : Integer;
            var wallX : Float = GetGameObjectPositionXByHandle(wallHnd);
            var wallZ : Float = GetGameObjectPositionZByHandle(wallHnd);
            var bCount : Integer = gCustomBuildPointsWall[TObj(pObj).wallvariation].builderCount;
            for i := 0 to bCount-1 do
            begin
               var bx : Float = gCustomBuildPointsWall[TObj(pObj).wallvariation].builderPoints[i].x;
               var bz : Float = gCustomBuildPointsWall[TObj(pObj).wallvariation].builderPoints[i].y;
               var region : Integer = _misc_GetRegionByPos(wallX+bx, wallZ+bz);
               if gRegionsList.IndexOf(region) >= 0 then
               begin
                  var pointFree : Boolean = true;
                  for j := TIntegerList(pOrdList).GetCount-1 downto 0 do
                  begin
                     var goHnd : Integer = TIntegerList(pOrdList).Get(j);
                     var pObj1 : Pointer = _unit_GetTObj(goHnd);
                     var order : Integer = TObj(pObj1).orders[0].itype;
                     var ordHnd : Integer = TObj(pObj1).orders[0].info.trg;
                     if ordHnd = wallHnd then
                     if order = gc_obj_order_type_build then
                     begin
                        var x : Float = TObj(pObj1).orders[0].info.x;
                        var z : Float = TObj(pObj1).orders[0].info.y;

                        if (x = wallX+bx) and (z = wallZ+bz) then
                        begin
                           pointFree := false;
                           break;
                        end;
                     end;
                  end;

                  if pointFree then
                  begin
                     wallFree := true;
                     break;
                  end;
               end;
            end;

            Result := wallFree;
         end;
      end;
   end;

   var pCluster : Pointer;
   var index : Integer = gWallSystem.GetWallClusterByHandle(TObj(pObj).pl, curHnd, pCluster);
   if (index<>-1) then
   begin
      var i : Integer;
      var count : Integer = TWallCluster(pCluster).Cells.GetCount;
      for i := 0 to count-1 do
      begin
         var ind : Integer;
         if i mod 2 = 0 then
         ind := index + i div 2
         else
         ind := index - (i+1) div 2;

         if ind < 0 then
         ind := ind+count
         else
         if ind >= count then
         ind := ind-count;

         var pCell : Pointer = TWallCluster(pCluster).Cells.GetPointer(ind);
         var objHnd : Integer = TWallCell(pCell).goHnd;
         if (objHnd<>0) and (objHnd<>curHnd) and (TestWallToConstruct(objHnd)) then
         begin
            Result := objHnd;
            break;
         end;
      end;
   end;

   //lets try some area search:
   if (Result=0) then
   begin
      const rad : Float = 10;
      var goX : Float = GetGameObjectPositionXByHandle(curHnd);
      var goZ : Float = GetGameObjectPositionZByHandle(curHnd);

      _misc_MakeListObjectsInRadius(goX, goZ, rad, true, (1 shl gc_obj_material_building) or (1 shl gc_obj_material_woodwall), 0, True, gIntegerList);
      var bestDist : Float = gc_MaxInt;
      var i : Integer;
      for i := 0 to gIntegerList.GetCount-1 do
      begin
         var trgHnd : Integer = gIntegerList.Get(i);
         var pObj : Pointer = _unit_GetTObj(trgHnd);
         if (pObj<>nil) then
         begin
            var usage : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].usage;
            if ((usage=gc_obj_usage_hardwall) or (usage=gc_obj_usage_weakwall)) and (TestWallToConstruct(trgHnd)) then
            begin
               var posx : Float = GetGameObjectPositionXByHandle(trgHnd);
               var posz : Float = GetGameObjectPositionZByHandle(trgHnd);
               var dist : Float = VectorDistance(goX, 0, goZ, posx, 0 posz);
               if dist<bestDist then
               begin
                  bestDist := dist;
                  Result := trgHnd;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_OrderBuild
//
function _unit_OrderBuild(var list : TIntegerList; const goHnd, trgHnd : Integer; const bRepair, bClearOrders : Boolean) : Boolean;
begin
   Result := false;
   if (trgHnd<>0) then
   begin
      var pTrgObj : Pointer = _unit_GetTObj(trgHnd);
      var cid : Integer = TObj(pTrgObj).cid;
      var id : Integer = TObj(pTrgObj).id;
      var myRegion : Integer = _unit_GetRegion(goHnd);

      var bwall : Boolean = gObjProp[cid][id].bwall;
      var variation : Integer = TObj(pTrgObj).wallvariation;
      var bCount : Integer;
      if (bwall) then
      bCount := gCustomBuildPointsWall[variation].builderCount
      else
      bCount := gCustomObjPoints[cid, id].builderCount;
      if (bCount>0) then
      begin
         var goX : Float = GetGameObjectPositionXByHandle(goHnd);
         var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
         var trgX, trgZ : Float;
         _unit_GetAlignedPosition(trgHnd, trgX, trgZ);

         var i, j : Integer;
         var minDist : Float = -1;
         var ind : Integer = -1;

         for i:=0 to bCount-1 do
         if (list.Get(i)=0) then
         begin
            var x, z : Float;
            if (bwall) then
            begin
               x := gCustomBuildPointsWall[variation].builderPoints[i].x;
               z := gCustomBuildPointsWall[variation].builderPoints[i].y;
            end
            else
            begin
               x := gCustomObjPoints[cid][id].builderPoints[i].x;
               z := gCustomObjPoints[cid][id].builderPoints[i].y;
            end;

            if _misc_GetRegionByPos(trgX+x, trgZ+z) = myRegion then
            begin
               var pSTOList : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_stolist);
               var free : Boolean = true;

               for j := TIntegerList(pSTOList).GetCount-1 downto 0 do
               begin
                  var tmpHnd : Integer = TIntegerList(pSTOList).Get(j);
                  var pTmpObj : Pointer = _unit_GetTObj(tmpHnd);
                  if ((not bRepair) and (TObj(pTmpObj).orders[0].itype=gc_obj_order_type_build)) or ((bRepair) and (TObj(pTmpObj).orders[0].itype=gc_obj_order_type_repair)) then
                  begin
                     var stX : Float = GetGameObjectStateTargetPositionXByHandle(tmpHnd);
                     var stZ : Float = GetGameObjectStateTargetPositionZByHandle(tmpHnd);
                     var tagAction : Integer = GetGameObjectStatesTagByHandle(tmpHnd) and gc_statetag_action;

                     if (stX=trgX+x) and (stZ=trgZ+z) and (tagAction=gc_statetag_action_build) then
                     begin
                        free := false;
                        break;
                     end;
                  end;
               end;

               if (free) then
               begin
                  var dist : Float = VectorLength((goX-trgX-x), 0, (goZ-trgZ-z));

                  if (minDist<0) or (dist<minDist) then
                  begin
                     ind := i;
                     minDist := dist;
                  end;
               end;
            end;
         end;

         if (ind>=0) then
         begin
            var x, z : Float;
            if (bwall) then
            begin
               x := gCustomBuildPointsWall[variation].builderPoints[ind].x;
               z := gCustomBuildPointsWall[variation].builderPoints[ind].y;
            end
            else
            begin
               x := gCustomObjPoints[cid][id].builderPoints[ind].x;
               z := gCustomObjPoints[cid][id].builderPoints[ind].y;
            end;
            list.Set(goHnd, ind);

            var posX : Float = trgX+x;
            var posZ : Float = trgZ+z;
            var ordtype : Integer;
            if (not bRepair) then
            ordtype := gc_obj_order_type_build
            else
            ordtype := gc_obj_order_type_repair;
            _unit_AddOrder(goHnd, ordtype, trgHnd, posX, posZ, 0, 0, 0, 0, 0, 0, 0, 0, 0, bClearOrders, False);
            Result := true;
         end;
      end;
   end
   else
   Log('_unit_OrderBuild : trgHnd=0');
end;

// _unit_RequestPlaySound
//
procedure _unit_RequestPlaySound(const goHnd, sndlibind : Integer);
begin
   if (sndlibind>gc_snd_ind_none) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) then
      begin
         var gridx, gridy : Integer;
         if (_misc_PosToSoundGridIndices(GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd), gridx, gridy)) then
         begin
            gint_dbg_logrequestsoundcount := gint_dbg_logrequestsoundcount+1;
            var psoundgrid : Pointer = gSoundGrid[gridx, gridy];
            TSoundGrid(psoundgrid).sndrequests[sndlibind] := TSoundGrid(psoundgrid).sndrequests[sndlibind]+1;
            TSoundGrid(psoundgrid).bsndrequestexist := True;
         end;
      end;
   end;
end;

// _unit_TryBuild
//
function _unit_TryBuild(const goHnd, trgHnd : Integer; const bRepair : Boolean) : Integer;
begin
   var res : Integer = gc_result_trybuild_targetnotvalid;
   if (trgHnd<>0) and (_misc_GetBaseObjID(trgHnd)=gc_baseid_obj) then
   begin
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_peasant) then
      begin
         var movetag : Integer;
         var actiontag : Integer;
         var executetag : Integer;
         var pTrgObj : Pointer = _unit_GetTObj(trgHnd);

         if (not bRepair) and (TObj(pTrgObj).bbuilt) then
         begin
            if (gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].usage=gc_obj_usage_mine) then
            begin
               _unit_ClearOrders(goHnd);
               movetag := gc_statetag_move_idle;
               actiontag := gc_statetag_action_none;
               _unit_OrderGoInside(goHnd, trgHnd);
            end
            else
            begin
               actiontag := gc_statetag_action_none;
               executetag := gc_statetag_execute_none;
               movetag := gc_statetag_move_idle;

               _unit_SetSTO(goHnd, 0);
               _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd));
               _unit_RemoveOrder(goHnd, 0);
            end;

            if (gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].bwall) then
            res := gc_result_trybuild_buildwall
            else
            res := gc_result_trybuild_targetnotvalid;
         end
         else
         begin
            if (bRepair) and (TObj(pTrgObj).hp>=gPlayer[TObj(pTrgObj).pl].objbase[TObj(pTrgObj).cid][TObj(pTrgObj).id].maxhp) then
            begin
               actiontag := gc_statetag_action_none;
               executetag := gc_statetag_execute_none;
               movetag := gc_statetag_move_idle;

               _unit_SetSTO(goHnd, 0);
               _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd));
               _unit_RemoveOrder(goHnd, 0);

               if gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].bwall then
               res := gc_result_trybuild_buildwall;
            end
            else
            begin
               var ordX : Float = TObj(pObj).orders[0].info.x;
               var ordZ : Float = TObj(pObj).orders[0].info.y;

               if (GetGameObjectSTOHandleByHandle(goHnd)<>trgHnd) then
               begin
                  _unit_SetSTO(goHnd, trgHnd);
                  _unit_SetSTP(goHnd, ordX, ordZ);

                  res := gc_result_tryextract_outofrange;
                  actiontag := gc_statetag_action_none;
                  executetag := gc_statetag_execute_move;
               end
               else
               begin
                  var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);
                  if ((statetag and gc_statetag_action_build)=0) then
                  begin
                     if ((statetag and gc_statetag_move_idle)<>0) then
                     begin
                        res := gc_result_trybuild_ok;
                        movetag := gc_statetag_move_idle;
                        actiontag := gc_statetag_action_build;
                     end
                     else
                     begin
                        var i : Integer;
                        var pSTOList : Pointer = _misc_GetObjectArgData(trgHnd, gc_argenv_stolist);
                        for i := TIntegerList(pSTOList).GetCount-1 downto 0 do
                        begin
                           var tmpHnd : Integer = TIntegerList(pSTOList).Get(i);
                           var stX : Float = GetGameObjectStateTargetPositionXByHandle(tmpHnd);
                           var stZ : Float = GetGameObjectStateTargetPositionZByHandle(tmpHnd);

                           if (tmpHnd<>goHnd) and (VectorDistance(stX, 0, stZ, ordX, 0, ordZ) < GetGameObjectEpsilonDistanceByHandle(goHnd)) then
                           begin
                              if ((GetGameObjectStatesTagByHandle(tmpHnd) and gc_statetag_action_build)<>0) then
                              begin
                                 _unit_SetSTO(goHnd, 0);
                                 var x, y : Float;

                                 if (_unit_GetFreeBuilderPoint(goHnd, trgHnd, x, y)) then
                                 begin
                                    TObj(pObj).orders[0].info.x := x;
                                    TObj(pObj).orders[0].info.y := y;
                                 end
                                 else
                                 begin
                                    actiontag := gc_statetag_action_none;
                                    executetag := gc_statetag_execute_none;
                                    movetag := gc_statetag_move_idle;
                                    _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(goHnd), GetGameObjectPositionZByHandle(goHnd));
                                    _unit_RemoveOrder(goHnd, 0);

                                    if (gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].bwall) then
                                    res := gc_result_trybuild_buildwall;

                                    break;
                                 end;
                              end;
                           end;
                        end;
                     end;
                  end;
               end;
            end;
         end;

         if (movetag<>0) or (actiontag<>0) or (executetag<>0) then
         _unit_SetTagStates(goHnd, movetag or actiontag or executetag);
      end;
   end;

   Result := res;
end;

// _unit_IsWorkResources
//
function _unit_IsWorkResources(const goHnd : Integer) : Boolean;
begin
   var anim : String = GetGameObjectAnimationCycleNameByHandle(goHnd);
   Result := (StrExists(anim, 'work'));
   //Result := (anim=gc_anim_workfood) or (anim=gc_anim_workwood) or (anim=gc_anim_workstone);
end;

// _unit_GetPeasantResPortion
//
function _unit_GetPeasantResPortion(const restype : Integer) : Integer;
begin
   case restype of
      gc_resource_type_food : Result := gc_obj_resource_portion_food;
      gc_resource_type_wood : Result := gc_obj_resource_portion_wood;
      gc_resource_type_stone : Result := gc_obj_resource_portion_stone;
      else
      Result := 20;
   end;
end;

// _unit_GetPeasantAddResToPlayerAmountByIndex
//
function _unit_GetPeasantAddResToPlayerAmountByIndex(const plInd, cid, restype : Integer) : Integer;
begin
   var eff : Integer = gPlayer[plInd].resefficiency[cid][restype];
   Result := ((_unit_GetPeasantResPortion(restype)*eff) div 100);
end;

// _unit_PeasantAddResToPlayerByIndex
//
procedure _unit_PeasantAddResToPlayerByIndex(const plInd, cid, restype : Integer);
begin
   var amount : Integer = _unit_GetPeasantAddResToPlayerAmountByIndex(plInd, cid, restype);
   _res_AddResToPlayerByIndex(plInd, restype, amount);
   gPlayer[plInd].stat.restotal[restype] := gPlayer[plInd].stat.restotal[restype]+amount
end;

// _unit_GetNearestStorehouse
//
function _unit_GetNearestStorehouse(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var restype : Integer = TObj(pobj).restype;
      var mindist : Float = gc_MaxInt;
      var minHnd : Integer;
      var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
      var i : Integer;
      for i:=gPlayer[TObj(pobj).pl].lists.storehouses.GetCount-1 downto 0 do
      begin
         var trgHnd : Integer = gPlayer[TObj(pobj).pl].lists.storehouses.Get(i);
         var ptrgobj : Pointer = _unit_GetTObj(trgHnd);
         if (ptrgobj<>nil) and (gObjProp[TObj(ptrgobj).cid][TObj(ptrgobj).id].resourcebase[restype]) and (TObj(ptrgobj).bbuilt) then
         begin
            var dist : Float = VectorDistance(GetGameObjectPositionXByHandle(goHnd), 0, GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd)+gCustomObjPoints[TObj(ptrgobj).cid][TObj(ptrgobj).id].resourcePoint.x, 0, GetGameObjectPositionZByHandle(trgHnd)+gCustomObjPoints[TObj(ptrgobj).cid][TObj(ptrgobj).id].resourcePoint.z);
            //if (gObjProp[TObj(ptrgobj).cid][TObj(ptrgobj).id].usage=gc_obj_usage_center) then // center require additional path to drop res
            //dist := dist+4;
            if (dist<mindist) then
            begin
               minHnd := trgHnd;
               mindist := dist;
               //mindist := distSqr;
            end;
         end;
      end;
      Result := minHnd;
   end;
end;

// _unit_DoReturnToStorehouse
//
function _unit_DoReturnToStorehouse(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var bDoSearch : Boolean = True;
   var trgHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   if (trgHnd<>0) and (_misc_GetBaseObjID(trgHnd)=gc_baseid_obj) then
   begin
      var pobj : Pointer = _unit_GetTObj(trgHnd);
      if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) then
      begin
         case gObjProp[TObj(pobj).cid][TObj(pobj).id].usage of
            gc_obj_usage_storage, gc_obj_usage_mill, gc_obj_usage_center : begin
               if (not TObj(pobj).bdead) then
               begin
                  bDoSearch := False;
                  _unit_SetSTO(goHnd, trgHnd);
                  if (GetMapCollisionTag(GetGameObjectPositionXByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.x, GetGameObjectPositionZByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.z, True)>0) then
                  ErrorLog('resourcepoint on collision goHnd='+IntToStr(trgHnd)+' basename='+GetGameObjectBaseNameByHandle(trgHnd));
                  _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.x, GetGameObjectPositionZByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.z);
                  Result := trgHnd;
               end;
            end;
         end;
      end;
   end;
   if (bDoSearch) then
   begin
      trgHnd := _unit_GetNearestStorehouse(goHnd);
      if (trgHnd<>0) then
      begin
         var pobj : Pointer = _unit_GetTObj(trgHnd);
         if (pobj<>nil) then
         begin
            _unit_SetSTO(goHnd, trgHnd);
            _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.x, GetGameObjectPositionZByHandle(trgHnd)+gCustomObjPoints[TObj(pobj).cid][TObj(pobj).id].resourcePoint.z);
         end
         else
         ErrorLog('_unit_DoReturnToStorehouse pobj=nil');
      end;
      Result := trgHnd;
   end;
end;

// _unit_CheckReturnToStorehouse
//
function _unit_CheckReturnToStorehouse(const goHnd : Integer) : Integer;
begin
   Result := gc_result_trystorehouse_notneeded;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var hitsneeded : Integer = _res_GetHitsNeeded(TObj(pobj).restype);
   if (TObj(pobj).resamount>=hitsneeded) then
   begin
      Result := gc_result_trystorehouse_return;
      TObj(pobj).resamount := hitsneeded;
      if (_unit_DoReturnToStorehouse(goHnd)=0) then
      Result := gc_result_trystorehouse_notavailable;
   end;
end;

// _unit_TryExtractResource
//
function _unit_TryExtractResource(const goHnd, trgHnd : Integer) : Integer;
begin
   //var profHnd : Integer = _misc_ProfilerBeginHnd('tryer');
   var res : Integer = gc_result_tryextract_ok;
   if (trgHnd<>0) then
   begin
      if (_misc_GetBaseObjID(trgHnd)<>gc_baseid_res) then
      ErrorLog('_unit_TryExtractResource we shouldnt be here. ask gec goHnd='+IntToStr(goHnd)+' trgHnd='+IntToStr(trgHnd));
      var pobj : Pointer = _unit_GetTObj(goHnd);
      if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_peasant) and (not TObj(pobj).bdead) then
      begin
         var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);
         var movetag, actiontag, executetag : Integer;
         var porderinfo : Pointer = TObj(pobj).orders[0].info;
         var resReturnToStorehouse : Integer = _unit_CheckReturnToStorehouse(goHnd);
         if (resReturnToStorehouse=gc_result_trystorehouse_notavailable) then
         res := gc_result_tryextract_targetnotvalid;
         if (resReturnToStorehouse=gc_result_trystorehouse_return) then
         begin
            res := gc_result_tryextract_returntostore;
            var storehouseHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
            if (storehouseHnd<>0) and (GetGameObjectPlayerHandleByHandle(storehouseHnd)=GetGameObjectPlayerHandleByHandle(goHnd)) then
            begin
               {if ((statetag and gc_statetag_action_extract)<>0)
               or (((statetag and gc_statetag_action_none)=0) and ((statetag and gc_statetag_move_idle)<>0)) then
               begin
                  actiontag := gc_statetag_action_none;
                  executetag := gc_statetag_execute_move;
               end;}

               var pobjStore : Pointer = _unit_GetTObj(storehouseHnd);
               if (pobjStore<>nil) then
               begin
                  var dstSqr : Float = Sqr(GetGameObjectPositionXByHandle(goHnd)-(GetGameObjectPositionXByHandle(storehouseHnd)+gCustomObjPoints[TObj(pobjStore).cid][TObj(pobjStore).id].resourcePoint.x))+Sqr(GetGameObjectPositionZByHandle(goHnd)-(GetGameObjectPositionZByHandle(storehouseHnd)+gCustomObjPoints[TObj(pobjStore).cid][TObj(pobjStore).id].resourcePoint.z));
                  //var dstSqr : Float = Sqr(GetGameObjectPositionXByHandle(goHnd)-GetGameObjectPositionXByHandle(storehouseHnd))+Sqr(GetGameObjectPositionZByHandle(goHnd)-GetGameObjectPositionZByHandle(storehouseHnd));
                  const gc_gameplay_resourceDropRadiusSqr = 0.5;
                  if (dstSqr<gc_gameplay_resourceDropRadiusSqr) then
                  begin
                     var hitsneeded : Integer = _res_GetHitsNeeded(TObj(pobj).restype);
                     if (TObj(pobj).resamount>=hitsneeded) then
                     begin
                        var plInd : Integer = GetPlayerIndexByHandle(GetGameObjectPlayerHandleByHandle(goHnd));
                        _unit_PeasantAddResToPlayerByIndex(plInd, TObj(pobj).cid, TObj(pobj).restype);
                        TObj(pobj).restype := gc_resource_type_none;
                        TObj(pobj).resamount := 0;

                        var resHnd : Integer = trgHnd;
                        if (resHnd=0) then
                        resHnd := _unit_SearchResourceInRadius(goHnd, TOrderInfo(porderinfo).x, TOrderInfo(porderinfo).y, gc_obj_res_searchradius, gc_obj_res_searchwaitrnd, TOrderInfo(porderinfo).restype, False);
                        if (resHnd<>0) then
                        begin
                           if (resHnd<>trgHnd) then
                           begin
                              Result := gc_result_tryextract_outofrange;
                              var px : Float = GetGameObjectPositionXByHandle(resHnd);
                              var py : Float = GetGameObjectPositionZByHandle(resHnd);
                              _unit_SetSTO(goHnd, resHnd);
                              _unit_SetSTP(goHnd, px, py);
                              //TOrderInfo(porderinfo).trg := resHnd;
                              _unit_SetOrderTrg(goHnd, 0, resHnd, True);
                              TOrderInfo(porderinfo).x := px;
                              TOrderInfo(porderinfo).y := py;
                              //movetag := gc_statetag_move_idle;
                              //actiontag := gc_statetag_action_none;
                              //executetag := gc_statetag_execute_none;
                           end;
                           movetag := gc_statetag_move_idle;
                           actiontag := gc_statetag_action_none;
                           executetag := gc_statetag_execute_none;
                           //exit;
                           //_misc_ProfilerEndHnd(profHnd);
                        end
                        else
                        begin
                           movetag := gc_statetag_move_idle;
                           actiontag := gc_statetag_action_none;
                           executetag := gc_statetag_execute_none;
                           _unit_SetSTO(goHnd, 0);
                        end;
                     end;
                  end
                  else
                  begin
                     var bExtracting : Boolean = ((statetag and gc_statetag_action_extract)<>0);
                     if (bExtracting) then
                     actiontag := gc_statetag_action_none;

                     var px : Float = GetGameObjectPositionXByHandle(storehouseHnd)+gCustomObjPoints[TObj(pobjStore).cid][TObj(pobjStore).id].resourcePoint.x;
                     var pz : Float = GetGameObjectPositionZByHandle(storehouseHnd)+gCustomObjPoints[TObj(pobjStore).cid][TObj(pobjStore).id].resourcePoint.z;
                     var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
                     var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);

                     if ((statetag and gc_statetag_move_idle)<>0) or ((Sqr(stx-px)+Sqr(stz-pz))>gc_ObjectEpsilonDistSqr) then
                     begin
                        actiontag := gc_statetag_action_none;
                        executetag := gc_statetag_execute_move;
                     end;
                  end;
               end;
            end;
         end
         else
         if (resReturnToStorehouse=gc_result_trystorehouse_notneeded) then
         begin
            var bNewSTO : Boolean = (GetGameObjectSTOHandleByHandle(goHnd)<>trgHnd);
            if (bNewSTO) then
            begin
               _unit_SetSTO(goHnd, trgHnd);
               _unit_SetSTP(goHnd, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd));
            end;
            var bExtracting : Boolean = ((statetag and gc_statetag_action_extract)<>0);

            var dx : Float = GetGameObjectPositionXByHandle(trgHnd);
            var dz : Float = GetGameObjectPositionZByHandle(trgHnd);
            var dstSqr : Float = Sqr(GetGameObjectPositionXByHandle(goHnd)-dx)+Sqr(GetGameObjectPositionZByHandle(goHnd)-dz);
            var rmax : Float;
            var pres : Pointer = _res_GetTRes(trgHnd);
            if (pres<>nil) then
            case TRes(pres).itype of
               gc_resource_type_food : begin
                  if ((statetag and gc_statetag_move_walk)<>0) then
                  rmax := gc_obj_extract_food_radiusmaxSqr*random
                  else
                  rmax := gc_obj_extract_food_radiusmaxSqr;
               end;
               gc_resource_type_stone : rmax := gc_obj_extract_stone_radiusmaxSqr;
               else
               rmax := gc_obj_extract_wood_radiusmaxSqr;
            end;
            if ((dstSqr>rmax)) then // Distance not OK
            begin
               res := gc_result_tryextract_outofrange;
               var tpx, tpy, tpz : Float;
               GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, GetGameObjectTrackPointCountByHandle(goHnd)-1, tpx, tpy, tpz);
               var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
               var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);

               if ((statetag and gc_statetag_move_idle)<>0) or ((Sqr(stx-tpx)+Sqr(stz-tpz))>gc_objectEpsilonDistSqr) then
               begin
                  _unit_SetSTP(goHnd, dx, dz);
                  actiontag := gc_statetag_action_none;
                  executetag := gc_statetag_execute_move;
               end
               else
               if (bExtracting) then
               actiontag := gc_statetag_action_none;
            end
            else
            begin
               res := gc_result_tryextract_ok;
               if (not bExtracting) then
               begin
                  var r : Float = gc_obj_res_searchradius*0.25;
                  if (TObj(pobj).standtime>=9) or (random>0.9) then
                  begin
                     r := gc_obj_res_searchradius*(0.3+(TObj(pobj).standtime/5));
                     if (r>gc_obj_res_searchradius*2) then
                     r := gc_obj_res_searchradius*2;
                  end;
                  var resHnd : Integer = _unit_SearchResourceInRadius(goHnd, dx, dz, r, gc_obj_res_searchwaitrnd, TOrderInfo(porderinfo).restype, False);
                  if (resHnd=0) then
                  begin
                     if (TObj(pobj).standtime>59) then
                     res := gc_result_tryextract_targetnotvalid;
                  end
                  else
                  begin
                     if (resHnd<>trgHnd) then
                     begin
                        Result := gc_result_tryextract_outofrange;
                        _unit_SetSTO(goHnd, resHnd);
                        _unit_SetSTP(goHnd, dx, dz);
                        _unit_SetOrderTrg(goHnd, 0, resHnd, True);
                        TOrderInfo(porderinfo).x := dx;
                        TOrderInfo(porderinfo).y := dz;
                        //_misc_ProfilerEndHnd(profHnd);
                        exit;
                     end;

                     var bTurn : Boolean = ((statetag and gc_statetag_move_turn)<>0) or ((bNewSTO) and (_unit_NeedTurn(goHnd)));
                     if (bTurn) then
                     begin
                        res := gc_result_tryextract_outofrange;
                        movetag := gc_statetag_move_turn;
                        actiontag := gc_statetag_action_none;
                     end
                     else
                     begin
                        var pres : Pointer = _res_GetTRes(trgHnd);
                        if (pres<>nil) then
                        begin
                           var maxatt : Integer;
                           case TRes(pres).itype of
                              gc_resource_type_food : maxatt := gc_gameplay_resource_maxattackers_food;
                              gc_resource_type_wood : maxatt := gc_gameplay_resource_maxattackers_wood;
                              gc_resource_type_stone : maxatt := gc_gameplay_resource_maxattackers_stone;
                           end;
                           var pattlist : Pointer = _misc_GetObjectArgData(trgHnd, gc_argenv_attlist);
                           if (pattlist<>nil) and (TIntegerList(pattlist).GetCount<maxatt) then
                           begin
                              if (TIntegerList(pattlist).IndexOf(goHnd)=-1) then
                              TIntegerList(pattlist).Add(goHnd);
                              res := gc_result_tryextract_ok;
                              movetag := gc_statetag_move_idle;
                              actiontag := gc_statetag_action_extract;
                           end;
                        end;
                     end;
                  end;
               end;
            end;
         end;
         if (movetag<>0) or (actiontag<>0) or (executetag<>0) then
         _unit_SetTagStates(goHnd, movetag or actiontag or executetag);
      end
      else
      res := gc_result_tryextract_targetnotvalid;
   end
   else
   res := gc_result_tryextract_targetnotvalid;
   if (res=gc_result_tryextract_targetnotvalid) then
   begin
      _unit_SetSTO(goHnd, 0);
      _unit_ClearOrders(goHnd);
      _unit_SetTagStates(goHnd, gc_statetag_execute_none or gc_statetag_move_idle or gc_statetag_action_none);
   end;
   Result := res;
   //_misc_ProfilerEndHnd(profHnd);
end;

// _unit_IsSelectSquad
//
function _unit_IsSelectSquad(const goHnd : Integer) : Boolean;
begin
   // don't select squad for single officer or drummer without squad
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var formInd : Integer = TObj(pObj).squad;

   if (formInd < 0) and (_unit_IsOfficer(goHnd) or _unit_IsDrummer(goHnd)) then
   Result := false
   else
   Result := true;
end;

// _unit_GetUnitForSelectionByType
//
function _unit_GetUnitForSelectionByType(const goHnd : Integer) : Integer;
begin
   // need to change unit if selected officer or drummer in squad
   Result := goHnd;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var formInd : Integer = TObj(pObj).squad;

   if (formInd >= 0) then
   begin
      var isOfficer : Boolean = _unit_IsOfficer(goHnd);
      var isDrummer : Boolean = _unit_IsDrummer(goHnd);

      if isOfficer or isDrummer then
      begin
         var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
         var plInd : Integer = GetPlayerIndexByHandle(plHnd);
         var pSquad : Pointer = gPlayer[plInd].squads.Get(formInd);
         var count : Integer = TSquad(pSquad).GetCount;
         Result := TSquad(pSquad).Get(count-1);
      end;
   end;
end;

// _unit_GetSquadUnit
//
function _unit_GetSquadUnit(const squad : Pointer) : Integer;
begin
   if (squad <> nil) and (TSquad(squad).GetCount > 0) then
   Result := TSquad(squad).Get(TSquad(squad).GetCount-1)
   else
   Result := 0;
end;

// _unit_IsUnitsCanCapture
//
function _unit_IsUnitsCanCapture(var list : TIntegerList; trgHnd : Integer) : Boolean;
begin
   Result := False;
   var pobjtrg : Pointer = _unit_GetTObj(trgHnd);
   if (pobjtrg<>nil) then
   begin
      if (not gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id].bcapture) then
      exit;
   end
   else
   exit;

   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) and ((not gObjProp[TObj(pobj).cid][TObj(pobj).id].bcapture) and (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding)) then
      begin
         Result := True;
         break;
      end;
   end;
end;

// _unit_IsUnitsCanConstruct
//
function _unit_UnitsCanConstruct(var list : TIntegerList; trgHnd : Integer) : Boolean;
begin
   Result := False;
   var i : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      if (_unit_GetUsage(goHnd)=gc_obj_usage_peasant) then
      begin
         Result := True;
         break;
      end;
   end;
end;

// _unit_SelectedUnitsCanMove
//
function _unit_SelectedUnitsCanMove(plInd : Integer; var list : TIntegerList) : Boolean;
begin
   Result := False;
   var i, j : Integer;

   if list.GetCount > 0 then
   begin
      for i:=list.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         if not _unit_IsBuilding(goHnd) then
         begin
            Result := true;
            break;
         end;
      end;
   end
   else
   begin
      for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = gPlayer[plInd].squads.Get(i);
         if TSquad(pSquad).fSelected then
         begin
            Result := true;
            break;
         end;
      end;
   end;
end;

// _unit_IsUnitCanAbsorbInside
//
function _unit_IsUnitCanAbsorbInside(goHnd : Integer) : Boolean;
begin
   Result := False;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      if (TObj(pobj).bbuilt) and ((TObjProp(pobjprop).peasantabsorber>0) or (TObjProp(pobjprop).transport>0)) then
      begin
         var pObjInside : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_inside);
         if (pObjInside<>nil) then
         begin
            var inCount : Integer = TIntegerList(pObjInside).GetCount;
            Result := ((TObjProp(pobjprop).peasantabsorber+TObjProp(pobjprop).transport+TObj(pobj).individual.addpeasantabsorber+TObj(pobj).individual.addtransport)>inCount);
         end;
      end;
   end;
end;

// _unit_CanGoInside
//
function _unit_CanGoInside(goHnd, trgHnd : Integer) : Boolean;
begin
   var pTrgObjProp : Pointer = _unit_GetObjProp(trgHnd);
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   if TObjProp(pTrgObjProp).bBuilding then
   Result := ((TObjProp(pTrgObjProp).usage = gc_obj_usage_mine) and (TObjProp(pObjProp).usage = gc_obj_usage_peasant))
   else
   Result := ((TObjProp(pTrgObjProp).transport > 0) and (not TObjProp(pObjProp).bBuilding) and (TObjProp(pObjProp).media = gc_obj_media_land));
end;

// _unit_UnitsCanGoInside
//
function _unit_UnitsCanGoInside(trgHnd : Integer; var list : TIntegerList; var squads : TPtrList) : Boolean;
begin
   Result := False;
   var pobj : Pointer = _unit_GetTObj(trgHnd);
   if (pobj<>nil) then
   begin
      var built : Boolean = TObj(pobj).bbuilt;
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      if (TObj(pobj).bbuilt) and ((TObjProp(pobjprop).peasantabsorber>0) or (TObjProp(pobjprop).transport>0)) then
      begin
         var pObjInside : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_inside);
         var inCount : Integer;
         if (pObjInside<>nil) then
         inCount := TIntegerList(pObjInside).GetCount;

         var f : Boolean = ((TObjProp(pobjprop).peasantabsorber + TObj(pobj).individual.addpeasantabsorber) > inCount);
         if not f then
         begin
            if ((TObjProp(pobjprop).transport + TObj(pobj).individual.addtransport > inCount) and TObj(pobj).bRally) then
            begin
               var x : Float = TObj(pobj).rallyX;
               var y : Float = TObj(pobj).rallyY;
               var tag : Integer = GetMapCollisionTag(x, y, true);
               f := (tag < gc_collisiontag_building);
            end
            else
            f := false;
         end;

         if f then
         begin
            var bmine : Boolean = (TObjProp(pobjprop).usage=gc_obj_usage_mine);
            var i : Integer;
            for i := list.GetCount-1 downto 0 do
            begin
               var goHnd : Integer = list.Get(i);
               if _unit_CanGoInside(goHnd, trgHnd) then
               begin
                  Result := true;
                  break;
               end;
            end;

            if not Result then
            begin
               for i := squads.GetCount-1 downto 0 do
               begin
                  var pSquad : Pointer = squads.GetPointer(i);
                  var count : Integer = TSquad(pSquad).GetCount;
                  if count > 0 then
                  begin
                     var goHnd : Integer = TSquad(pSquad).Get(0);
                     if _unit_CanGoInside(goHnd, trgHnd) then
                     begin
                        Result := true;
                        break;
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_SelectedUnitsCanAttack
//
function _unit_SelectedUnitsCanAttack(plInd, trgHnd : Integer; var list : TIntegerList) : Boolean;
begin
   Result := False;
   if (trgHnd<>0) then
   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) and (_unit_CanKillIncludeAttMask(goHnd, trgHnd)) and (not gObjProp[TObj(pobj).cid][TObj(pobj).id].bpriest) then
         begin
            Result := True;
            break;
         end;
      end;
      if (not Result) then
      begin
         for i := gPlayer[plInd].squads.GetCount-1 downto 0 do
         begin
            var pSquad : Pointer = gPlayer[plInd].squads.Get(i);
            if TSquad(pSquad).fSelected then
            begin
               var goHnd : Integer = _unit_GetSquadUnit(pSquad);
               if (goHnd<>0) and (_unit_CanKillIncludeAttMask(goHnd, trgHnd)) then
               begin
                  Result := True;
                  break;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_UnitsCanBuild
//
function _unit_UnitsCanBuild(trgHnd : Integer; var list : TIntegerList) : Boolean;
begin
   Result := False;
   var i : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      if (_unit_GetUsage(goHnd)=gc_obj_usage_peasant) then
      begin
         Result := True;
         break;
      end;
   end;
end;

// _unit_IsBuildingExistsInList
//
function _unit_IsBuildingExistsInList(var list : TIntegerList) : Boolean;
begin
   Result := False;
   var i : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      if _unit_IsBuilding(goHnd) then
      begin
         Result := True;
         exit;
      end;
   end;
end;

// _unit_IsUnitExistsInList
//
function _unit_IsUnitExistsInList(var list : TIntegerList) : Boolean;
begin
   Result := False;
   var i : Integer;
   for i:=list.GetCount-1 downto 0 do
   begin
      var goHnd : Integer = list.Get(i);
      if not _unit_IsBuilding(goHnd) then
      begin
         Result := True;
         exit;
      end;
   end;
end;

// _unit_GetSquadUnitCount
//
function _unit_GetSquadUnitCount(pSquad : Pointer) : Integer;
begin
   var delta : Integer;
   var goHnd : Integer = TSquad(pSquad).Get(0);
   if (goHnd <> 0) and _unit_IsOfficerOrDrummer(goHnd) then delta := delta-1;
   goHnd := TSquad(pSquad).Get(1);
   if (goHnd <> 0) and _unit_IsOfficerOrDrummer(goHnd) then delta := delta-1;
   Result := TSquad(pSquad).GetCount + delta;
end;

// _unit_MakeUpgrade
//
function _unit_MakeUpgrade(const plHnd : Integer; var list : TIntegerList; const upgid : Integer; const bState, blan : Boolean) : Boolean;
begin
   Result := False;
   var pobj : Pointer;
   var cid : Integer;
   var upgsid : String;
   var bldHnd : Integer = list.Get(0);
   if (bldHnd<>0) then
   begin
      pobj := _unit_GetTObj(bldHnd);
      if (pobj<>nil) then
      begin
         cid := TObj(pobj).cid;
         _country_GetUpgradeSIDByUpgradeID(cid, upgid, upgsid);
      end;
   end;
   var benoughres : Boolean = True;
   if _net_IsOffline then
   begin
      if (bldHnd<>0) and (pobj<>nil) then
      begin
         cid := TObj(pobj).cid;
         var plInd : Integer = GetPlayerIndexByHandle(plHnd);
         var i : Integer;
         for i := list.GetCount-1 downto 0 do
         begin
            var tmpHnd : Integer = list.Get(i);
            var pobj : Pointer = _unit_GetTObj(tmpHnd);
            var bPerformUpgrade : Boolean;
            var j : Integer;
            for j:=gc_obj_MaxOrderCount-1 downto 0 do
            begin
               if (TObj(pobj).orders[j].itype=gc_obj_order_type_performupgrade) then
               begin
                  bPerformUpgrade := True;
                  break;
               end;
            end;
            if (bState) then
            begin
               if (not bPerformUpgrade) and (TObj(pobj).bbuilt) and (not TObj(pobj).bdead) then
               begin
                  if (not gCountry[cid].upgrade[upgid].bindividual) or (gCountry[cid].upgrade[upgid].level=TObj(pobj).individual.upglevel+2) then
                  begin
                     bldHnd := tmpHnd;
                     break;
                  end
                  else
                  bldHnd := 0;
               end
               else
               bldHnd := 0;
            end
            else
            if (bPerformUpgrade) then
            begin
               for j:=gc_obj_MaxOrderCount-1 downto 0 do
               if (TObj(pobj).orders[j].info.upgradeid=upgid) then
               begin
                  bldHnd := tmpHnd;
                  break;
               end;
            end;
         end;

         if (bldHnd<>0) then
         begin
            if (bState) then
            begin
               if (_unit_CanApplyUpgradeCostBySID(plHnd, cid, upgsid)) then
               begin
                  if (not gPlayer[plInd].upgstate[cid][upgid].inprogress) or (gCountry[cid].upgrade[upgid].bindividual) then
                  begin
                     _unit_OrderUpgrade(bldHnd, upgsid);
                     _unit_ApplyUpgradeCostBySID(plHnd, cid, upgsid);
                     gPlayer[plInd].upgstate[cid][upgid].inprogress := True;
                     gPlayer[plInd].upgstate[cid][upgid].timestart := GetGameTime;
                     Result := True;
                  end;
               end
               else
               benoughres := False;
               {else
               // Show log if player InterfaceIO
               if (plHnd=GetPlayerHandleInterfaceIO) then
               begin
                  var txtres, s2 : String;
                  var food, wood, stone, gold, iron, coal : Integer;
                  _country_GetUpgradeCostBySID(cid, upgsid, food, wood, stone, gold, iron, coal);
                  _misc_PrepareLogMessageNotEnoughResourcesText(txtres, food, wood, stone, gold, iron, coal);
                  _misc_ShowLogMessage(gc_logmessagesid_notenoughres, txtres, s2, False);
               end;}
            end
            else
            begin
               _unit_CancelUpgradePerform(bldHnd, upgsid);
               Result := True;
            end;
         end;
      end;
   end;
   if (bState) and (plHnd=GetPlayerHandleInterfaceIO) and (not benoughres) then
   begin
      var txtres, s2 : String;
      var food, wood, stone, gold, iron, coal : Integer;
      _country_GetUpgradeCostBySID(cid, upgsid, food, wood, stone, gold, iron, coal);
      _misc_PrepareLogMessageNotEnoughResourcesText(txtres, food, wood, stone, gold, iron, coal);
      _misc_ShowLogMessage(gc_logmessagesid_notenoughres, txtres, s2, False);
   end
   else
   // network
   if {Result and }blan and (_net_IsOnline or _net_IsRecord) then begin
      var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
      if parg<>nil then begin
         TPlayerArgs(parg).fintlst:=list;
         TPlayerArgs(parg).fcid:=upgid;
         TPlayerArgs(parg).fbstate:=bState;
         PlayerExecuteStateByHandle(plhnd, 'WriteUpgrade');
      end else _misc_ErrorLog('_unit_MakeUpgrade: parg = nil');
   end;
end;

// _unit_ProduceUnit
//
function _unit_ProduceUnit(const plHnd : Integer; var list : TIntegerList; const cid, id, tryamount : Integer; const bState, bcheckres, blan : Boolean) : Boolean;
begin
   Result := False;
   var amount : Integer = tryamount;
   var amountproceed : Integer;
   var bldHnd : Integer = list.Get(0);
   var bsetinfinite : Boolean;
   if (bldHnd<>0) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var bldcount : Integer = list.GetCount;
      if (amount<=gc_obj_order_produce_infinite) then
      begin
         bsetinfinite := True;
         amount := round(Abs(amount));
      end;
      var i : Integer;
      for i:=0 to amount-1 do
      begin
         // find min/maxproduceind
         var produceind : Integer = -1;
         var produceamount : Integer;
         var j : Integer;
         for j:=0 to bldcount-1 do
         begin
            var curproduceamount : Integer;
            var curproduceamountmisc : Integer;
            var curproduceinfinite : Integer;
            var curproduceinfinitemisc : Integer;
            var bcurinfinite : Boolean;
            bldHnd := list.Get(j);
            var pobj : Pointer = _unit_GetTObj(bldHnd);
            if (pobj<>nil) and (TObj(pobj).bbuilt) then
            begin
               var k : Integer;
               for k:=gc_obj_MaxOrderCount-1 downto 0 do
               begin
                  var porder : Pointer = TObj(pobj).orders[k]; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(porder);
                  if (TOrder(porder).itype=gc_obj_order_type_produce) then
                  begin
                     if (TOrder(porder).info.amount<>gc_obj_order_produce_infinite) then
                     begin
                        if (TOrder(porder).info.produceid=id) then
                        curproduceamount := curproduceamount+TOrder(porder).info.amount
                        else
                        curproduceamountmisc := curproduceamountmisc+TOrder(porder).info.amount;
                     end
                     else
                     begin
                        if (TOrder(porder).info.produceid=id) then
                        begin
                           bcurinfinite := True;
                           curproduceinfinite := curproduceinfinite+1
                        end
                        else
                        curproduceinfinitemisc := curproduceinfinitemisc+1;
                     end;
                  end;
               end;
               if (bState) then
               begin
                  curproduceamount := curproduceamount+curproduceamountmisc;
                  curproduceinfinite := curproduceinfinite+curproduceinfinitemisc;
                  curproduceamount := curproduceamount+curproduceinfinite;
               end;
               if (not bcurinfinite) then
               begin
                  if ((bState) and ((produceind=-1) or (curproduceamount<produceamount))) or
                  ((not bState) and (curproduceamount>0) and ((produceind=-1) or (curproduceamount>produceamount))) then
                  begin
                     produceamount := curproduceamount;
                     produceind := j;
                  end;
               end;
            end;
         end;
         if (produceind<>-1) then
         begin
            bldHnd := list.Get(produceind);
            var pobj : Pointer = _unit_GetTObj(bldHnd);
            if (pobj<>nil) then
            begin
               if (bState) then
               begin
                  var enoughres : Boolean = (not bcheckres) or _unit_CanApplyCostByID(cid, id, plInd);
                  if (enoughres{ or (_net_IsClient or _net_IsReplay)}) then
                  begin
                     if (_net_IsClient and (not bcheckres)) or (not _net_IsClient) then
                     begin
                        if (bsetinfinite) then
                        _unit_OrderProduce(bldHnd, cid, id, gc_obj_order_produce_infinite)
                        else
                        _unit_OrderProduce(bldHnd, cid, id, 1);
                     end;

                     amountproceed := amountproceed+1;
                     Result := True;
                  end;
                  // Show log if player InterfaceIO
                  if (not enoughres) and (plHnd=GetPlayerHandleInterfaceIO) then
                  begin
                     var txtres, s2 : String;
                     var food, wood, stone, gold, iron, coal : Integer;
                     _unit_GetCostByID(cid, id, plInd, food, wood, stone, gold, iron, coal);
                     _misc_PrepareLogMessageNotEnoughResourcesText(txtres, food, wood, stone, gold, iron, coal);
                     _misc_ShowLogMessage(gc_logmessagesid_notenoughres, txtres, s2, False);
                  end;
               end
               else
               begin
                  if (_net_IsClient and (not bcheckres)) or (not _net_IsClient) then
                  _unit_CancelUnitProduction(bldHnd, cid, id, 1, False);
                  amountproceed := amountproceed+1;
                  Result := True;
               end;
            end;
         end
         else
         begin
            if (bState) and (_net_IsClient) and (amountproceed=0) then
            begin
               amountproceed := amount;
               Result := True;
            end;
            break;
         end;
      end;
      if (not bState) and (amountproceed<amount) then
      begin
         for [MAIN]i:=0 to (amount-amountproceed)-1 do
         begin
            var j : Integer;
            for j:=0 to bldcount-1 do
            begin
               if (amountproceed>=amount) then
               break(MAIN);
               bldHnd := list.Get(j);
               var pobj : Pointer = _unit_GetTObj(bldHnd);
               if (TObj(pobj).bbuilt) then
               begin
                  var k : Integer;
                  for k:=gc_obj_MaxOrderCount-1 downto 0 do
                  begin
                     var porder : Pointer = TObj(pobj).orders[k]; if (gProfile.bDbgLogPointerNil) then _init_DebugPointerNil(porder);
                     if (TOrder(porder).itype=gc_obj_order_type_produce) and (TOrder(porder).info.produceid=id) then
                     begin
                        if (TOrder(porder).info.amount=gc_obj_order_produce_infinite) then
                        begin
                           if (_net_IsClient and (not bcheckres)) or (not _net_IsClient) then
                           _unit_CancelUnitProduction(bldHnd, cid, id, 1, False);
                           Result := True;
                           amountproceed := amountproceed+1;
                           break;
                        end;
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;
   // network
   if Result and blan and (_net_IsOnline or _net_IsRecord) then begin
      var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
      if parg<>nil then begin
         TPlayerArgs(parg).fintlst:=list;
         TPlayerArgs(parg).fid:=id;
         TPlayerArgs(parg).fcid:=cid;
         if (bsetinfinite) then
         TPlayerArgs(parg).famount:=-amount
         else
         TPlayerArgs(parg).famount:=amountproceed;
         TPlayerArgs(parg).fbstate:=bState;
         PlayerExecuteStateByHandle(plhnd, 'WriteProduce');
      end else _misc_ErrorLog('_unit_ProduceUnit: parg = nil');
   end;
end;

// _unit_ListSetSearchEnemy
//
function _unit_ListSetSearchEnemy(plHnd : Integer; var list : TIntegerList; bState, blan : Boolean) : Boolean;
begin
   Result := False;
   var count : Integer = list.GetCount;
   if (count>0) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var i : Integer;
      for i:=count-1 downto 0 do
      begin
         var goHnd : Integer = list.Get(i);
         var pobj : Pointer = _unit_GetTObj(goHnd);
         if (pobj<>nil) then
         begin
            TObj(pobj).bsearchenemy := bState;
            Result := True;
         end;
      end;
      // network
      if Result and blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
         if parg<>nil then begin
            TPlayerArgs(parg).fintlst:=list;
            TPlayerArgs(parg).fbstate:=bState;
            PlayerExecuteStateByHandle(plhnd, 'WriteSearch');
         end else _misc_ErrorLog('_unit_ListSetSearchEnemy: parg = nil');
      end;
   end;
end;

// _unit_SetSquadsSearchEnemy
//
function _unit_SetSquadsSearchEnemy(plHnd : Integer; var list : TIntegerList; bState, blan : Boolean) : Boolean;
begin
   Result := False;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var count : Integer = list.GetCount;
   if (count>0) then
   begin
      if (blan) then
      gWriteSquadPtrList.Clear;
      var i, j : Integer;
      for i:=0 to count-1 do
      begin
         var squadind : Integer = list.Get(i);
         if (squadind>=0) then
         begin
            var pSquad : Pointer = gPlayer[plInd].squads.Get(squadind);
            if pSquad <> nil then
            begin
               if (blan) then
               gWriteSquadPtrList.AddPointer(psquad);
               TSquad(pSquad).fSearchVictim := bstate;
               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
                  TObj(pobj).bsearchenemy := bState;
               end;
               Result := True;
            end;
         end;
      end;
      // network
      if Result and blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
         if parg<>nil then begin
            gIntListWriteSquadAction.Clear;
            TPlayerArgs(parg).fpointer0 := gWriteSquadPtrList;
            TPlayerArgs(parg).fordtyp := gc_playersquad_action_searchenemy;
            TPlayerArgs(parg).fbstate := bstate;
            TPlayerArgs(parg).fform := 0;
            TPlayerArgs(parg).fintlst := gIntListWriteSquadAction;
            PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
         end else _misc_ErrorLog('_unit_SetSelectedSquadsSearchEnemy: parg = nil');
      end;
   end;
end;

// _unit_ListSetStandGround
//
function _unit_ListSetStandGround(plHnd : Integer; var list : TIntegerList; bState, blan : Boolean) : Boolean;
begin
   Result := False;
   var count : Integer = list.GetCount;
   if (count>0) then
   begin
      var plInd : Integer = GetPlayerIndexByHandle(plHnd);
      var i : Integer;
      for i:=count-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 := bState;
            Result := True;
         end;
      end;
      // network
      if Result and blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
         if parg<>nil then begin
            TPlayerArgs(parg).fintlst:=list;
            TPlayerArgs(parg).fbstate:=bState;
            PlayerExecuteStateByHandle(plhnd, 'WriteStand');
         end else _misc_ErrorLog('_unit_ListSetStandGround: parg = nil');
      end;
   end;
end;

// _unit_SetSquadsStandGround
//
function _unit_SetSquadsStandGround(plHnd : Integer; var list : TIntegerList; bState, blan : Boolean) : Boolean;
begin
   Result := False;
   var plInd : Integer = GetPlayerIndexByHandle(plHnd);
   var count : Integer = list.GetCount;
   if (count>0) then
   begin
      if (blan) then
      gWriteSquadPtrList.Clear;
      var i, j : Integer;
      for i:=0 to count-1 do
      begin
         var squadind : Integer = list.Get(i);
         if (squadind>=0) then
         begin
            var pSquad : Pointer = gPlayer[plInd].squads.Get(squadind);
            if pSquad <> nil then
            begin
               if (blan) then
               gWriteSquadPtrList.AddPointer(pSquad);
               TSquad(pSquad).fStandGround := bState;
               if (not bState) then
               begin
                  TSquad(pSquad).fHoldMode := False;
                  TSquad(pSquad).fHoldModeProgress := 0;
               end;
               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
                  TObj(pobj).bstandground := bState;
               end;
               Result := True;
            end;
         end;
      end;
      // network
      if Result and blan and (_net_IsOnline or _net_IsRecord) then begin
         var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plhnd), gc_argplayer_plr);
         if parg<>nil then begin
            gIntListWriteSquadAction.Clear;
            TPlayerArgs(parg).fpointer0 := gWriteSquadPtrList;
            TPlayerArgs(parg).fordtyp := gc_playersquad_action_standground;
            TPlayerArgs(parg).fbstate := bstate;
            TPlayerArgs(parg).fform := 0;
            TPlayerArgs(parg).fintlst := gIntListWriteSquadAction;
            PlayerExecuteStateByHandle(plhnd, 'WriteSquadListAction');
         end else _misc_ErrorLog('_unit_SetSquadsStandGround: parg = nil');
      end;
   end;
end;

// _unit_AddExitPoint
//
procedure _unit_AddExitPoint(cid, id : Integer; x, y, z : Float);
begin
   var eCount : Integer = gCustomObjPoints[cid, id].exitCount;
   if eCount < gc_MaxExitPointsCount then
   begin
      gCustomObjPoints[cid, id].exitPoints[eCount].x := x;
      gCustomObjPoints[cid, id].exitPoints[eCount].y := y;
      gCustomObjPoints[cid, id].exitPoints[eCount].z := z;
      gCustomObjPoints[cid, id].exitCount := eCount+1;
   end;
end;

// _unit_SetResourcePoint
//
procedure _unit_SetResourcePoint(cid, id : Integer; x, y, z : Float);
begin
   gCustomObjPoints[cid, id].resourcePoint.x := x;
   gCustomObjPoints[cid, id].resourcePoint.y := y;
   gCustomObjPoints[cid, id].resourcePoint.z := z;
end;

// _unit_AddDecal
//
procedure _unit_AddDecal(cid, id : Integer; scale, offx, offz, angle : Float);
begin
   gCustomObjPoints[cid, id].decal.bexists := True;
   gCustomObjPoints[cid, id].decal.scale := scale;
   gCustomObjPoints[cid, id].decal.offx := offx;
   gCustomObjPoints[cid, id].decal.offz := offz;
   gCustomObjPoints[cid, id].decal.angle := angle;
end;

// _unit_GetLastOrderIndex
//
function _unit_GetLastOrderIndex(const goHnd : Integer) : Integer;
begin
   Result := -1;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (pObj<>nil) then
   begin
      var i : Integer;
      for i := gc_obj_MaxOrderCount-1 downto 0 do
      if (TObj(pObj).orders[i].itype<>gc_obj_order_type_none) then
      begin
         Result := i;
         break;
      end;
   end;
end;

procedure _unit_ApplyAttackPause(const goHnd, weapind : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
      var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
      var bNeedPause : Boolean;
      var attpause : Float = TObjBase(pobjbase).weapon[weapind].pause;
      bNeedPause := (attpause>0);
      if (TObj(pobj).individual.benabled) and (attpause>0) then
      attpause := attpause*TObj(pobj).individual.attackrate;
      var frames : Integer;
      {if (bNeedPause) and (not TObjProp(pobjprop).bbuilding) then
      begin
         var sf, ef : Integer;
         var anim : String;
         case weapind of
            0 : anim := gc_anim_attack0;
            1 : anim := gc_anim_attack1;
            2 : anim := gc_anim_attack2;
            else
            Log('unit have more then 3 weapons, check this');
         end;
         if (GetGameObjectFrameAnimationDataByHandle(goHnd, anim, sf, ef)) then
         begin
            frames := 1+ef-sf;
            if ((attpause-(frames/30))<(1/60)) then
            bNeedPause := False;
         end;
      end;}
      // DEBUG TODO REMOVE STAT PORPOSES..!!!!!!!!!!!!!!!!!..!!!!!!!!!!
      // think of add of several frames to melee units animations, and not add small attpauses in 1-2 frames
      //if (bNeedPause) then
      //Log('basename='+GetGameObjectBaseNameByHandle(goHnd)+' bNeedPause='+BoolToStr(bNeedPause)+' attpauseframes='+IntToStr(Round(attpause*30))+' frames='+IntToStr(frames)+' deltapause='+FloatToStr(attpause-frames/30));

      if (not bNeedPause) then attpause := 0;

      TObj(pobj).attackdelay := attpause;
      TObj(pobj).attackmaxdelay := attpause;

      //if (attpause=0) and (gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].bshowdelay) then
      //TObj(pobj).attackmaxdelay := 1;
   end;
end;

// _unit_ForceStateTag
//
procedure _unit_ForceStateTag(const hnd, newtag : Integer); //set newtag to 0, for use current tag
begin
   // _unit_ForceStateTag is similar to unit.aix OnTagStates at 16.09.2015. last changes made by gec. need to test online.
   procedure DoDeath(hnd : Integer);
   begin
      SetGameObjectPlayableObjectByHandle(hnd, False);
      SetGameObjectTrackPointMovementModeByHandle(hnd, 'mmNone');
      SetGameObjectTargetRotatingModeByHandle(hnd, 'trmNone');
      GameObjectPFXClearByHandle(hnd);
      SetGameObjectPickedByHandle(hnd, false);
      SetGameObjectAnimationCyclesModeByHandle(hnd, 'acmPlayOnce');
      SetGameObjectAnimationModeByHandle(hnd, 'aamPlayOnce');
      SetGameObjectAnimationControlerEnabledByHandle(hnd, False);
      SetGameObjectOnStateStartCyclesReachedByHandle(hnd, '');
      SetGameObjectOnStateEndCyclesReachedByHandle(hnd, '');
      SetGameObjectOnStateFBEndCyclesReachedByHandle(hnd, '');
      SetGameObjectOnStateDirectionReachedByHandle(hnd, '');
      SetGameObjectOnStateEndPointReachedByHandle(hnd, '');
      SetGameObjectOnStateCollectingPointReachedByHandle(hnd, '');
      //SetGameObjectCollidedStateNameByHandle(hnd, '');
      //SetGameObjectUncollidedStateNameByHandle(hnd, '');
      SetGameObjectCollisionDetectionByHandle(hnd, False);
      var unittype : Integer = _misc_GetUnitType(hnd);
      if (unittype=gc_result_unittype_inf) then
      begin
         const cDeathRollAngle = 55;
         GameObjectRollByHandle(hnd, -cDeathRollAngle+random*cDeathRollAngle*2);
      end;
   end;
   if (hnd<>0) then
   begin
      var pobj : Pointer = _unit_GetTObj(hnd);
      if (pobj<>nil) then
      begin
         var tag : Integer;
         if (newtag<>0) then
         tag := newtag
         else
         tag := GetGameObjectStatesTagByHandle(hnd);
         var iResource : Integer = tag and gc_statetag_resource;
         var iExecute : Integer = tag and gc_statetag_execute;
         var iMove : Integer = tag and gc_statetag_move;
         var iWeapon : Integer = tag and gc_statetag_weapon;
         var iAction : Integer = tag and gc_statetag_action;
         var iEssential : Integer = tag and gc_statetag_essential;
         var iVisual : Integer = tag and gc_statetag_visual;

         var animidle, animwalk, anim : String;
         var minFrameBlend : Integer = 6;
         var maxFrameBlend : Integer = 8;

         var animinterval : Float = 1;

         if (iVisual=gc_statetag_visual_stage_0) then
         SetGameObjectAnimationCyclesModeByHandle(hnd, 'acmPlayOnce')
         else
         SetGameObjectAnimationCyclesModeByHandle(hnd, 'acmLoop');

         procedure DoSetupMoveAnimation(hnd : Integer);
         begin
            var bSkip : Boolean;
            var bUseWeaponAnim : Boolean;
            var animturn : String;
            var trgHnd : Integer = GetGameObjectSTOHandleByHandle(hnd);
            if (iAction<>gc_statetag_action_none) then
            begin
               case iAction of
                  gc_statetag_action_extract : begin
                     if (trgHnd<>0) then
                     begin
                        var pres : Pointer = _res_GetTRes(trgHnd);
                        if pres<>nil then
                        case TRes(pres).itype of
                           gc_resource_type_food : anim := gc_anim_workfood;
                           gc_resource_type_wood : anim := gc_anim_workwood;
                           gc_resource_type_stone : anim := gc_anim_workstone;
                        end;
                     end;
                  end;
                  gc_statetag_action_build : anim := gc_anim_construct;
                  gc_statetag_action_attack : begin
                     case iWeapon of
                        gc_statetag_weapon_none, gc_statetag_weapon_0 : anim := gc_anim_attack0;
                        gc_statetag_weapon_1 : anim := gc_anim_attack1;
                        gc_statetag_weapon_2 : anim := gc_anim_attack2;
                     end;
                  end;
               end;
               if (anim<>'') then
               begin
                  var bExists : Boolean = _unit_IsAnimationExists(hnd, anim);
                  if (bExists) then
                  begin
                     GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, False, False, False, 0, 0); // 0, 0 - dont use blend cause it kills animation
                     GameObjectResetFrameAnimationBlend(hnd); // force playing animation as it should looks like
                     bSkip := True;
                  end;
               end;
            end
            else
            if (iResource<>gc_statetag_resource_none) then
            begin
               case iResource of
                  gc_statetag_resource_food : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idlefood;
                        gc_statetag_move_turn : animidle := gc_anim_idlefood;
                        gc_statetag_move_walk : animwalk := gc_anim_walkfood;
                     end;
                  end;
                  gc_statetag_resource_wood : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idlewood;
                        gc_statetag_move_turn : animidle := gc_anim_idlewood;
                        gc_statetag_move_walk : animwalk := gc_anim_walkwood;
                     end;
                  end;
                  gc_statetag_resource_stone : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idlestone;
                        gc_statetag_move_turn : animidle := gc_anim_idlestone;
                        gc_statetag_move_walk : animwalk := gc_anim_walkstone;
                     end;
                  end;
               end;
            end
            else
            begin
               //bUseWeaponAnim := True; // commented, same as in unit.aix OnTagStates
               case iWeapon of
                  gc_statetag_weapon_none : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idle;
                        gc_statetag_move_turn : animidle := gc_anim_idle;
                        gc_statetag_move_walk : animwalk := gc_anim_walk;
                     end
                  end;
                  gc_statetag_weapon_0 : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idle0;
                        gc_statetag_move_turn : animidle := gc_anim_idle0;
                        gc_statetag_move_walk : animwalk := gc_anim_walk0;
                     end
                  end;
                  gc_statetag_weapon_1 : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idle1;
                        gc_statetag_move_turn : animidle := gc_anim_idle1;
                        gc_statetag_move_walk : animwalk := gc_anim_walk1;
                     end
                  end;
                  gc_statetag_weapon_2 : begin
                     case iMove of
                        gc_statetag_move_idle : animidle := gc_anim_idle2;
                        gc_statetag_move_turn : animidle := gc_anim_idle2;
                        gc_statetag_move_walk : animwalk := gc_anim_walk2;
                     end
                  end;
               end;
            end;
            var bRotate : Boolean;
            if (not bSkip) then
            begin
               if (iMove=gc_statetag_move_turn) then
               begin
                  {if (GetGameObjectTrackPointCurrentPointIndexByHandle(hnd)<GetGameObjectTrackPointCountByHandle(hnd)-1) then
                  begin
                     if _unit_IsAnimationExists(hnd, animwalk) then
                     anim := animwalk
                     else
                     anim := gc_anim_walk;

                     GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, True, False, False, minFrameBlend, maxFrameBlend)
                  end
                  else}
                  begin
                     if (bUseWeaponAnim) then
                     anim := animidle
                     else
                     case GetGameObjectFrameAnimationNameByHandle(hnd) of
                        gc_anim_trans10, gc_anim_trans20, gc_anim_walk0, gc_anim_prepare0, gc_anim_attack0, gc_anim_idle0 : anim := gc_anim_idle0;
                        gc_anim_trans01, gc_anim_trans21, gc_anim_walk1, gc_anim_prepare1, gc_anim_attack1, gc_anim_idle1 : anim := gc_anim_idle1;
                        gc_anim_trans02, gc_anim_trans12, gc_anim_walk2, gc_anim_prepare2, gc_anim_attack2, gc_anim_idle2 : anim := gc_anim_idle2;
                        else
                        if iResource<>gc_statetag_resource_none then
                        anim := animidle
                        else
                        anim := gc_anim_idle;
                     end;
                     if (not _unit_IsAnimationExists(hnd, animidle)) then
                     anim := gc_anim_idle;

                     if (trgHnd=0) then
                     begin
                        var dx, dy, dz : Float;
                        var angle : Float = GetGameObjectSTArrowAngleByHandle(hnd);
                        dx := 1;
                        VectorRotateY(dx, dy, dz, angle);
                        bRotate := GameObjectSetupRotatingToDirectionByHandle(hnd, dx, dy, dz, anim, anim, anim, gObjProp[TObj(pobj).cid][TObj(pobj).id].rotatespeed*2.225, 50, GetGameObjectEpsilonAngleByHandle(hnd), True);
                     end
                     else
                     begin
                        GameObjectResetFrameAnimationBlend(hnd);
                        bRotate := GameObjectSetupRotatingToTargetByHandle(hnd, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionYByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd), anim, anim, anim, gObjProp[TObj(pobj).cid][TObj(pobj).id].rotatespeed*2.225, 50, GetGameObjectEpsilonAngleByHandle(hnd), True);
                        if (bRotate) then
                        begin
                           //SetGameObjectTargetObjectByHandle(hnd, trgHnd); // crashes if target is field, and field game object removed. here is invalid handle.
                           //SetGameObjectTargetRotatingModeByHandle(hnd, 'trmToTargetObject');
                           SetGameObjectTargetRotatingModeByHandle(hnd, 'trmToTargetCoordinates');
                        end;
                     end;
                     if (not bRotate) then
                     begin
                        SetGameObjectTargetRotatingModeByHandle(hnd, 'trmNone');
                        iMove := gc_statetag_move_idle;
                        // added to prevent situation, when brotate=false but unit in some cases continue playing previous attack animation, without changing it to idle, as it should
                        GameObjectResetFrameAnimationBlend(hnd);
                        //if (anim<>GetGameObjectFrameAnimationNameByHandle(hnd)) then // not in unit.aix OnTagStates
                        GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, True, False, False, minFrameBlend, maxFrameBlend);
                     end;
                  end;
               end;
               // else removed, cause we want to set idle animation, if setup rotation return (not rotate).
               if (iMove=gc_statetag_move_idle) then
               begin
                  if (bUseWeaponAnim) then
                  anim := animidle
                  else
                  case GetGameObjectFrameAnimationNameByHandle(hnd) of
                     gc_anim_trans10, gc_anim_trans20, gc_anim_walk0, gc_anim_prepare0, gc_anim_attack0, gc_anim_idle0 : anim := gc_anim_idle0;
                     gc_anim_trans01, gc_anim_trans21, gc_anim_walk1, gc_anim_prepare1, gc_anim_attack1, gc_anim_idle1 : anim := gc_anim_idle1;
                     gc_anim_trans02, gc_anim_trans12, gc_anim_walk2, gc_anim_prepare2, gc_anim_attack2, gc_anim_idle2 : anim := gc_anim_idle2;
                     else
                     if iResource<>gc_statetag_resource_none then
                     anim := animidle
                     else
                     anim := gc_anim_idle;
                  end;
                  if (not _unit_IsAnimationExists(hnd, animidle)) then
                  anim := gc_anim_idle;

                  if (anim<>GetGameObjectFrameAnimationNameByHandle(hnd)) then
                  begin
                     GameObjectResetFrameAnimationBlend(hnd);
                     GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, True, False, False, 0, 0); // 0, 0 - to reset blend. needed to prevent 2 shooting animation effect
                  end;
               end
               else
               if (iMove=gc_statetag_move_walk) then
               begin
                  if _unit_IsAnimationExists(hnd, animwalk) then
                  anim := animwalk
                  else
                  anim := gc_anim_walk;
                  if (anim<>GetGameObjectFrameAnimationNameByHandle(hnd)) then
                  begin
                     animinterval := gObjProp[TObj(pobj).cid][TObj(pobj).id].walkintervalfactor;
                     GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, True, False, False, minFrameBlend, maxFrameBlend);
                  end;
               end;
            end;
            if (not bRotate) and (_unit_NeedTurn(hnd)) then
            begin
               //_misc_LogInt(tag and gc_statetag_sync_stp);
               var angle : Float = GetGameObjectSTArrowAngleByHandle(hnd);
               var dx : Float = 1;
               var dy, dz : Float;
               VectorRotateY(dx, dy, dz, angle);
               bRotate := GameObjectSetupRotatingToDirectionByHandle(hnd, dx, dy, dz, anim, anim, anim, gObjProp[TObj(pobj).cid][TObj(pobj).id].rotatespeed*2.225, 50, GetGameObjectEpsilonAngleByHandle(hnd), True);
            end;
            if (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_peasant) and (GetGameObjectCountChildByHandle(hnd)>0) and (anim<>'') then
            _unit_ManagePeasantAttach(hnd, anim);
         end;

         if (iEssential=gc_statetag_essential_death) then
         begin
            if (not TObj(pobj).bdead) then // check if we need this, uncomment
            GameObjectExecuteStateByHandle(hnd, 'OnDeath');
            if (iVisual=gc_statetag_visual_hide) then // inside mine or transport
            begin
               DoDeath(hnd);
               SetGameObjectAnimationCyclesModeByHandle(hnd, 'acmNone');
               SetGameObjectAnimationModeByHandle(hnd, 'aamNone');
            end
            else
            begin
               GameObjectCancelDelayExecuteStateByHandle(hnd);
               GameObjectDelayExecuteStateByHandle(hnd, 'DeathStage1', gc_unit_deathtime_0);
               case iWeapon of
                  gc_statetag_weapon_none : anim := gc_anim_death;
                  gc_statetag_weapon_0 : anim := gc_anim_death0;
                  gc_statetag_weapon_1 : anim := gc_anim_death1;
                  gc_statetag_weapon_2 : anim := gc_anim_death2;
               end;
               if (iWeapon<>gc_statetag_weapon_none) and (not _unit_IsAnimationExists(hnd, anim)) then
               anim := gc_anim_death;

               DoDeath(hnd);
               var basename : String = GetGameObjectBaseNameByHandle(hnd);
               var bartillery : Boolean = (_misc_GetUnitType(hnd)=gc_result_unittype_art);
               if (bartillery) then
               ReloadGameObjectProperties(hnd, '', basename+'_death');
               GameObjectSwitchToTreeAnimationCyclesBlendByHandle(hnd, anim, False, False, False, False, minFrameBlend, maxFrameBlend);
               if (bartillery) then
               GameObjectResetFrameAnimationBlend(hnd); // reset after switch
               SetGameObjectIntervalFactorByHandle(hnd, 0.7+random*0.6); // units die at different animation speed, to desyncronise visual part
            end;
         end
         else
         DoSetupMoveAnimation(hnd);

         if (iVisual=gc_statetag_visual_hide) then
         begin
            SetGameObjectVisibleByHandle(hnd, False);
            SetGameObjectPlayableObjectByHandle(hnd, False); // Same as in unit.aix OnTagStates
            SetGameObjectCollisionDetectionByHandle(hnd, False);
            var trgHnd : Integer = GetGameObjectSTOHandleByHandle(hnd);
            if (trgHnd<>0) then
            begin
               //SetGameObjectPositionByHandle(hnd, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionYByHandle(trgHnd), GetGameObjectPositionZByHandle(trgHnd));
               _misc_UnitRemoveMiniMapPrimitive(hnd);
               SetGameObjectPickedByHandle(hnd, False);
               gSelectedObjects.Remove(hnd);
               gGuiUpdateHighlights.Add(hnd);
               var pobjinside : Pointer = _misc_GetObjectArgData(trgHnd, gc_argunit_inside);
               var pTrgObj : Pointer = _unit_GetTObj(trgHnd);
               if (pobjinside<>nil) and (gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].peasantabsorber + gObjProp[TObj(pTrgObj).cid][TObj(pTrgObj).id].transport > 0) then // Same check as in unit.aix OnTagStates
               begin
                  if TIntegerList(pobjinside).IndexOf(hnd)<0 then
                  TIntegerList(pobjinside).Add(hnd);
               end;
            end;
         end
         else
         if (iVisual=gc_statetag_visual_none) and (not GetGameObjectVisibleByHandle(hnd)) then
         begin
            _misc_UnitCreateMiniMapPrimitive(hnd, False);
            SetGameObjectPlayableObjectByHandle(hnd, True);
            SetGameObjectCollisionDetectionByHandle(hnd, True);
            gGuiUpdateHighlights.Add(hnd);
         end;

         // added 15.11.2015, same as in OnTagStates. not needed, break lan when essential_none. set sto to zero
         {if (iEssential=gc_statetag_essential_birth) then
         begin
            SetGameObjectPlayableObjectByHandle(hnd, false);
            SetGameObjectCollisionDetectionByHandle(hnd, False);
            SetGameObjectPickedByHandle(hnd, False);
            gSelectedObjects.Remove(hnd);
            gGuiUpdateHighlights.Add(hnd);
            _unit_ScanGridRemoveUnit(hnd);
            _unit_SelectedGroupRemoveUnit(hnd);
         end
         else
         if (iEssential=gc_statetag_essential_none) then
         begin
            SetGameObjectPlayableObjectByHandle(hnd, True);
            SetGameObjectCollisionDetectionByHandle(hnd, True);
            gGuiUpdateHighlights.Add(hnd);
            _unit_SetSTO(hnd, 0);
            _unit_ScanGridProgress(hnd);
         end;}
         if (iAction=gc_statetag_action_build) and (GetGameObjectCIMovableByHandle(hnd)) then
         begin
            SetGameObjectCIMovableByHandle(hnd, False);
            SetGameObjectCIIntersectRadiusByHandle(hnd, gc_collision_radius_default*0);
         end
         else
         if (iAction<>gc_statetag_action_build) and (not GetGameObjectCIMovableByHandle(hnd)) then
         begin
            SetGameObjectCIMovableByHandle(hnd, True);
            SetGameObjectCIIntersectRadiusByHandle(hnd, gc_collision_radius_default);
         end;

         if (animinterval<>GetGameObjectIntervalFactorByHandle(hnd)) then
         SetGameObjectIntervalFactorByHandle(hnd, animinterval);
      end
      else
      ErrorLog('ForceStateTag : pobj=nil');
   end
   else
   ErrorLog('ForceStateTag : hnd=0');
end;

// _unit_SearchEnemyLongRangeArtillery
//
function _unit_SearchEnemyLongRangeArtillery(const goHnd : Integer; posX, posZ : Float; checkMinDist : Boolean; var targetHnd : Integer; var outPosX, outPosZ : Float; bSearchMaxAttRadius : Boolean) : Boolean;
begin
   Result := False;
   targetHnd := 0;

   var pobj : Pointer = _unit_GetTObj(goHnd);
   var plInd : Integer = TObj(pObj).pl;
   var pSquad : Pointer = _unit_GetSquad(goHnd);
   var pobjprop : Pointer = gObjProp[TObj(pobj).cid][TObj(pobj).id];
   var pobjbase : Pointer = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id];
   var enemyplmask : Integer = gPlayer[TObj(pobj).pl].enemyplmask;
   var killmask : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].kmask;
   var maxR : float;
   if (bSearchMaxAttRadius) then
   maxR := _unit_GetMaxAttackRadius(goHnd)
   else
   maxR := TObjProp(pobjprop).searchradius;
   var minsearchdist : Float = gObjProp[TObj(pobj).cid][TObj(pobj).id].minattackradius;
   var goHeight : Float;
   if (not RayCastWater(posX, posZ, goHeight)) then // is water, then goHeight is water height
   goHeight := RayCastHeight(posX, posZ);
   if (goHeight<0) then goHeight := 0;
   maxR := maxR+goHeight*2;

   if gPlayer[plInd].bai and (pSquad <> nil) then
   begin
      var army : Integer = TSquad(pSquad).fArmy;
      if (army>=0) then
      begin
         var pArmy : Pointer = gPlayer[plInd].aiData.armyList.Get(army);
         if pArmy <> nil then
         begin
            var uid : Integer = TArmy(pArmy).fOrder.targetUID;
            if uid <> 0 then
            begin
               var tHnd : Integer = GetGameObjectHandleByUniqueId(uid);
               if tHnd <> 0 then
               begin
                  var goX : Float = GetGameObjectPositionXByHandle(goHnd);
                  var goZ : Float = GetGameObjectPositionZByHandle(goHnd);
                  var trgX : Float = GetGameObjectPositionXByHandle(tHnd);
                  var trgZ : Float = GetGameObjectPositionZByHandle(tHnd);

                  if VectorDistance(goX, 0, goZ, trgX, 0, trgZ) < maxR then
                  begin
                     targetHnd := tHnd;
                     Result := true;

                     var pObjProp : Pointer = _unit_GetObjProp(tHnd);
                     if TObjProp(pObjProp).bWall then
                     TArmy(pArmy).fOrder.iType := gc_ai_armyorder_attackwall;

                     exit;
                  end;
               end
               else
               _unit_SetArmyOrderTarget(pArmy, 0);
            end;
         end;
      end;
   end;

   //try towers first:
   var pEnemyInfo : pointer = _misc_GetPlayerEnemyInfo(TObj(pobj).pl);

   var i, j : Integer;
   if (pEnemyInfo<>nil) then
   begin
      for i:=0 to TEnemyInfo(pEnemyInfo).towers.GetCount-1 do
      begin
         var pTower : pointer = TEnemyInfo(pEnemyInfo).towers.get(i);
         if (pTower<>nil) then
         begin
            var towerHnd : Integer = TTower(pTower).hnd;
            var pObj : pointer = _unit_GetTObj(towerHnd);
            if (pObj<>nil) and (not TObj(pObj).bdead) then
            begin
               var trgPosX : Float = GetGameObjectPositionXByHandle(towerHnd);
               var trgPosZ : Float = GetGameObjectPositionZByHandle(towerHnd);

               var check : Boolean;
               if not gbool_peacemode then
               check := true
               else
               begin
                  var gridX, gridY : Integer;
                  if _misc_PosToScanGridIndices(trgPosX, trgPosZ, gridX, gridY) then
                  begin
                     if gScanGrid[gridX, gridY].owner = plInd then
                     check := true;
                  end;
               end;

               if check then
               begin
                  var dist : Float = VectorDistance(trgPosX, 0, trgPosZ, posX, 0, posZ);
                  if ((not checkMinDist) or (dist>minsearchdist)) and (dist<(maxR*0.75)) then
                  begin
                     if (_misc_IsBuildingInRay(goHnd, posX, GetGameObjectPositionYByHandle(goHnd)+0.5, posZ, trgPosX, GetGameObjectPositionYByHandle(towerHnd)+0.5, trgPosZ)=0) then
                     begin
                        Result := True;
                        targetHnd := towerHnd;
                        exit;
                     end;
                  end;
               end;
            end;
         end;
      end;
   end;

   var bMortar : Boolean = (TObjProp(pobjprop).usage=gc_obj_usage_supermortar);
   var bGalley : Boolean = (TObjProp(pobjprop).usage=gc_obj_usage_galley); // TODO SUPPORT GALLEY
   var bestdiff, bestgridx, bestgridy : Integer;
   var mindist : Float = gc_MaxInt;
   var minTrgHnd : Integer;
   var rx1 : Integer = floor(maxR/gc_scangrid_size)+1;
   var cellx, celly, cellxmax, cellymax : Integer;
   var scanGridX, scanGridY : Integer;
   _misc_PosToScanGridIndices(posX, posZ, scanGridX, scanGridY);
   _misc_CalcScanCellsMinMax(scanGridX, scanGridY, rx1, cellx, celly, cellxmax, cellymax);
   for i:=cellx to (cellxmax) do
   for j:=celly to (cellymax) do
   if _misc_IsCorrectScanCellOwner(plInd, i, j) then
   begin
      var plmask : Integer = gScanGrid[i,j].fPlMask;
      var bisenemy : Boolean = (enemyplmask and plmask<>0);
      if (bisenemy) then
      begin
         if (not bMortar) or (gScanGrid[i, j].fMatCount[gc_obj_material_building]>0) then
         begin
            var enemycount, allycount : Integer;
            var k : Integer;
            for k:=0 to gScanGrid[i, j].GetCount-1 do
            begin
               var trgHnd : Integer = gScanGrid[i, j].Get(k);
               var pobjtrg : Pointer = _unit_GetTObj(trgHnd);
               var pobjtrgprop : Pointer = gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id];
               if (not bMortar) then
               begin
                  if (enemyplmask and gPlayer[TObj(pobjtrg).pl].myplmask<>0) then
                  begin
                     if (TObjProp(pobjtrgprop).mmask<>killmask) and (not TObjProp(pobjtrgprop).bcapture) then
                     begin
                        if (TObjProp(pobjtrgprop).media=gc_obj_media_water) then
                        enemycount := enemycount+6
                        else
                        enemycount := enemycount+1;
                     end;
                  end
                  else
                  allycount := allycount+1;
               end;
               if ((gObjProp[TObj(pobjtrg).cid][TObj(pobjtrg).id].bbuilding) and (not TObj(pobjtrg).bdead) and (enemyplmask and gPlayer[TObj(pobjtrg).pl].myplmask<>0))
               and (not TObjProp(pObjTrgProp).bWall) then
               begin
                  if (not TObjProp(pobjtrgprop).bcapture) or (TObjProp(pobjtrgprop).usage=gc_obj_usage_mine) or (TObjProp(pobjtrgprop).usage=gc_obj_usage_center) or (TObjProp(pobjtrgprop).usage=gc_obj_usage_artdepo) or (TObjProp(pobjtrgprop).bmarket) then
                  begin
                     var dist : Float = VectorDistance(posx, 0, posz, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
                     if (dist<mindist) and ((not checkMinDist) or (dist>minsearchdist)) and (dist<maxR) then
                     begin
                        if ((TObjProp(pobjprop).weapon[0].weaponid=0) or (not gWeapons[0].bcheckfriendonline)) or (_misc_IsBuildingInRay(goHnd, posX, GetGameObjectPositionYByHandle(goHnd)+0.5, posZ, GetGameObjectPositionXByHandle(trgHnd), GetGameObjectPositionYByHandle(trgHnd)+0.5, GetGameObjectPositionZByHandle(trgHnd))=0) then
                        begin
                           mindist := dist;
                           minTrgHnd := trgHnd;
                        end;
                     end;
                  end;
               end;
            end;
            var bcannister : Boolean;
            if (TObjProp(pobjprop).usage=gc_obj_usage_cannon) then // cannister
            begin
               var ax : Float = Abs(scanGridX-i);
               var ay : Float = Abs(scanGridY-j);
               if (ax<3) and (ay<3) then
               begin
                  bcannister := True;
                  var len : Float = VectorLength(ax, 0, ay);
                  len := len*len;
                  enemycount := floor(enemycount*(8-len));
                  allycount := 10+floor(allycount*3*(8-len));
               end;
            end;
            var diff : Integer = enemycount-allycount;
            if (((diff>5) and (allycount<30)) or ((enemycount>=1) and (allycount<3))) and (diff>bestdiff) then //sacrifice small amounts of friendly units
            begin
               var tmpx, tmpz : Float;
               _misc_ScanGridIndicesToPos(i, j, tmpx, tmpz);
               var dist : Float = VectorDistance(posX, 0, posZ, tmpx, 0, tmpz);
               if ((not checkMinDist) or (dist>minsearchdist)) and (dist<maxR) then
               begin
                  bestdiff := diff;
                  if (bcannister) and (dist>TObjBase(pobjbase).weapon[0].radiusmax) then
                  _misc_GetPositionOnLine(posX, posZ, tmpx, tmpz, (TObjBase(pobjbase).weapon[0].radiusmax-gc_objectEpsilonDist), outposx, outposz)
                  else
                  begin
                     bestgridx := i;
                     bestgridy := j;
                     outposx := tmpx;
                     outposz := tmpz;
                  end;
               end;
            end;
         end;
      end;
   end;

   if (TObjProp(pObjProp).media=gc_obj_media_water) and (bestdiff>0) then
   begin
      var tmpTrgHnd : Integer = _unit_SearchEnemyInCellShips(goHnd, bestgridx, bestgridy, gPlayer[TObj(pobj).pl].bai);
      if (tmpTrgHnd<>0) then
      minTrgHnd := tmpTrgHnd;
   end
   else
   if (not bMortar) and (bestdiff>0) then
   begin
      Result := True;
      exit;
   end;

   if (minTrgHnd<>0) then
   begin
      Result := True;
      targetHnd := minTrgHnd;
      exit;
   end;

   if not Result then
   begin
      //TODO: add wall destruction code
   end;
end;

// _unit_DoExplosion
//
procedure _unit_DoExplosion(hnd : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(hnd);
   if (pobj<>nil) then
   begin
      var pobjprop : Pointer = _unit_GetObjProp(hnd);
      var bship : Boolean = _unit_IsWaterUnit(hnd);
      if ((TObjProp(pobjprop).bbuilding) and (TObjProp(pobjprop).usage<>gc_obj_usage_weakwall)) or ((bship) and (TObjProp(pobjprop).usage<>gc_obj_usage_fisher)) then
      begin
         if (not gbool_gui_gatefinished) or (TObjProp(pobjprop).usage<>gc_obj_usage_hardwall) then
         begin
            var w : Integer = GetGameObjectCollisionMaskWidthByHandle(hnd);
            var h : Integer = GetGameObjectCollisionMaskHeightByHandle(hnd);
            if ((h>0) and (w>0)) or (bship) then
            begin
               var minx, miny, minz, maxx, maxy, maxz : Float;
               GetGameObjectCustomBoundingAABBByHandle(hnd, minx, miny, minz, maxx, maxy, maxz);
               if (miny>0) then miny := 0;
               var aabbheight : Float = maxy-miny;

               var px : Float = GetGameObjectPositionXByHandle(hnd);
               var py : Float = GetGameObjectPositionYByHandle(hnd);
               var pz : Float = GetGameObjectPositionZByHandle(hnd);
               var plist : Pointer;
               if (TObjProp(pobjprop).bbuilding) then
               plist := gDestructListBuilding
               else
               plist := gDestructListShip;
               if (plist<>nil) then
               begin
                  var count : Integer = TIntegerList(plist).GetCount;
                  if (count>0) then
                  begin
                     SetRandomKey(floor(TObj(pobj).uniqrnd*gc_MaxInt)); // sync multiplayer
                     if (not bship) then
                     begin
                        var mapW : Integer = GetMapWidth;
                        var mapH : Integer = GetMapHeight;
                        var i, j : Integer;
                        for i := 0 to h-1 do
                        for j := 0 to w-1 do
                        if GetGameObjectCollisionMaskValueByHandle(hnd, i, j) then
                        begin
                           var explX, explZ : Float;
                           if w mod 2 = 1 then
                           explX := px + (j-(w-1)/2)/2
                           else
                           explX := px + (j+1-w/2)/2;

                           if h mod 2 = 1 then
                           explZ := pz + (i-(h-1)/2)/2
                           else
                           explZ := pz + (i-h/2)/2;

                           var k : Integer;
                           for k:=0 to 3 do
                           begin
                              if (RandomExt<0.33) then
                              if (explX>=-mapW/2) and (explX<=mapW/2) and (explZ>=-mapH/2) and (explZ<=mapH/2) then
                              begin
                                 explX := Floor(2*explX)/2+0.125+(k div 2)*0.25;
                                 explZ := Floor(2*explZ)/2+0.125+(k mod 2)*0.25;
                                 var aabbmod : Float = (0.675-RandomExt)*aabbheight*0.5;
                                 if (aabbmod<0) then
                                 aabbmod := 0;
                                 var zmod : Float = (pz-explZ)*0.4;
                                 if (zmod<0) then
                                 zmod := 0;
                                 var explY : Float = py+0.3+aabbmod+zmod; // 0.3 - const (magic number), same as in C1
                                 var rnd : Integer = floor(RandomExt*count);
                                 var rndweapid : Integer = TIntegerList(plist).Get(rnd);
                                 if (rndweapid>0) then
                                 _weapon_CreateProjectileByWeaponID(hnd, 0, False, explX, explY, explZ, rndweapid, 0, 0, 0, TObj(pobj).cid, TObj(pobj).id, 0, RandomExt);
                              end;
                           end;
                        end;
                     end
                     else
                     begin
                        const cShipDestructCount = 25;
                        var i : Integer;
                        for i:=0 to cShipDestructCount-1 do
                        begin
                           var rnd : Integer = floor(RandomExt*count);
                           var rndweapid : Integer = TIntegerList(plist).Get(rnd);
                           if (rndweapid>0) then
                           _weapon_CreateProjectileByWeaponID(hnd, 0, False, px+(0.5-RandomExt*gc_tiles_to_pixels*180), py+gc_tiles_to_pixels*60/3, pz+(0.5-RandomExt*gc_tiles_to_pixels*180), rndweapid, 0, 0, 0, TObj(pobj).cid, TObj(pobj).id, 0, RandomExt);
                        end;
                     end;
                     gEffectLibrary.GetOrCreate('expflash', hnd);
                  end;
               end;
            end;
         end;
      end;
   end;
end;

// _unit_DoProjectile
//
procedure _unit_DoProjectile(goHnd, trgHnd : Integer; bUseTrgPos : Boolean; sx, sy, sz : Float; var tx, ty, tz : Float; weapind : Integer);
begin
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      if (trgHnd<>0) and (bUseTrgPos) then
      GetGameObjectAbsolutePositionByHandle(trgHnd, tx, ty, tz);
      var disp : Float = gPlayer[TObj(pobj).pl].objbase[TObj(pobj).cid][TObj(pobj).id].weapon[weapind].dispertion;
      SetRandomKey(floor(random*gc_MaxInt)); // needed to sync multiplayer arg.frnd
      if (disp>0) then
      _weapon_CalcShotDispertion(goHnd, disp, tx, ty, tz, tx, ty, tz);
      var weaponid : Integer = gObjProp[TObj(pobj).cid][TObj(pobj).id].weapon[weapind].weaponid;

      var plhnd : Integer;
      if goHnd<>0 then
      plHnd := GetGameObjectPlayerHandleByHandle(goHnd)
      else
      plHnd := GetPlayerHandleByIndex(0);
      var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
      if (parg<>nil) then
      begin
         TPlayerArgs(parg).fhandle:=goHnd;
         TPlayerArgs(parg).ftarget:=trgHnd;
         TPlayerArgs(parg).fbstate:=bUseTrgPos;
         TPlayerArgs(parg).fposx:=sx;
         TPlayerArgs(parg).fposy:=sy;
         TPlayerArgs(parg).fposz:=sz;
         TPlayerArgs(parg).fweaponid:=weaponid;
         TPlayerArgs(parg).ftrgx:=tx;
         TPlayerArgs(parg).ftrgy:=ty;
         TPlayerArgs(parg).ftrgz:=tz;
         TPlayerArgs(parg).fcid:=TObj(pobj).cid;
         TPlayerArgs(parg).fid:=TObj(pobj).id;
         TPlayerArgs(parg).find:=weapind;
         TPlayerArgs(parg).frnd:=RandomExt;
         PlayerExecuteStateByHandle(plHnd, 'WriteProj');
      end
      else
      ErrorLog('_unit_DoProjectile parg=nil');
      //_weapon_CreateProjectileByWeaponID(goHnd, trgHnd, bUseTrgPos, sx, sy, sz, weaponid, tx, ty, tz, TObj(pobj).cid, TObj(pobj).id, weapind, RandomExt);
   end;
end;

// _unit_GetVision
//
procedure _unit_GetVision(var res: Integer; const hnd: Integer);
begin
   //const cBaseVal = 18;
   //const cModVal = 3.6;
   //const cModVal = 3.75;
   const cBaseVal = 20;
   const cModVal = 4;
   if (GetGameObjectStringPropertyTag(hnd)=gc_properties_stringtag_fogreveal) then
   begin
      var smhnd: Integer=GetGameObjectStateMachineHandle(hnd);
      if smhnd<>0 then begin
         var pproj: Pointer=StateMachineGetArgDataByInd(smhnd, gc_argproj_proj);
         if (pproj<>nil) then
         res:=floor(TProj(pproj).dx)
         else begin
            res:=0;
            ErrorLog('_unit_GetVision pproj=nil');
         end;
         end else begin
         res:=0;
         ErrorLog('_unit_GetVision proj smhnd=0');
      end;
   end
   else
   begin
      if (GetGameObjectVisibleByHandle(hnd)) then
      begin
         var smhnd: Integer=GetGameObjectStateMachineHandle(hnd);
         if smhnd<>0 then begin
            var pobj: Pointer=StateMachineGetArgDataByInd(smhnd, gc_argunit_obj);
            if (pobj<>nil) then
            begin
               if (not TObj(pobj).bdead) and (TObj(pobj).bbuilt) then
               res:=floor(cBaseVal+cModVal*gObjProp[TObj(pobj).cid][TObj(pobj).id].vision)
               else
               res:=0;
            end
            else begin
               res:=0;
               ErrorLog('_unit_GetVision pobj=nil');
            end;
            end else begin
            res:=0;
            ErrorLog('_unit_GetVision smhnd=0');
         end;
      end
      else
      res:=0;
   end;
end;

// _unit_IsOnEnemyTerritory
//
function _unit_IsOnEnemyTerritory(hnd : Integer; pObj : Pointer) : Boolean;
begin
   var goX, goY, goZ : Float;
   GetGameObjectAbsolutePositionByHandle(hnd, goX, goY, goZ);
   var gridX, gridY : Integer;
   if _misc_PosToScanGridIndices(goX, goZ, gridX, gridY) then
   Result := (gScanGrid[gridX, gridY].owner <> TObj(pObj).pl)
   else
   Result := false;
end;

// _unit_UseParentTLFBehaviour
//
procedure _unit_UseParentTLFBehaviour(const parentHnd : Integer);
begin
   var bHnd : Integer = BehaviourCreate(parentHnd, 'TXTLFAnimationBehaviour', true, false);
   SetBehaviourIntProperty(bHnd, 'CleanObjects', 0);
   SetBehaviourIntProperty(bHnd, 'SetTLFObject', parentHnd);
   var i : Integer;
   for i := 0 to GetGameObjectCountChildByHandle(parentHnd)-1 do
   begin
      var cHnd : Integer = GetGameObjectGOHandleChildByHandle(parentHnd, i);
      SetGameObjectUseIdentityMatrix(cHnd, false);
      GameObjectRootSetupIdentityRecTransformationByHandle(cHnd);
      SetBehaviourIntProperty(bHnd, 'AddObject', cHnd);
   end;
end;

// _unit_CreateShipChilds
//
procedure _unit_CreateShipChilds(hnd : Integer);
begin
   var basename : String = GetGameObjectBaseNameByHandle(hnd);
   //removing TLF
   var bhnd : Integer = GameObjectEffectBehaviourByHandle(Hnd, 'TXTLFAnimationBehaviour');
   if (bhnd<>0) then
   BehaviourDestroy(bhnd);
   case basename of
      //settuping ships attaches:
      'battleship' : begin
         var childHnd : Integer;
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'battleship_flag1');
         SetGameObjectMatrixByHandle(childHnd, 0, 3.4669*2, 2.0082*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'battleship_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0, 1.6349*2, 3.8574*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'battleship_flag3');
         SetGameObjectMatrixByHandle(childHnd, 0, -0.0325*2, 5.3732*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'battleship_flag4');
         SetGameObjectMatrixByHandle(childHnd, 0, -1.8655*2, 4.8566*2, 0, 0, 1, 0, 1, 0);
         GameObjectAddNewChild(hnd, gc_racename_units, 'battleship_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'chaika' : begin
         var childHnd : Integer;
         GameObjectAddNewChild(hnd, gc_racename_units, 'chaika_sail');
         GameObjectAddNewChild(hnd, gc_racename_units, 'chaika_paddle_r');
         GameObjectAddNewChild(hnd, gc_racename_units, 'chaika_paddle_l');
         //GameObjectAddNewChild(hnd, gc_racename_units, 'chaika_flag1');
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'chaika_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0, -0.4286*2, 1.671*2, 0, 0, 1, 0, 1, 0);
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'ferry' : begin
         GameObjectAddNewChild(hnd, gc_racename_units, 'ferry_sail');
         GameObjectAddNewChild(hnd, gc_racename_units, 'ferry_paddle_r');
         GameObjectAddNewChild(hnd, gc_racename_units, 'ferry_paddle_l');
         GameObjectAddNewChild(hnd, gc_racename_units, 'ferry_ladder');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'fishboat' : begin
         GameObjectAddNewChild(hnd, gc_racename_units, 'fishboat_sail');
         GameObjectAddNewChild(hnd, gc_racename_units, 'fishboat_paddle_r');
         GameObjectAddNewChild(hnd, gc_racename_units, 'fishboat_paddle_l');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'frigate' : begin
         var childHnd : Integer;
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'frigate_flag1');
         SetGameObjectMatrixByHandle(childHnd, 0, 3.0976*2, 1.8363*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'frigate_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0, 1.8652*2, 4.1988*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'frigate_flag3');
         SetGameObjectMatrixByHandle(childHnd, 0, 0.5506*2, 5.7399*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'frigate_flag4');
         SetGameObjectMatrixByHandle(childHnd, 0, -1.4975*2, 5.363*2, 0, 0, 1, 0, 1, 0);
         GameObjectAddNewChild(hnd, gc_racename_units, 'frigate_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'galley' : begin
         var childHnd : Integer;
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'galley_flag1');
         SetGameObjectMatrixByHandle(childHnd, 0, 3.147*2, 1.038*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'galley_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0, 2.4123*2, 0.9418*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'galley_flag3');
         SetGameObjectMatrixByHandle(childHnd, 0, -0.0302*2, 2.7284*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'galley_flag4');
         SetGameObjectMatrixByHandle(childHnd, 0, -1.801*2, 2.5381*2, 0, 0, 1, 0, 1, 0);
         GameObjectAddNewChild(hnd, gc_racename_units, 'galley_paddle_r');
         GameObjectAddNewChild(hnd, gc_racename_units, 'galley_paddle_l');
         GameObjectAddNewChild(hnd, gc_racename_units, 'galley_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'xebec' : begin
         var childHnd : Integer;
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'xebec_flag1');
         SetGameObjectMatrixByHandle(childHnd, 0, 1.9455*2, 2.8989*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'xebec_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0, -2.5726*2, 3.2912*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'xebec_flag3');
         SetGameObjectMatrixByHandle(childHnd, 0, -0.24*2, 4.2834*2, 0, 0, 1, 0, 1, 0);
         GameObjectAddNewChild(hnd, gc_racename_units, 'xebec_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'yacht' : begin
         var childHnd : Integer;
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'yacht_flag1');
         SetGameObjectMatrixByHandle(childHnd, 0, 1.3961*2, 1.2389*2, 0, 0, 1, 0, 1, 0);
         childHnd := GameObjectAddNewChild(hnd, gc_racename_units, 'yacht_flag2');
         SetGameObjectMatrixByHandle(childHnd, 0{-0.0526*2 ??}, -0.6458*2, 3.0541*2, 0, 0, 1, 0, 1, 0);
         GameObjectAddNewChild(hnd, gc_racename_units, 'yacht_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
      'yachttur' : begin
         GameObjectAddNewChild(hnd, gc_racename_units, 'yachttur_sail');
         _unit_UseParentTLFBehaviour(hnd);
      end;
   end;
end;

// _unit_SetRallyPoint
//
procedure _unit_SetRallyPoint(goHnd : Integer; x, z : Float; setRally, checkFlag : Boolean);
begin
   if (goHnd<>0) then
   begin
      var pObj : Pointer = _unit_GetTObj(goHnd);
      if (pObj<>nil) and ((not checkFlag) or gObjProp[TObj(pObj).cid][TObj(pObj).id].bcansetrally) then
      begin
         TObj(pObj).brally := setRally;
         TObj(pObj).rallyx := x;
         TObj(pObj).rallyy := z;
         TObj(pObj).rallytmpx := x;
         TObj(pObj).rallytmpy := z;
         // network
         if _net_IsOnline or _net_IsRecord then begin
            var plHnd : Integer = GetPlayerHandleByIndex(TObj(pObj).pl);
            var parg: Pointer = StateMachineGetArgDataByInd(GetPlayerStateMachineHandle(plHnd), gc_argplayer_plr);
            if parg<>nil then begin
               TPlayerArgs(parg).fhandle:=goHnd;
               TPlayerArgs(parg).fbstate:=setRally;
               TPlayerArgs(parg).fposx:=x;
               TPlayerArgs(parg).fposz:=z;
               PlayerExecuteStateByHandle(plHnd, 'WriteRally');
            end else _misc_ErrorLog('parg = nil');
         end;
      end;
   end;
end;

// _unit_GetArmy
//
function _unit_GetArmy(goHnd : Integer) : Pointer;
begin
   Result := nil;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (pObj<>nil) and (TObj(pObj).squad>=0) then
   begin
      var pSquad : Pointer = gPlayer[TObj(pObj).pl].squads.Get(TObj(pObj).squad);
      if pSquad <> nil then
      begin
         var army : Integer = TSquad(pSquad).fArmy;
         if army >= 0 then
         Result := gPlayer[TObj(pObj).pl].aiData.armyList.Get(army);
      end;
   end;
end;

// _unit_CheckTransportOpenTrap
//
procedure _unit_CheckTransportOpenTrap(goHnd : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var pObjProp : Pointer = _unit_GetObjProp(goHnd);
   if (pObj <> nil) and (pObjProp <> nil) and (TObjProp(pObjProp).transport > 0) and (TObj(pObj).orders[0].itype = gc_obj_order_type_move) and (TObj(pObj).orders[0].info.progress = gc_obj_order_move_mode_landing) then
   begin
      // TODO : FAUST : move to write/readmove states to work on client side
      SetGameObjectRayCastIntersectEnabledByHandle(goHnd, True);
      var childHnd : Integer = GetGameObjectGOHandleChildByCustomName(goHnd, 'ferry_ladder');
      SetGameObjectCIMovableByHandle(goHnd, False);
      GameObjectSetFrameAnimationByHandle(childHnd, 'open', false);
      SetGameObjectAnimationModeByHandle(childHnd, 'aamPlayOnce');
      GameObjectDelayExecuteStateByHandle(goHnd, 'SetRallyPoint', gc_unit_opentraptime);

      var pArmy : Pointer = _unit_GetArmy(goHnd);
      if pArmy <> nil then
      begin
         if TArmy(pArmy).fOrder.tag = gc_ai_armyorder_transport_moor then
         TArmy(pArmy).fOrder.tag := gc_ai_armyorder_transport_waitloadunits;
      end;
   end;
end;

// _unit_ShowTags
//
procedure _unit_ShowTags(goHnd : Integer);
begin
   var tag : Integer = GetGameObjectStatesTagByHandle(goHnd);
   var essTag : Integer = (tag and gc_statetag_essential);
   var moveTag : Integer = (tag and gc_statetag_move);
   var actionTag : Integer = (tag and gc_statetag_action);
   var executeTag : Integer = (tag and gc_statetag_execute);
   var weaponTag : Integer = (tag and gc_statetag_weapon);
   var resTag : Integer = (tag and gc_statetag_resource);
   var visualTag : Integer = (tag and gc_statetag_visual);

   var sEssTag, sMoveTag, sActionTag, sExecuteTag, sWeaponTag, sResTag, sVisualTag : String;
   _misc_GetStateTagNameByValue(essTag, sEssTag);
   _misc_GetStateTagNameByValue(moveTag, sMoveTag);
   _misc_GetStateTagNameByValue(actionTag, sActionTag);
   _misc_GetStateTagNameByValue(executeTag, sExecuteTag);
   _misc_GetStateTagNameByValue(weaponTag, sWeaponTag);
   _misc_GetStateTagNameByValue(resTag, sResTag);
   _misc_GetStateTagNameByValue(visualTag, sVisualTag);

   Log('Essential tag = ' + sEssTag);
   Log('Move tag = ' + sMoveTag);
   Log('Action tag = ' + sActionTag);
   Log('Execute tag = ' + sExecuteTag);
   Log('Weapon tag = ' + sWeaponTag);
   Log('Resource tag = ' + sResTag);
   Log('Visual tag = ' + sVisualTag);
end;

// _unit_GetNearestPort
//
function _unit_GetNearestPort(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (pobj<>nil) then
   begin
      var restype : Integer = TObj(pobj).restype;
      var mindist : Float = gc_MaxInt;
      var minHnd : Integer;
      var plHnd : Integer = GetGameObjectPlayerHandleByHandle(goHnd);
      //var posx : Float =
      //var posz : Float =
      var i : Integer;
      for i:=gPlayer[TObj(pobj).pl].lists.ports.GetCount-1 downto 0 do
      begin
         var trgHnd : Integer = gPlayer[TObj(pobj).pl].lists.ports.Get(i);
         var ptrgobj : Pointer = _unit_GetTObj(trgHnd);
         if (ptrgobj<>nil) and (TObj(ptrgobj).bbuilt) then
         begin
            //var dist : Float = VectorDistance(GetGameObjectPositionXByHandle(goHnd), 0, GetGameObjectPositionZByHandle(goHnd), GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
            //if (gObjProp[TObj(ptrgobj).cid][TObj(ptrgobj).id].usage=gc_obj_usage_center) then // center require additional path to drop res
            //dist := dist+2.5;
            var dist : Float = VectorDistance(GetGameObjectPositionXByHandle(goHnd){+gCustomObjPoints[TObj(ptrgobj).cid][TObj(ptrgobj).id].resourcePoint.x}, 0, GetGameObjectPositionZByHandle(goHnd){+gCustomObjPoints[TObj(ptrgobj).cid][TObj(ptrgobj).id].resourcePoint.z}, GetGameObjectPositionXByHandle(trgHnd), 0, GetGameObjectPositionZByHandle(trgHnd));
            if (dist<mindist) then
            begin
               minHnd := trgHnd;
               mindist := dist;
               //mindist := distSqr;
            end;
         end;
      end;
      Result := minHnd;
   end;
end;

// _unit_DoReturnToPort
//
function _unit_DoReturnToPort(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var bDoSearch : Boolean = True;
   var trgHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
   if (trgHnd<>0) and (_misc_GetBaseObjID(trgHnd)=gc_baseid_obj) then
   begin
      var pobj : Pointer = _unit_GetTObj(trgHnd);
      if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].bbuilding) and (not TObj(pobj).bdead) {and
      (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_port)} then
      begin
         bDoSearch := False;
         Result := trgHnd;
      end;
   end;
   if (bDoSearch) then
   begin
      trgHnd := _unit_GetNearestPort(goHnd);
      if (trgHnd<>0) then
      begin
         var pobj : Pointer = _unit_GetTObj(trgHnd);
         if (pobj<>nil) then
         begin
            _unit_SetSTO(goHnd, trgHnd);
         end
         else
         ErrorLog('_unit_DoReturnToStorehouse pobj=nil');
      end;
      Result := trgHnd;
   end;

   if trgHnd <> 0 then
   begin
      var resx, resz : Float;
      _misc_GetPortResourcePoint(trgHnd, resx, resz, True);
      _unit_SetSTP(goHnd, resx, resz);
   end;
end;

//_unit_GetFishBoatMaxCapacity
//
{
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));
}
function _unit_GetFishBoatMaxCapacity(const goHnd : Integer) : Integer;
begin
   Result := 0;
   var plInd : Integer = GetPlayerIndexByHandle(GetGameObjectPlayerHandleByHandle(goHnd));
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (pObj<>nil) then
   Result := gPlayer[plInd].objbase[TObj(pObj).cid][TObj(pObj).id].fishingmax;
end;

// _unit_CheckReturnToPort
//
function _unit_CheckReturnToPort(const goHnd : Integer) : Integer;
begin
   Result := gc_result_trystorehouse_notneeded;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   if (TObj(pobj).resamount>=_unit_GetFishBoatMaxCapacity(goHnd)) then
   begin
      Result := gc_result_trystorehouse_return;
      TObj(pobj).resamount := _unit_GetFishBoatMaxCapacity(goHnd);
      if (_unit_DoReturnToPort(goHnd)=0) then
      Result := gc_result_trystorehouse_notavailable;
   end;
end;

//_unit_GetRandomWaterPosition
//
function _unit_GetRandomWaterPosition(const goHnd : Integer; const radius : Float; var xpos, zpos : Float) : Boolean;
begin
   Result := False;
   var x0 : Float = GetGameObjectPositionXByHandle(goHnd);
   var z0 : Float = GetGameObjectPositionZByHandle(goHnd);
   var i : Integer;
   var pObj : Pointer = _unit_GetTObj(goHnd);
   var plInd : Integer = TObj(pObj).pl;
   For i:=0 to 19 do
   begin
      var angle : Float = random*360;
      var dirx : Float = radius;
      var diry, dirz : Float;
      VectorRotateY(dirx, diry, dirz, angle);
      xpos := x0+dirx;
      zpos := z0+dirz;
      if GetMapCollisionTag(xpos, zpos, false)=gc_collisiontag_water then
      begin
         var gridX, gridY : Integer;
         _misc_PosToScanGridIndices(xPos, zPos, gridX, gridY);
         if _misc_IsCorrectScanCellOwner(plInd, gridX, gridY) then
         begin
            Result := True;
            break;
         end;
      end;
   end;
end;

// _unit_PeasantAddResToPlayerByIndex
//
procedure _unit_FisherAddResToPlayerByIndex(const plInd, goHnd, restype : Integer);
begin
   var amount : Integer = _unit_GetFishBoatMaxCapacity(goHnd);
   _res_AddResToPlayerByIndex(plInd, restype, amount);
   gPlayer[plInd].stat.restotal[restype] := gPlayer[plInd].stat.restotal[restype]+amount;
end;

//_unit_TryFishing
//
function _unit_TryFishing(const goHnd, trgHnd : Integer) : Integer;
begin
   //var profHnd : Integer = _misc_ProfilerBeginHnd('tryer');
   const gc_fisher_radius_move_change = 15.0;
   var res : Integer = gc_result_tryextract_ok;
   var pobj : Pointer = _unit_GetTObj(goHnd);
   var movetag, actiontag, executetag : Integer;

   if (pobj<>nil) and (gObjProp[TObj(pobj).cid][TObj(pobj).id].usage=gc_obj_usage_fisher) and (not TObj(pobj).bdead) then
   begin
      var statetag : Integer = GetGameObjectStatesTagByHandle(goHnd);

      var porderinfo : Pointer = TObj(pobj).orders[0].info;
      var plInd : Integer = GetPlayerIndexByHandle(GetGameObjectPlayerHandleByHandle(goHnd));
      var resReturnToStorehouse : Integer = _unit_CheckReturnToPort(goHnd);
      if (resReturnToStorehouse=gc_result_trystorehouse_notavailable) then
      res := gc_result_tryextract_targetnotvalid;
      if (resReturnToStorehouse=gc_result_trystorehouse_return) then
      begin
         var storehouseHnd : Integer = GetGameObjectSTOHandleByHandle(goHnd);
         if (storehouseHnd<>0) and (GetGameObjectPlayerHandleByHandle(storehouseHnd)=GetGameObjectPlayerHandleByHandle(goHnd)) then
         begin
            var pobjStore : Pointer = _unit_GetTObj(storehouseHnd);
            if (pobjStore<>nil) then
            begin
               var resx, resz : Float;
               _misc_GetPortResourcePoint(storehouseHnd, resx, resz, True);
               var dstSqr : Float = Sqr(GetGameObjectPositionXByHandle(goHnd)-resx)+Sqr(GetGameObjectPositionZByHandle(goHnd)-resz);
               var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
               var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
               const gc_gameplay_resourceDropRadiusSqr = 4*4;
               if (dstSqr<gc_gameplay_resourceDropRadiusSqr) then
               begin
                  _unit_FisherAddResToPlayerByIndex(plInd, goHnd, gc_resource_type_food);
                  //reseting amount and setting new dest point:
                  TObj(pobj).resamount := 0;
                  var trgPosX, trgPosZ : Float;
                  if _unit_GetRandomWaterPosition(goHnd, (0.5+random)*gc_fisher_radius_move_change, trgPosX, trgPosZ) then
                  begin
                     _unit_SetSTO(goHnd, 0);
                     _unit_SetSTP(goHnd, trgPosX, trgPosZ);
                     //TOrderInfo(porderinfo).trg := resHnd;
                     //_unit_SetOrderTrg(goHnd, 0, resHnd, True);
                     TOrderInfo(porderinfo).x := trgPosX;
                     TOrderInfo(porderinfo).y := trgPosZ;
                     movetag := gc_statetag_move_walk;
                     actiontag := gc_statetag_action_none;
                     executetag := gc_statetag_execute_move;
                  end
                  else
                  begin
                     movetag := gc_statetag_move_idle;
                     actiontag := gc_statetag_action_none;
                     executetag := gc_statetag_execute_none;
                     _unit_SetSTO(goHnd, 0);
                  end;
               end
               else
               begin
                  var resx, resz : Float;
                  _misc_GetPortResourcePoint(storehouseHnd, resx, resz, True);
                  var px : Float = resx;
                  var pz : Float = resz;
                  var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
                  var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);
                  if ((statetag and gc_statetag_move_idle)<>0) or ((Sqr(stx-px)+Sqr(stz-pz))>gc_ObjectEpsilonDistSqr) then
                  begin
                     actiontag := gc_statetag_action_none;
                     executetag := gc_statetag_execute_move;
                  end;
               end;
            end;
         end;
      end
      else
      if (resReturnToStorehouse=gc_result_trystorehouse_notneeded) then
      begin
         //keep moving:
         //add food if moving:
         var posx : Float = GetGameObjectPositionXByHandle(goHnd);
         var posz : Float = GetGameObjectPositionZByHandle(goHnd);
         var stx : Float = GetGameObjectStateTargetPositionXByHandle(goHnd);
         var stz : Float = GetGameObjectStateTargetPositionZByHandle(goHnd);

         if (((Sqr(posx-stx)+Sqr(posz-stz))<=gc_ObjectEpsilonDistSqr) or (TObj(pobj).standtime>2)) then
         begin
            //find new point
            var trgPosX, trgPosZ : Float;
            if _unit_GetRandomWaterPosition(goHnd, (0.5+random)*gc_fisher_radius_move_change, trgPosX, trgPosZ) then
            begin
               _unit_SetSTO(goHnd, 0);
               _unit_SetSTP(goHnd, trgPosX, trgPosZ);
               //TOrderInfo(porderinfo).trg := resHnd;
               //_unit_SetOrderTrg(goHnd, 0, resHnd, True);
               TOrderInfo(porderinfo).x := trgPosX;
               TOrderInfo(porderinfo).y := trgPosZ;
               movetag := gc_statetag_move_walk;
               actiontag := gc_statetag_action_none;
               executetag := gc_statetag_execute_move;
            end
            else
            begin
               movetag := gc_statetag_move_idle;
               actiontag := gc_statetag_action_none;
               executetag := gc_statetag_execute_none;
               _unit_SetSTO(goHnd, 0);
            end;
         end
         else
         begin
            //keep moving
            var deltatime : Float = (GetGameTime-TObj(pObj).lastprogresstime);
            var addFish : Float = gPlayer[plInd].objbase[TObj(pObj).cid][TObj(pObj).id].fishingspeed*deltaTime+TObj(pobj).orders[0].info.progress;
            var addFishInt : Integer = Floor(addFish);
            TObj(pobj).resamount := TObj(pobj).resamount+addFishInt;
            TObj(pobj).orders[0].info.progress := addFish-addFishInt;
         end;
      end;
      if (movetag<>0) or (actiontag<>0) or (executetag<>0) then
      _unit_SetTagStates(goHnd, movetag or actiontag or executetag);
   end
   else
   res := gc_result_tryextract_targetnotvalid;
   Result := res;
   //_misc_ProfilerEndHnd(profHnd);
end;

// _unit_TestTransportPosition
//
function _unit_TestTransportPosition(goHnd : Integer; posX, posZ, dirX, dirZ : Float) : Boolean;
begin
   if (goHnd <> 0) then
   begin
      var tag : Integer = GetMapCollisionTag(posX, posZ, false);
      if (tag = gc_collisiontag_water) or (tag = gc_collisiontag_waterborder) then
      begin
         var x, y, z : Float;
         _unit_GetLastExitPoint(goHnd, false, x, y, z);
         var angle : Float = _misc_GetDirAngleToXVector(dirX, dirZ);
         VectorRotateY(x, y, z, angle);
         var eX : Float = posX + x;
         var eZ : Float = posZ + z;
         Result := _misc_TestTransportExitPoint(eX, eZ);
      end
      else
      Result := false;
   end
   else
   Result := false;
end;

// _unit_SetDirection
//
procedure _unit_SetDirection(goHnd : Integer; dirX, dirZ : Float);
begin
   // pay attention that direction is set as up
   var upx, upy, upz, dx, dy, dz : Float;
   GetGameObjectOrientationByHandle(goHnd, upx, upy, upz, dx, dy, dz);
   upx := dirX;
   upz := dirZ;
   VectorNormalize(upx, upy, upz);
   SetGameObjectOrientationByHandle(goHnd, upx, upy, upz, dx, dy, dz);
end;

// _unit_TryExitUnits
//
procedure _unit_TryExitUnits(goHnd : Integer);
begin
   var pObj : Pointer = _unit_GetTObj(goHnd);
   if (pObj <> nil) and (TObj(pObj).exitDelay = 0) then
   begin
      if TObj(pObj).orders[0].info.amount > 0 then
      begin
         var pObjProp : Pointer = _unit_GetObjProp(goHnd);
         var pObjInside : Pointer = _misc_GetObjectArgData(goHnd, gc_argunit_inside);
         if TIntegerList(pObjInside).GetCount > 0 then
         begin
            var hnd : Integer = TIntegerList(pObjInside).Get(0);
            _unit_GoOutside(hnd, false, true);
            TObj(pObj).exitDelay := TObjProp(pObjProp).exitMaxDelay;
            TObj(pObj).orders[0].info.amount := TObj(pObj).orders[0].info.amount - 1;
         end
         else
         TObj(pObj).orders[0].info.amount := 0;

         if TObj(pObj).orders[0].info.amount <= 0 then
         _unit_RemoveOrder(goHnd, 0);
      end;
   end;
end;

//_unit_SetShipDummyOffset
//
procedure _unit_SetShipDummyOffset(goHnd : Integer);
begin
   var basename : String = GetGameObjectBaseNameByHandle(goHnd);
   var offset : Float;
   case basename of
      //settuping ships offset
      'battleship' : begin
         offset := 0.7332;
      end;
      'chaika' : begin
         offset := 0.3142;
      end;
      'ferry' : begin
         offset := 0.4224;
      end;
      'fishboat' : begin
         offset := 0.2376;
      end;
      'frigate' : begin
         offset := 0.6224;
      end;
      'galley' : begin
         offset := 0.3542;
      end;
      'xebec' : begin
         offset := 0;
      end;
      'yacht' : begin
         offset := 0.2625;
      end;
      'yachttur' : begin
         offset := 0.3458;
      end;
   end;
   SetGameObjectPositionByHandle(goHnd, GetGameObjectPositionXByHandle(goHnd), offset, GetGameObjectPositionZByHandle(goHnd));
end;

// _unit_SetTerrainCollision
//
procedure _unit_SetTerrainCollision(goHnd, tag : Integer);
begin
   var i, j : Integer;
   var x : Float = GetGameObjectPositionXByHandle(goHnd);
   var z : Float = GetGameObjectPositionZByHandle(goHnd);

   var w : Integer = GetGameObjectCollisionMaskWidthByHandle(goHnd);
   var h : Integer = GetGameObjectCollisionMaskHeightByHandle(goHnd);

   for i := 0 to h-1 do
   for j := 0 to w-1 do
   if GetGameObjectCollisionMaskValueByHandle(goHnd, i, j) then
   begin
      var colX, colY : Float;
      if w mod 2 = 1 then
      colX := x + (j-(w-1)/2)/2
      else
      colX := x + (j-w/2)/2;

      if h mod 2 = 1 then
      colY := z + (i-(h-1)/2)/2
      else
      colY := z + (i-h/2)/2;

      MapDrawCollision(colX, colY, tag, 0, false);
   end;
end;

// _unit_TooCloseToCollision
//
function _unit_TooCloseToCollision(goHnd : Integer) : Boolean;
begin
   Result := false;
   var i, j, i1, j1 : Integer;
   var goX : Float = GetGameObjectPositionXByHandle(goHnd);
   var goZ : Float = GetGameObjectPositionZByHandle(goHnd);

   var w : Integer = GetGameObjectCollisionMaskWidthByHandle(goHnd);
   var h : Integer = GetGameObjectCollisionMaskHeightByHandle(goHnd);
   var mapW : Integer = GetMapWidth;
   var mapH : Integer = GetMapHeight;

   for i := 0 to h-1 do
   for j := 0 to w-1 do
   if GetGameObjectCollisionMaskValueByHandle(goHnd, i, j) then
   begin
      var colX, colY : Float;
      if w mod 2 = 1 then
      colX := goX + (j-(w-1)/2)/2
      else
      colX := goX + (j-w/2)/2;

      if h mod 2 = 1 then
      colY := goZ + (i-(h-1)/2)/2
      else
      colY := goZ + (i-h/2)/2;

      const rad = 2;
      for i1 := -rad to rad do
      for j1 := -rad to rad do
      begin
         var x : Float = colX + i1/2;
         var y : Float = colY + j1/2;
         if IsInRange(x, -mapW div 2, mapW div 2) and IsInRange(y, -mapH div 2, mapH div 2) then
         begin
            if GetMapCollisionTag(x, y, false) > 0 then
            begin
               Result := true;
               exit;
            end;
         end
         else
         begin
            Result := true;
            exit;
         end;
      end;
   end;
end;

// _unit_VectorDistanceToLastTrackPoint
//
function _unit_VectorDistanceToLastTrackPoint(goHnd : Integer; x, y : Float) : Float;
begin
   Result := 0;
   var tpCount : Integer = GetGameObjectTrackPointCountByHandle(goHnd);
   if (tpCount>0) then
   begin
      var tx, ty, tz : Float;
      GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, tpCount-1, tx, ty, tz);
      Result := VectorDistance(tx, 0, tz, x, 0, y);
   end;
end;

// _unit_VectorDistanceFromSTPToLastTrackPoint
//
function _unit_VectorDistanceFromSTPToLastTrackPoint(goHnd : Integer) : Float;
begin
   Result := 0;
   var tpCount : Integer = GetGameObjectTrackPointCountByHandle(goHnd);
   if (tpCount>0) then
   begin
      var tx, ty, tz : Float;
      GetGameObjectTrackPointCoordsByIndexByHandle(goHnd, tpCount-1, tx, ty, tz);
      Result := VectorDistance(tx, 0, tz, GetGameObjectStateTargetPositionXByHandle(goHnd), 0, GetGameObjectStateTargetPositionZByHandle(goHnd));
   end;
end;

// _unit_WallIsOneRegion
//
function _unit_WallIsOneRegion(wHnd : Integer) : Boolean;
begin
   Result := true;
   if wHnd <> 0 then
   begin
      var pObjProp : Pointer = _unit_GetObjProp(wHnd);
      if (pObjProp <> nil) and TObjProp(pObjProp).bWall then
      begin
         var wx : Float = GetGameObjectPositionXByHandle(wHnd);
         var wz : Float = GetGameObjectPositionZByHandle(wHnd);
         var region : Integer = -1;

         var pObj : Pointer = _unit_GetTObj(wHnd);
         var variation : Integer = TObj(pObj).wallVariation;
         var bCount : Integer = gCustomBuildPointsWall[variation].builderCount;

         var i : Integer;
         for i := 0 to bCount-1 do
         begin
            var bx : Float = gCustomBuildPointsWall[variation].builderPoints[i].x;
            var bz : Float = gCustomBuildPointsWall[variation].builderPoints[i].y;
            var reg : Integer = _misc_GetRegionByPos(wx+bx, wz+bz);

            if reg >= 0 then
            begin
               if region < 0 then
               region := reg
               else
               if region <> reg then
               begin
                  Result := false;
                  break;
               end;
            end;
         end;
      end;
   end;
end;
