/* 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.3 */ #include "..\headers\define.h" //#include "..\headers\command.h" #include "main.h" #define OBJ_DATA_LIGHT_DISTANCE (0x6C) #define OBJ_DATA_LIGHT_INTENSITY (0x70) #define PID_PLAYER (16777216) procedure start; procedure AllowControl(variable pid); procedure SetLight(variable critter, variable int, variable dist); procedure CombatTurn_Handler; procedure GameModeChange_Handler; procedure InventoryMove_Handler; procedure InvenWield_Handler; procedure SetGlobalVar_Handler; variable controlMode, pidList, perksList, lightInt, lightDist, npcControl, displayName, displayNameColor, isShowTag, inControl := false, dudeLightInt, dudeLightDist; procedure AllowControl(variable pid) begin return ((pidList == 0 or scan_array(pidList, pid bwand 0xFFFFFF) != -1) and (party_member_obj(pid))); end procedure SetLight(variable critter, variable lInt, variable lDist) begin if (lInt) then lInt := round((lInt / 65536.0) * 100); // calc percent of intensity obj_set_light_level(critter, lInt, lDist); // TODO: make sfall obj_set_light_level function 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); pid := obj_pid(critter); if (pid == PID_PLAYER and inControl == false) then return; if (pid != PID_PLAYER and controlMode != 1 and AllowControl(pid) == false) then begin if (npcControl) then begin npcControl := 0; call SetLight(dude_obj, dudeLightInt, dudeLightDist); // restore dude light //move_to(dude_obj, dude_tile, dude_elevation); intface_redraw; end //display_msg("Skip control."); return; end if (status == 1) then begin set_dude_obj(critter); //display_msg("Take control of: " + obj_name(critter)); if (critter != real_dude_obj) then begin if (npcControl == 0) then begin dudeLightDist := get_object_data(real_dude_obj, OBJ_DATA_LIGHT_DISTANCE); dudeLightInt := get_object_data(real_dude_obj, OBJ_DATA_LIGHT_INTENSITY); obj_set_light_level(real_dude_obj, 0, 0); // dude off light end npcControl := critter; lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE); lightInt := get_object_data(critter, OBJ_DATA_LIGHT_INTENSITY); // 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; obj_set_light_level(critter, 100, 4); inControl := true; end else if (npcControl) then begin call SetLight(dude_obj, dudeLightInt, dudeLightDist); // restore dude light npcControl := 0; // for dude control intface_redraw; end if (inControl) then begin // check preference setting _combat_highlight if (read_byte(0x56D38C)) then set_outline(critter, OUTLINE_GREY); // TODO: replace read_byte with normal function // 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 begin if (npcControl) then begin call SetLight(critter, lightInt, lightDist); // restore light for controlled NPC end if (isShowTag) then begin hide_iface_tag(displayName); isShowTag := false; end // 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 if (status == 0) then set_dude_obj(0); // w/o redraw interface bar (otherwise it flickers when switching from the player to NPCs) //display_msg("Return control to real dude!"); end if (status < 0) then begin set_dude_obj(real_dude_obj); call SetLight(dude_obj, dudeLightInt, dudeLightDist); end end end procedure GameModeChange_Handler begin if (inControl and (get_game_mode bwand COMBAT) == 0) 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 SetGlobalVar_Handler begin if (npcControl and get_sfall_arg == GVAR_PLAYER_REPUTATION and obj_pid(dude_obj) != PID_PLAYER) then begin variable value := global_var(GVAR_PLAYER_REPUTATION); if (get_sfall_arg > value) then begin set_sfall_arg(1, value); set_sfall_return(value); end 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 else if (controlMode == 1 and GetIniConfig("Debugging", "Enable", 0, "ddraw.ini") == 0) then begin controlMode := 2; 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"); if (len_array(pidList) > 0) then fix_array(pidList); else 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); if (GetConfig("CombatControl", "GainReputation", 0) == 0) then begin register_hook_proc(HOOK_SETGLOBALVAR, SetGlobalVar_Handler); end end else begin exit; end end end