/*

   NPC Combat Control

   Allows to take control of your party member or other NPCs during combat

   NOTE: this script requires compiler from sfall modderspack with -s option
   (short circuit evaluation)

   version 1.1

*/

#include "..\headers\define.h"
//#include "..\headers\command.h"
#include "main.h"

#define OBJ_DATA_LIGHT_DISTANCE     (0x6C)
#define OBJ_DATA_LIGHT_INTENSITY    (0x70)

procedure start;
procedure AllowControl(variable pid);
procedure CombatTurn_Handler;
procedure GameModeChange_Handler;
procedure InventoryMove_Handler;
procedure InvenWield_Handler;

variable
   controlMode,
   noCheckArray,
   pidList, perksList,
   lightInt, lightDist, npcControl,
   displayName, displayNameColor, isShowTag,
   inControl := false;


procedure AllowControl(variable pid) begin
   if (noCheckArray or scan_array(pidList, pid bwand 0xFFFFFF) != -1) and (party_member_obj(pid)) then return true;
   return false;
end

procedure CombatTurn_Handler begin
   variable
      status := get_sfall_arg,
      critter := get_sfall_arg,
      pid, perkID, level;

   //display_msg("Combat Turn: " + status + ", by "  + obj_name(critter) + "/" + critter + ", arg3: " + get_sfall_arg);
   if npcControl then begin
      if lightInt then lightInt := round((lightInt / 65536.0) * 100); // calc percent of intensity
      obj_set_light_level(npcControl, lightInt, lightDist);           // restore light for prev. controlled NPC
   end

   pid := obj_pid(critter);
   if (status == 1 and (AllowControl(pid) or controlMode == 1)) then begin
      set_dude_obj(critter);
      //display_msg("Take control of: " + obj_name(critter));
      if (critter != real_dude_obj) then begin
         if not(npcControl) then obj_set_light_level(real_dude_obj, 0, 0); // dude off light
         npcControl := critter;
         // set perks (only work with 4.1.8+)
         foreach (perkID in perksList) begin
            level := has_trait(TRAIT_PERK, real_dude_obj, perkID);
            if (level) then critter_add_trait(critter, TRAIT_PERK, perkID, level);
         end
         intface_redraw;
         lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE);
         lightInt := get_object_data(critter, OBJ_DATA_LIGHT_INTENSITY);
         obj_set_light_level(critter, 100, 4);
         inControl := true;
      end else if npcControl then begin
         obj_set_light_level(dude_obj, 100, 4); // set dude default light
         npcControl := 0; // dude control
      end
      if inControl then begin
         // check preference setting _combat_highlight
         if (read_byte(0x56D38C)) then set_outline(critter, OUTLINE_GREY);
         // center the screen on the controlled critter/dude and remove roof tiles
         move_to(dude_obj, dude_tile, dude_elevation);
         if (displayName and critter != real_dude_obj) then begin
            set_iface_tag_text(displayName, obj_name(critter), displayNameColor);
            show_iface_tag(displayName);
            isShowTag := true;
         end
      end
   end else if inControl then begin
      // remove perks before switching control (only work with 4.1.8+)
      if (dude_obj != real_dude_obj) then begin
         foreach (perkID in perksList) begin
            level := has_trait(TRAIT_PERK, real_dude_obj, perkID);
            if (level) then critter_rm_trait(critter, TRAIT_PERK, perkID, level);
         end
      end
      if isShowTag then begin
         hide_iface_tag(displayName);
         isShowTag := false;
      end
      if (status < 0 or AllowControl(pid) == false) then begin
         if (dude_obj != real_dude_obj) then begin
            set_dude_obj(real_dude_obj);
            obj_set_light_level(dude_obj, 100, 4);
            npcControl := 0;
         end
      end
   end
end

procedure GameModeChange_Handler begin
   if (inControl and not(get_game_mode BWAND COMBAT)) then begin
      inControl := false;
      npcControl := 0;
      move_to(dude_obj, dude_tile, dude_elevation);
   end
end

procedure InventoryMove_Handler begin
   if (npcControl and get_sfall_arg == 3) then begin // armor slot
      if (obj_pid(dude_obj) == PID_MARCUS or proto_data(obj_pid(dude_obj), cr_body_type) != CR_BODY_BIPED) then begin
         display_msg(message_str_game(GAME_MSG_PROTO, 675));
         set_sfall_return(true);
      end
   end
end

procedure InvenWield_Handler begin
   variable
      critter := get_sfall_arg,
      item  := get_sfall_arg,
      slot  := get_sfall_arg,
      event := get_sfall_arg;

   // Fix weapon duplication when equipping in combat
   if (event and slot == INVEN_TYPE_RIGHT_HAND and AllowControl(obj_pid(critter))) then begin
      set_flags(item, get_flags(item) bwand bwnot(FLAG_LEFT_HAND));
   end
end

procedure start begin
   if (game_loaded and sfall_ver_major >= 4) then begin
      //variable configSection := "CombatControl";

      set_perk_ranks(PERK_gecko_skinning_perk, 1);
      set_perk_level(PERK_gecko_skinning_perk, 999); // prevent it from appearing in the perk selection window

      controlMode := GetConfig("CombatControl", "Mode", 0);
      if (controlMode >= 3) then begin
         if (controlMode == 3) then metarule3(999, 0, 0, 0); // enable feature: order party members to attack a specified target
         controlMode := 0;
      end
      if (controlMode > 0) then begin
         displayName := GetConfig("CombatControl", "DisplayName", 0);

         if displayName then begin
            variable nameTag := add_iface_tag;
            debug_msg("NPC control - added new box: " + nameTag);
            if (nameTag <= 0) then begin // box added?
               variable maxBoxCount := get_ini_setting("ddraw.ini|Misc|BoxBarCount");
               if (maxBoxCount < 0) then maxBoxCount := 9;
               else maxBoxCount += 4;
               if (displayName < 5 or displayName > maxBoxCount) then displayName := 0;
            end else begin
               displayName := nameTag;
            end
            displayNameColor := GetConfig("CombatControl", "DisplayNameColor", 0);
         end

         pidList := GetConfigListInt("CombatControl", "PIDList");
         fix_array(pidList);
         noCheckArray := (len_array(pidList) == 0);

         perksList := GetConfigListInt("CombatControl", "PerksList");
         fix_array(perksList);

         register_hook_proc(HOOK_COMBATTURN, CombatTurn_Handler);
         register_hook_proc(HOOK_GAMEMODECHANGE, GameModeChange_Handler);
         register_hook_proc(HOOK_INVENTORYMOVE, InventoryMove_Handler);
         register_hook_proc(HOOK_INVENWIELD, InvenWield_Handler);
      end
   end
end
