-- ====================================================================== --[[ Personal Adjustable Waypoint & Pins Recording Intel, Notes, or Terrain Yes, I see what I did there, and so do you. -- ====================================================================== Author: Catspaw I post gameplay video on my Youtube channel, Catspaw Adventures: https://www.youtube.com/channel/UCtG8fiWPUZEzWlkUn60btAw Source: https://www.moddb.com/mods/stalker-anomaly/addons/personal-adjustable-waypoint-for-anomaly-151-152-and-gamma Version: 2.1.3 Updated: 20240330 You may alter any part of this mod and do whatever you like with it, just give credit where due. Right-click on any visible map point to set/move/remove a waypoint or map pins. Map pins can be named, renamed, removed, and given custom icons. The waypoint is tracked like a task. Semicolon (default) toggles the current or most recent waypoint on/off. To see map points, hold Slash (default, configurable in MCM) to enter Cartography Mode. In this mode, all terrain smart points are visible. Both the hold and the terrain display are options in MCM. While in Cartography Mode, you can also Ping the map from the context menu, creating a temporary grid of visible mapspots that you can use for more precise placement. Press Apostrophe (default) to drop a Quick Pin at the player's current location. Press Period or Comma (default) to cycle through active pins in the current set, or hold Alt while doing this to cycle sets instead. Credits: HarukaSai, RavenAscendant, Ghen Tuong, demonized, and many others who've assisted in ways big and small. -- ====================================================================== -- SHORTCUTS, FLAGS, AND SYSTEM STUFF (Most of which you probably shouldn't muck with) -- ===================================================================--]] script_version = "2.1.3" release_date = 20240330 local scriptname = "tasks_placeable_waypoints" local logprefix = " " local language = "eng" debuglogs = false -- Controls debug logging if you don't have MCM verbose = true -- Verbose being on won't do anything if debuglogs isn't also enabled, -- but it will ensure that if anyone has to turn on debug logging, -- their logs will contain the maximum amount of info automatically. -- It's noisy and you can turn it off here if you have to. -- ====================================================================== modxml_map_spots_paw.load_me = function() end -- forces DXML mapspots to load -- ====================================================================== function dl(logtext,...) -- Debug logging - to disable, set debuglogs to false if logtext and debuglogs then printf (logprefix..logtext,...) end end function vl(logtext,...) -- Verbose logging - to disable, set either debuglogs or verbose to false if logtext and debuglogs and verbose then dl("[V] "..logtext,...) end end -- ====================================================================== mcm_killswitch = false -- Enabling the killswitch will hard-cutoff any attempt -- to load values from MCM, in case that is causing issues. disable_mcm_updates = false -- This flag will disable the immediate MCM updates that happen -- when changing icons or sets in-game. paw_enabled = true -- setting false will killswitch the entire addon local icoset_changed = nil started = false -- runtime flag waypoint_active = false -- runtime flag waypoint_canceling = false -- runtime flag disable_load_warning = false local load_failed = false -- runtime flag welcome_msg_shown = false -- if you don't have MCM, setting this true will disable the welcome message local get_game_time = game.get_game_time local get_start_time = level.get_start_time local ts = game.translate_string local tm = task_manager.get_task_manager() local psk = utils_data.parse_string_keys local task_info = tm.task_info local floor,ceil = math.floor,math.ceil local pow,sqrt = math.pow,math.sqrt -- ====================================================================== mousewheel_override = false --[[ If set to true, mousewheel_override will skip the autodetection of mousewheel support and force it to be on. This may have bad results if your binaries don't actually support it. Mousewheel support can also be forced on by setting the following MCM config value (not exposed in the menu): pawsys/pawbinds/mwheel_override = true --]] right_click_override = false -- Similarly with right_click_override, but for the on_map_right_click callback mwheel_enabled = false -- managed at runtime mwheel_notify = true -- news tip on state change mwheel_poll_interval = 50 -- interval between checks for mouse wheel input mwheel_next_poll = time_global() local mwheel_exe_ver = 20230701 -- mouse wheel support added to demonized binaries in this version local rclick_exe_ver = 20230922 -- right-click map support added to demonized binaries in this version local game_version = ts("ui_st_game_version") local gamma_modpack = game_version:find("G.A.M.M.A.") -- GAMMA now supports both features (mwheel and rclick) mwheel_avail = mousewheel_override or gamma_modpack or (MODDED_EXES_VERSION and (MODDED_EXES_VERSION >= mwheel_exe_ver)) right_click_avail = right_click_override or gamma_modpack or (MODDED_EXES_VERSION and (MODDED_EXES_VERSION >= rclick_exe_ver)) last_clicked_id = nil -- ====================================================================== unsquish_ratio = 1 local DIK_name = ui_mcm and ui_mcm.dispaly_key or (function() return "" end)-- typo is in the mcm script local default_id = 15797 -- WP defaults to Rookie Village if missing for any reason to avoid crash local mcm_update_throttle = 3019 -- minimum time between MCM updates of icon/set data local next_mcm_update = time_global() local wp_proxcheck_interval = 837 -- Time between checks for waypoint proximity local next_wp_proxcheck = time_global() local garbcollect_interval = 4984 -- Time between pin/sz garbage collection checks local next_gc_check = time_global() local wp_clear_dist = 5 local widget_hide_delay = 5000 local next_pin = time_global() local pin_delay = 1000 -- timeout to prevent dupe actions from registering local ping_lifetime = 10000 local ping_grid_size = 10 local ping_grid_radius = 10 --local max_dynamic_faves = 5 -- not yet implemented local current_active_set = "pins" local active_theme = "classicauto" local current_set_max = 1 local current_ico_max = 1 placed_waypoint = nil last_waypoint = nil -- ====================================================================== local toggle_bind = DIK_keys.DIK_SEMICOLON local toggle_mod = 2 local quickpin_bind = DIK_keys.DIK_APOSTROPHE local quickpin_mod = 0 local cart_mode_hold = DIK_keys.DIK_SLASH local cart_mode_mod = 0 local mcm_keybinds = ui_mcm and ui_mcm.key_hold cart_mode = false cartography_must_hold = false cart_shows_smarts = true cartmode_unfade = true tip_on_icoset_change = false allow_non_wp_targets = false local pin_tooltip_mode = 2 -- ====================================================================== enable_wp_proxcheck = true show_pins = true local script_zone_changed = false -- runtime flag local custom_task_info = false local icon_cycle_active = false local set_cycle_active = false local psw_sidebar_state = true widget_enabled = true widget_active = true hide_widget = true widget_use_custom_pos = false widget_custom_pos = {x=491,y=670} widget_last_used = time_global() wp_hud_icon_enabled = true pin_hud_icon_default = true manual_smart_pins = false mark_on_positive_id = true reticle_mustzoom = true reticle_mode = 2 autotag_mode = 0 autotag_persistence = false -- if enabled, autotags are cleared on map change or the below timeout value autotag_lifetime = 120 * 6 -- time in ms before autotags are cleared (def 2 minutes) autotags_time_out = autotag_lifetime > 0 clear_pin_on_death = true -- when set on a living thing, pin will be cleared if it dies pin_near_fade_dist = 5 -- min distance within which pins will begin to fade out, set to 0 to disable pin_far_fade_dist = 100 -- distance beyond which pins will begin to fade out, set to 0 to disable pin_far_hide_dist = 150 -- max distance after far_fade at which pins will hide, set to 0 to disable wp_near_fade_dist = 5 -- same but for waypoints wp_far_fade_dist = 100 -- same but for waypoints wp_far_hide_dist = 150 -- same but for waypoints ret_fade_attack_time = 400 ret_fade_decay_time = 600 reticle_color = { ["a"] = 150, ["r"] = 255, ["g"] = 255, ["b"] = 255, } show_marker_dist = { ["pins"] = 4, ["wp"] = 4, } show_marker_hint = { ["pins"] = 2, ["wp"] = 0, -- not implemented yet } -- ====================================================================== -- STRINGS AND LTX DATA -- ====================================================================== local cartmode = "cartmode" local sep = "_" local snd_path = "catspaw\\" local snd_ping = snd_path.."paw_ping" local snd_cycle_blip = snd_path.."paw_blip" local snd_tag_target = snd_path.."paw_blip" local snd_place_pin = snd_path.."paw_pin" local snd_note = "device\\pda\\pda_note" local use_ping_snd = true local use_ui_snd = true local add_mapspot_functor = scriptname..".func_add_mapspot" local ren_mapspot_functor = scriptname..".func_ren_mapspot" local ren_wp_functor = scriptname..".func_ren_waypoint" local task_id = "task_placeable_waypoint" local pawmenu_path = "ui_mcm_pawsys_pawmenu_" local hint_path = "ui_paw_hint_" -- Don't change any of these icon definitions. If you want to -- override PAW's pin icons with any of yours, use MCM or -- see below for how to add your own, it's easy. current_body_icon = nil body_icon_mode = "off" patch_res = "badge" local custom_pin_icon = false use_custom_backpack_icon = false -- Customize the icon for player stashes init_backpack_as_pin = true -- Player stash icons are created as pins local default_mapspot = "paw_pin_redpush32" local smart_terrain_icon = "paw_pin_magnifier32" local icon_pingspot = "paw_ping_alpha50" waypoint_mapspot = "paw_task_default" map_pin_icon = default_mapspot force_icon_override = nil -- If force_icon_override is set to anything, PAW will try to use it -- for pins instead of what it normally would. -- This is purely a debugging tool. There are better ways to use your own -- custom icons. -- ====================================================================== icons_ini = ini_file_ex("scripts\\paw\\icons.ltx") icon_sets_ini = ini_file_ex("scripts\\paw\\icon_sets.ltx") actions_ini = ini_file_ex("scripts\\paw\\menu_actions.ltx") task_ini = ini_file_ex("scripts\\paw\\task_config.ltx") clsids_ini = ini_file_ex("scripts\\paw\\valid_clsids.ltx") icon_ltx = icons_ini:collect_section("icons") iconset_ltx = icon_sets_ini:get_sections(true) actions_ltx = actions_ini:get_sections(true) clsids_ltx = clsids_ini:collect_section("valid_clsids") task_cfg = task_ini:collect_section("task_config") custom_name = (task_cfg and task_cfg.name) custom_desc = (task_cfg and task_cfg.desc) icons = icon_ltx curr_menu_options = {} icon_sets = {} action_codes = {} action = {} texture_data = {} icon_index = {} set_index = {} valid_clsids = {} map_labels = {} waypoint_history = {} -- not implemented yet local script_zones = {} local pings = {} local pawdata = { pins = {}, --dynamic_faves = {}, curr_set_ind = 1, curr_ico_ind = 1, curr_set_name = "pins", curr_ico_name = default_mapspot, } pawdata.smart_pins = { ["human_f"] = { pin = "chevron_friendly", enabled = false, }, ["human_n"] = { pin = "chevron_neutral", enabled = false, }, ["human_e"] = { pin = "chevron_enemy", enabled = true, }, ["monster"] = { pin = "chevron_enemy", enabled = true, }, ["stash"] = { pin = "bwhr_loot", enabled = false, }, } pawdata.npc_ident = { delay_id = true, id_bodies = false, id_speed = 0.3, lenience = 0.99, tick_speed = 200, progress = {}, } pawdata.wphist = {} waypoint_history = pawdata.wphist npc_ident = pawdata.npc_ident smart_pins = pawdata.smart_pins pins = pawdata.pins dynamic_faves = pawdata.dynamic_faves -- not implemented yet -- ====================================================================== pda_defs = { -- By default, all PDAs support waypointing and autotag ["device_pda_1"] = { show_w = true, autotag = true, }, ["device_pda_2"] = { show_w = true, autotag = true, }, ["device_pda_3"] = { show_w = true, autotag = true, }, ["device_pda_milspec"] = { show_w = true, autotag = true, }, -- If you use an addon with an unsupported PDA section, add it here -- ["device_pda_added_by_your_mod"] = { -- show_w = false, -- autotag = false, -- }, } local default_stash_icon_basic = "treasure" local default_stash_icon_rare = "treasure_searched" local default_stash_icon_isg = "treasure_unique" local default_stash_icon_player = "paw_stash_backpack" stash_icons = { ["basic"] = axr_main.config:r_value("mcm", "pawsys/custom_stash_icon_basic", {val=0}) or default_stash_icon_basic, -- Normal stashes ["rare"] = axr_main.config:r_value("mcm", "pawsys/custom_stash_icon_rare", {val=0}) or default_stash_icon_rare, -- Rare stashes ["isg"] = axr_main.config:r_value("mcm", "pawsys/custom_stash_icon_isg", {val=0}) or default_stash_icon_isg, -- Special (e.g. ISG) stashes ["player"] = axr_main.config:r_value("mcm", "pawsys/custom_stash_icon_player", {val=0}) or default_stash_icon_player, -- Custom player backpack stashes } managed_factions = { -- editing this may cause crashes or other unexpected results ["army"] = true, ["bandit"] = true, ["csky"] = true, ["dolg"] = true, ["ecolog"] = true, ["freedom"] = true, ["killer"] = true, ["monolith"] = true, ["stalker"] = true, ["greh"] = true, ["isg"] = true, ["renegade"] = true, } managed_options = { -- editing this may cause context menus to behave oddly ["mping"] = true, ["wp_set"] = true, ["wp_mov"] = true, ["wp_del"] = true, ["waypoint_rename"] = true, -- deprecated, included here so it doesn't appear if someone re-adds it ["waypoint_redesc"] = true, -- deprecated ["hud_vis_on"] = true, ["hud_vis_off"] = true, ["show_all_pins"] = true, ["hide_all_pins"] = true, ["show_label"] = true, ["hide_label"] = true, ["pn_add"] = true, ["pn_del"] = true, ["pn_ren"] = true, -- deprecated ["pn_clr"] = true, ["pn_settings"] = true, ["wp_settings"] = true, ["lock_pin"] = true, ["unlock_pin"] = true, ["convert_to_pin"] = true, ["cm_dbg"] = true, } hud_themes = { ["classicauto"] = { name = "classicauto", node = "pawhudind", style = "full", tex = "ui_paw_hud_indicator_classic", file = "paw_ui_elements.xml", pos = {x=890,y=620}, w = 136, h = 75, pda_thm = "vertright", }, ["classic"] = { name = "classic", node = "pawhudind", style = "full", tex = "ui_paw_hud_indicator_classic", file = "paw_ui_elements.xml", pos = {x=890,y=620}, w = 136, h = 75, }, ["vertright"] = { name = "vertright", node = "pawhudind_vertright", style = "minimal", tex = "ui_paw_hud_indicator_vertright", file = "paw_ui_elements.xml", pos = {x=922,y=440}, w = 100, h = 154, }, ["vertleft"] = { name = "vertleft", node = "pawhudind_vertleft", style = "minimal", tex = "ui_paw_hud_indicator_vertleft", file = "paw_ui_elements.xml", pos = {x=4,y=570}, w = 100, h = 154, }, ["gamma_right"] = { name = "gamma_right", node = "pawhudind_gamma_right", style = "full", tex = "ui_paw_hud_indicator_gamma_right", file = "paw_ui_elements.xml", pos = {x=888,y=440}, w = 130, h = 150, }, ["gamma_left"] = { name = "gamma_left", node = "pawhudind_gamma_left", style = "full", tex = "ui_paw_hud_indicator_gamma_left", file = "paw_ui_elements.xml", pos = {x=4,y=440}, w = 130, h = 150, }, ["minimal_h"] = { name = "minimal_h", node = "pawhudind_minimal_h", style = "minimal", tex = "ui_paw_hud_indicator_minimal_h", file = "paw_ui_elements.xml", pos = {x=924,y=645}, w = 94, h = 52, }, ["minimal_v"] = { name = "minimal_v", node = "pawhudind_minimal_v", style = "minimal", tex = "ui_paw_hud_indicator_minimal_v", file = "paw_ui_elements.xml", pos = {x=924,y=440}, w = 92, h = 112, }, ["minicom"] = { name = "minicom", node = "pawhudind_minicom", style = "compact", tex = "ui_paw_hud_indicator_minicom", file = "paw_ui_elements.xml", pos = {x=820,y=725}, w = 38, h = 37, }, ["compact_noui"] = { name = "compact_noui", node = "pawhudind_minicom", style = "compact", file = "paw_ui_elements.xml", pos = {x=820,y=725}, w = 32, h = 37, pre_tex = "ui_paw_hud_indicator_minicom", }, ["pin_sidebar"] = { name = "pin_sidebar", node = "pawhudind_pin_sidebar", style = "full", tex = "ui_paw_dialog_sidebar", file = "paw_ui_elements.xml", pos = {x=0,y=0}, w = 161.25, h = 188, }, } colors = { dimmed = GetARGB(100, 255, 255, 255), active = GetARGB(255, 255, 255, 255), } text_colors = { ["clr_255"] = "%" .. "%c[255,255,255,255]", ["clr_red"] = "%" .. "%c[255,255,0,0]", ["clr_orn"] = "%" .. "%c[255,255,140,0]", ["clr_grn"] = "%" .. "%c[255,0,255,0]", ["clr_blu"] = "%" .. "%c[255,0,0,255]", ["clr_lbl"] = "%" .. "%c[255,0,200,220]", ["clr_yel"] = "%" .. "%c[255,250,218,94]", ["clr_wht"] = "%" .. "%c[255,220,220,220]", ["clr_prp"] = "%" .. "%c[255,138,43,226]", ["clr_blk"] = "%" .. "%c[255,0,0,0]", } null_tokens = { ["clr_255"] = "", ["clr_red"] = "", ["clr_grn"] = "", ["clr_blu"] = "", ["clr_lbl"] = "", ["clr_yel"] = "", ["clr_wht"] = "", ["clr_prp"] = "", ["clr_blk"] = "", } local keybinds = { ["cartmode"] = { enabled = function() return paw_enabled end, hold = cartography_must_hold, bind = DIK_keys.DIK_SLASH, name = "ui_mcm_pawsys_pawbinds_bind_cartmode", action = function(tf) if tf == nil then tf = not cart_mode end cartography_mode(tf) end, }, ["wp_target_obj"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_SEMICOLON, mod = 3, name = "ui_mcm_pawsys_pawbinds_bind_wp_target", action = function() wp_target_obj() end, }, ["pin_target_obj"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_APOSTROPHE, mod = 3, name = "ui_mcm_pawsys_pawbinds_bind_pin_target", action = function() pin_target_obj(nil,nil,nil,manual_smart_pins) end, }, ["quickpin"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_APOSTROPHE, name = "ui_mcm_pawsys_pawbinds_bind_quickpin", action = function() drop_quick_pin() end, }, ["wptoggle"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_SEMICOLON, name = "ui_mcm_pawsys_pawbinds_bind_wptoggle", action = function() toggle_waypoint() end, }, ["set_next"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_PERIOD, mod = 3, name = "ui_mcm_pawsys_pawbinds_bind_set_next", action = function() cycle_items(1,"set",tip_on_icoset_change) end, }, ["set_prev"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_COMMA, mod = 3, name = "ui_mcm_pawsys_pawbinds_bind_set_prev", action = function() cycle_items(-1,"set",tip_on_icoset_change) end, }, ["ico_next"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_PERIOD, name = "ui_mcm_pawsys_pawbinds_bind_ico_next", action = function() cycle_items(1,"icon",tip_on_icoset_change) end, }, ["ico_prev"] = { enabled = function() return paw_enabled end, bind = DIK_keys.DIK_COMMA, name = "ui_mcm_pawsys_pawbinds_bind_ico_prev", action = function() cycle_items(-1,"icon",tip_on_icoset_change) end, }, ["ico_scroll"] = { enabled = function() return paw_enabled and mwheel_enabled end, bind = DIK_keys.DIK_Z, name = "ui_mcm_pawsys_pawbinds_bind_ico_scroll", mod = 3, hold = true, action = function(tf) icon_cycle(true,tf) end, }, ["set_scroll"] = { enabled = function() return paw_enabled and mwheel_enabled end, bind = DIK_keys.DIK_Z, name = "ui_mcm_pawsys_pawbinds_bind_set_scroll", mod = 2, hold = true, action = function(tf) set_cycle(true,tf) end, }, } local hover_elements = { ["btn_iprev"] = "icon", ["btn_inext"] = "icon", ["btn_sprev"] = "set", ["btn_snext"] = "set", ["logo_set"] = "set", ["box_curr"] = "icon", ["box_prev"] = "icon", ["box_next"] = "icon", ["box_text"] = "set", ["box_text2"] = "icon", ["btn_apply"] = "icon", } -- ====================================================================== -- GENERAL UTILITY FUNCTIONS -- ====================================================================== function exec(str,...) if str then str = str_explode(str,"%.") if str[1] and str[2] and _G[ str[1] ] and _G[ str[1] ][ str[2] ] then _G[ str[1] ][ str[2] ](...) else dl("Could not exec function %s", str) end end end function delay_exec_of(func,secs,...) if not func then return end secs = secs or 0 CreateTimeEvent("pawdelay","paw"..tostring(time_global()),secs,func,...) end function dotip(tiptext,dur,src,beep,icon,snd) vl("Tip call received: dur %s | src \"%s\" | beep %s\n\"%s\"",dur,src,beep,tiptext) if tiptext == nil then return end db.actor:give_game_news(src or "PAW System", tiptext, icon or "ui_inGame2_Mesta_evakuatsii", 0, dur or 5000) if beep then xr_sound.set_sound_play(AC_ID, snd or "pda_tips") end end function get_time_elapsed() return math.floor(get_game_time():diffSec(get_start_time())) end function round2(n) return (math.floor(n * 100) / 100) end function roll(val,min,max) if not (val and min and max) then return end if val > max then return min elseif val < min then return max else return val end end function lerp(a, b, f) if a and b and f then return a + f * (b - a) else return a or b or 0 end end function paw_enabled_check() if not paw_enabled then actor_menu.set_msg(1,game.translate_string("st_paw_system_disabled"),5) end return paw_enabled end function valid_bind(kb) return (kb ~= nil) and (kb ~= "") and (kb ~= "") and (kb ~= -1) end function bindtext(kb,km) local kbt = "" local kmt = "" local mt = "" local vbkb = valid_bind(kb) local vbkm = km and km > 0 if vbkb then kbt = DIK_name(kb) end if vbkm then if km and km < 29 then km = ((km == 1) and 42) or ((km == 2) and 29) or ((km == 3) and 56) end if km then kmt = DIK_name(km) end end if vbkb and vbkm then mt = "+" elseif not (vbkb or vbkm) then kmt = "[UNBOUND]" mt = "" kbt = "" end return string.format("%s%s%s",kmt,mt,kbt) end -- ====================================================================== function safeid(obj) local id = obj and obj.id if not id and obj then return end if type(id) ~= "number" then id = obj:id() end return id end function safename(id) if not (id and (id > 0)) then return end local text = ts("st_paw_unknown") local se_obj = alife_object(id) if not se_obj then return text end local name = se_obj:name() or text local cls = se_obj:clsid() if pins[id] and not script_zones[id] then -- If there's a pin for this ID, use its text above all else text = pins[id].name vl("%s (%s) is a pin: %s",name,id,text) elseif (cls == clsid.script_zone) then text = ts("st_paw_pin_default_name") vl("%s (%s) is a script zone: %s",name,id,text) elseif (cls == clsid.script_restr) then local _,_,hint = txr_routes.get_route_info(name) if hint then text = hint and ts(hint) or text else text = ts(name) end vl("%s (%s) is a level transition: %s",name,id,text) elseif (cls == clsid.smart_terrain) then text = ts("st_"..name.."_name"):gsub("%."," -") vl("%s (%s) is a smart terrain: %s",name,id,text) elseif (cls == clsid.online_offline_group_s) then local comm = se_obj.player_id local sq = ts("st_paw_squad") if managed_factions[comm] then text = ts(comm) end text = text.." "..sq if debuglogs then text = text..": "..name end vl("%s (%s) is NPC squad with faction %s: %s",name,id,comm,text) elseif IsInvbox(nil,cls) then local locst = "st_"..name.."_name" text = ts(locst) if text == locst then dl("No localization found for stash %s",locst) text = ts("st_paw_unknown").." "..ts("st_paw_stash") if debuglogs then text = text..": "..name end end vl("%s (%s) is a stash marker: %s",name,id,text) elseif IsStalker(nil,cls) then text = se_obj:character_name() vl("%s (%s) is a stalker: %s",name,id,text) elseif IsMonster(nil,cls) then local obj = get_object_by_id(id) local section = obj and obj:section() if not section then return text end --[[ Hack adapted from ui_enemy_health.script This logic acts as a translation layer for any monster kind/species that doesn't line up with the name of its encyclopedia entry --]] local special_cases = { dog = "blind_dog", bird = "crow", giant = "pseudogiant", SM_KARLIK = "karlik", SM_LURKER = "lurker", SM_POLTER_G = "poltergeist", SM_PYRO_G = "pyrogeist", SM_PSEUDO_G = "pseudogeist", SM_PSYSUCKER = "psysucker", } if string.find(section,'rotan') then text = "rat" elseif string.find(section,'psy_dog') then text = "psydog" else local what = ini_sys:r_string_ex(section,"kind") or ini_sys:r_string_ex(section,"species") or nil text = special_cases[what] or what end --text = string.lower(ts("encyclopedia_mutants_"..text)) text = ts("encyclopedia_mutants_"..text) vl("%s (%s) is a creature: %s",name,id,text) else text = ts(name) if text == name then text = ts(name.."_name") if text == name.."_name" then text = name dl("No localization or other name found for object %s (%s)",name,id) end end end dl("safename returns %s for %s",text,name) return text end function valid_pda(feature) feature = feature or "show_w" local dev = db.actor and db.actor:item_in_slot(8) local sec = dev and dev:section() if not sec then return false end if dev and pda_defs[sec] then if pda_defs[sec][feature] then return true end elseif item_device.device_npc_pda[sec] then local text = ts("st_paw_npc_pda_equipped") dotip(text) else dl("WARNING: Equipped PDA %s not found in pda_defs - if this is a valid player PDA, add to that table to enable support",sec) end return false end function blacklisted_object(id) if not (id and (id > 0)) then return true end local se_obj = alife_object(id) if not se_obj then return true end local cls = se_obj:clsid() if (cls == clsid.poltergeist_s) or (cls == clsid.psy_dog_phantom_s) then return true end end function valid_waypoint_target(se_obj) if not se_obj then return end if blacklisted_object(se_obj.id) then return end -- can add other smart types to this logic later if needed local clsid = se_obj:clsid() local valid = (valid_clsids[clsid] ~= nil) or IsStalker(se_obj) or IsMonster(se_obj) or isLc(se_obj) return valid end function feature_valid_in_mode(mode) -- -1: debug mode only -- 0: disabled -- 1: enabled in normal view only -- 2: enabled in cartography mode only -- 4: enabled in both return ((mode < 0) and debuglogs) or ((mode == 1) and not cart_mode) or ((mode >= 2) and cart_mode) or (mode >= 4) end function play_sound_for_actor(effect) local snd = xr_sound.get_safe_sound_object(effect) snd:play(db.actor, 0, sound_object.s2d) end function play_ui_sound(effect) if effect and use_ui_snd then play_sound_for_actor(effect) end end function add_mapspot(text,id,icon) if not id then return end icon = icon or default_mapspot text = text or get_tooltip_for_pin(id) or "" vl("Adding mapspot %s for id %s with icon %s",text,id,icon) level.map_add_object_spot(id,icon,text) if not pins[id] then return end local pin = pins[id] if pin.custom_colors and pin.colors then local clr = pin.colors local spot = level.map_get_object_spot_static(id,icon) spot:SetTextureColor(GetARGB(clr.a, clr.r, clr.g, clr.b)) end if pin.hud then show_hud_pin(id) end if pin.show_label then show_pin_label(id,true) end return true end function remove_mapspot(id,icon) if not (id and icon) then return end vl("Trying to remove mapspot for id %s with icon %s",id,icon) local pin = pins[id] if pin and pin.show_label then show_pin_label(id,false,true) end if pin and pin.hud then hide_hud_pin(id) end level.map_remove_object_spot(id,icon) level.map_remove_object_spot(id,icon) -- this operation sometimes doesn't work the first time return true end function change_mapspot(text,id,icon,old_icon) if not id then return end text = text or get_tooltip_for_pin(id) icon = icon or (pins and pins[id] and pins[id].icon) or default_mapspot old_icon = old_icon or icon vl("Trying to change mapspot with id %s from icon %s to icon %s with text %s",id,old_icon,icon,text) remove_mapspot(id,old_icon) return add_mapspot(text,id,icon) end function get_tooltip_for_pin(id) local pin = pins and pins[id] local text = "" if pin then local mode = pin.tooltip or pin_tooltip_mode if mode > 1 then text = pin.text or pin.name or ts("st_paw_pin_default_name") elseif mode > 0 then text = pin.name or ts("st_paw_pin_default_name") end vl("get_tooltip_for_pin(%s): tooltip mode %s\n* Returning hint: \"%s\"",id,mode,text) return text end end function has_mapspot(id,icon) return level.map_has_object_spot(id,icon) == 1 end function get_mapspot(id,icon) local found if icon then return has_mapspot(id,icon) and icon else for k,v in pairs(stash_icons) do local has_spot = has_mapspot(id,v) --printf("id %s has mapspot %s: %s",id,v,has_spot) if has_spot then found = v --printf("found %s",found) --return v end end end return found end function set_mapspot_colors(id,icon,a,r,g,b) if not (id and icon and a and r and g and b) then return end local pin = pins[id] if pin and pin.colors then local clr = pin.colors local spot = level.map_get_object_spot_static(id,icon) if spot then spot:SetTextureColor(GetARGB(clr.a, clr.r, clr.g, clr.b)) end end end function remove_stash_mapspots(id) level.map_remove_object_spot(id,"treasure") level.map_remove_object_spot(id,"treasure") for _,mapspot in pairs(stash_icons) do dl("Attempting to remove stash mapspot \"%s\" for ID %s",mapspot,id) level.map_remove_object_spot(id,mapspot) level.map_remove_object_spot(id,mapspot) end end function func_ren_waypoint(text,args) if text == "" then text = nil end local field = args.field if field == "name" then custom_name = text elseif field == "desc" then custom_desc = text end end function pins_exist() return not is_empty(pins) end function pin_exists(id) return pins[id] and true or false end function can_convert_to_pin(id) if not (id and (id > 0) and (id < 65535)) then return end local se_obj = alife_object(id) if not se_obj then return end local cls = se_obj:clsid() local is_stash = IsInvbox(nil,cls) local mapspot = get_mapspot(id) local is_pin = pin_exists(id) local can_convert = mapspot and is_stash and not is_pin vl("can_convert_to_pin(%s): is_stash: %s | is a pin: %s | mapspot: %s | can_convert = %s",id,is_stash,is_pin,mapspot,can_convert) return can_convert end function cancel_task() local id = placed_waypoint and placed_waypoint.id tm.task_info[task_id] = nil if id then remove_mapspot(id, waypoint_mapspot) set_pingspot_persistence(id,false) end waypoint_active = false placed_waypoint = nil dl("Waypoint task cancellation complete for %s",id) waypoint_canceling = false return true end function end_waypoint_task() waypoint_canceling = true return cancel_task() end function set_current_waypoint(se_obj) if not se_obj then return end local id = se_obj.id set_pingspot_persistence(id) if waypoint_active then last_waypoint = placed_waypoint end placed_waypoint = { id = id, name = safename(id), } waypoint_active = true waypoint_canceling = false end function valid_travel_target(id) -- To enable interaction with the Fair Fast Travel -- system addon, allowing fast travel to pins, but -- only those that we know are within the playable -- area--ones spawned at a spot where the player was -- standing at the time. local pin = pins and id and pins[id] local valid = pin and pin.quick vl("valid_travel_target | id %s is pin: %s | is quick pin %s",id,pin,valid) return valid end function init_new_pin(id,name,text,icon) if id and not pins[id] then icon = icon or default_mapspot set_pingspot_persistence(id) pins[id] = { id = id, name = name or ts("st_paw_pin_default_name"), text = text, icon = icon, hud = false } --rotate_dynamic_favorites(icon) vl("New pin initialized: %s (%s) | %s | %s",pins[id].name,id,icon,text) end end function register_script_zone(...) local se_obj = alife_create("script_zone",...) if not se_obj then return end local id = se_obj.id local now = time_global() script_zones[id] = { id = se_obj.id, pos = se_obj.position, created = now, cleanup = now + ping_lifetime, } script_zone_changed = true vl("Script zone %s spawned for mapspot",id) return se_obj end function deregister_script_zone(id) if not id then return end local se_obj = alife_object(id) if not se_obj then return end alife_release(se_obj) script_zones[id] = nil script_zone_changed = true vl("Script zone %s released",id) end function generate_scripted_mapspot_at(pos,lv,gv) if not (pos and lv and gv) then return end local sz = register_script_zone(pos,gv,lv) if not sz then return end return sz.id end function generate_scripted_mapspot_on(obj) if not (obj and obj.id) then return end local sz = register_script_zone(obj) if not sz then return end return sz.id end function spawn_pingspot(pid,pos,lv,gv) if not (pid and pos and lv and gv) then return end local sz = register_script_zone(pos,gv,lv) if not sz then return end local text = ts("st_paw_pingspot") local id = sz.id pings[id] = script_zones[id] pings[id].pid = pid add_mapspot(text,id,icon_pingspot) -- vl("Spawned temporary pingspot at %s,%s with id %s",pos.x,pos.z,id) return id end function despawn_pingspot(id) -- vl("despawn_pingspot(%s): deregistering script zone and nulling ping record",id) deregister_script_zone(id) pings[id] = nil end function script_zone_cleanup() local now = get_time_elapsed() for k,v in pairs(script_zones) do if v.cleanup and (v.cleanup < now) then deregister_script_zone(k) end end script_zone_changed = false dl("script_zone_cleanup completed") return true end function temp_pin_cleanup(wipe_all) local now = get_time_elapsed() for k,v in pairs(pins) do if v.cleanup then local expired = autotags_time_out and (v.cleanup < now) if expired or wipe_all then do_paw_action("pn_del",nil,nil,{syscall=true,id=k}) end end end dl("temp_pin_cleanup completed") return true end function set_pin_persistence(id,onoff,lifetime) if not (id and (id > 0)) then return end if not pins[id] then return end if onoff or (onoff == nil) then pins[id].cleanup = nil dl("Pin %s given persistence, it will not be subject to cleanup",id) else local now = get_time_elapsed() pins[id].cleanup = now + (lifetime or autotag_lifetime or 0) dl("Pin %s flagged as temporary, it will be subject to cleanup at %s",id,pins[id].cleanup) script_zone_changed = true end return true end function set_pingspot_persistence(id,onoff) if not (id and script_zones[id]) then return end if onoff or (onoff == nil) then pings[id] = nil script_zones[id].cleanup = nil dl("Temporary pingspot %s given persistence, it will not be subject to cleanup",id) else script_zones[id].cleanup = get_time_elapsed() dl("Persistent pingspot %s flagged as unused, it will be subject to cleanup",id) script_zone_changed = true end return true end function ping_area_around(obj) if not obj then return end local now = time_global() local pid = tostring(now) local span = ping_grid_radius or 10 -- radius of grid in squares local grid = ping_grid_size or 10 -- dimension of grid squares in game units local rad = span * grid -- actual ping radius in game units local anch_id = obj.id local anch_pos = obj.position local ax = anch_pos.x local ay = anch_pos.y local az = anch_pos.z local gv = obj.m_game_vertex_id local lv = game_graph():vertex(gv):level_id() for z = (az-(span*grid)),(az+(span*grid)),grid do for x = (ax-(span*grid)),(ax+(span*grid)),grid do local pos = vector():set(x,ay,z) if distance_2d(pos,anch_pos) <= (rad + 1) then spawn_pingspot(pid,pos,gv,lv) end end end if use_ping_snd and snd_ping then play_ui_sound(snd_ping) end CreateTimeEvent("paw_ping_cleanup","ping_"..pid,11,tasks_placeable_waypoints.ping_cleanup,pid) end function init_mapspot_label_wnd(mapspot_text,id,icon,anchor) vl("init_mapspot_label_wnd(%s,%s,%s)",mapspot_text,id,icon) mapspot_text = mapspot_text or "" local anchor = anchor or level.map_get_object_spot_static(id,icon) local xml = CScriptXmlInit() local xmlroot = "paw_ui_wnd_box" local frm = xmlroot..":frm_" xml:ParseFile("paw_ui_elements.xml") local cap = {} cap.id = id cap.box = xml:InitStatic(xmlroot,anchor) local tokens = 0 if string.find(mapspot_text,"$") then local levelname = ts(level.name()) local se_obj = alife_object(id) local pos = se_obj and se_obj.position local coord_x,coord_y,coord_z,coords if pos then coord_x = string.format("%.2f",pos.x) coord_y = string.format("%.2f",pos.y) coord_z = string.format("%.2f",pos.z) --coords = string.format("XYZ %s, %s, %s",coord_x,coord_y,coord_z) end local token_subs = { ["obj_id"] = id, ["name"] = safename(id), --["level"] = levelname,-- currently wrong --["coords"] = coords, -- XYZ coordinates formatted as above ["coord_x"] = coord_x, -- X coordinate to 2 decimal places ["coord_y"] = coord_y, -- Y coordinate to 2 decimal places ["coord_z"] = coord_z, -- Z coordinate to 2 decimal places } mapspot_text = psk(mapspot_text,token_subs) _,tokens = mapspot_text:gsub("%$","%$") plaintext = psk(mapspot_text,null_tokens) mapspot_text = psk(mapspot_text,text_colors) end local token_adj = tokens * 17 _, nlcnt = mapspot_text:gsub("\\n", "\\n") local w = clamp(#mapspot_text * 6,15,200) local h = ((floor(clamp((#mapspot_text - token_adj) / 35,1,3)) + nlcnt) * 15) + 4 cap.box:SetWndPos(vector2():set(15,-h)) cap.drop = xml:InitStatic(xmlroot..":min_full",cap.box) cap.drop:SetWndSize(vector2():set(w,h)) cap.text_sh = xml:InitStatic(xmlroot..":content:text_sh",cap.drop) cap.text_sh:SetWndSize(vector2():set(w,h)) cap.text_sh:TextControl():SetText(plaintext) cap.text_sh:TextControl():SetTextColor(GetARGB(220,0,0,0)) cap.text = xml:InitStatic(xmlroot..":content:text",cap.drop) cap.text:SetWndSize(vector2():set(w,h)) cap.text:TextControl():SetText(mapspot_text) cap.text:TextControl():SetTextColor(GetARGB(220,220,220,220)) cap.BL = xml:InitStatic(frm.."BL_extend",cap.drop) cap.BP = xml:InitStatic(frm.."BP",cap.drop) cap.BL:SetWndPos(vector2():set(0,h - 7)) cap.BP:SetWndPos(vector2():set(0,h - 7)) cap.BM = xml:InitStatic(frm.."BM_extend",cap.BP) local bmh = cap.BM:GetHeight() cap.BM:SetWndSize(vector2():set(w,bmh)) cap.Show = ( function(tf) pins[cap.id].show_label = tf cap.box:Show(tf) end ) return cap end function show_pin_label(id,onoff,destroy) vl("show_pin_label(%s,%s,%s)",id,onoff,destroy) if not (id and pins[id]) then return end onoff = onoff and true or false if not map_labels[id] then --printf("pin label container doesn't exist yet") local icon = pins[id].icon local text = pins[id].text or pins[id].name if not (icon and text) then return end map_labels[id] = init_mapspot_label_wnd(text,id,icon) end map_labels[id].box:Show(onoff) pins[id].show_label = onoff if not destroy then return end dl("Destroying map label for id %s",id) map_labels[id] = nil return onoff end function do_paw_action(act,se_obj,acode,args) args = args or {} local syscall = args.syscall and true or false if (not syscall) and not (paw_enabled and act) then return end acode = acode or act local id,name if se_obj then id = se_obj.id name = safename(id) elseif args and args.id then id = args.id se_obj = alife_object(id) or se_obj name = safename(id) end local ac = action_codes[acode] dl("Received paw action call \"%s\" on target %s (id %s)",act,name,id) if act == "wp_set" then if not se_obj then dl("do_paw_action called with action %s but no valid se_obj!",act) return end set_current_waypoint(se_obj) tm:give_task("task_placeable_waypoint") waypoint_active = true elseif act == "wp_mov" then if not se_obj then dl("do_paw_action called with action %s but no valid se_obj!",act) return end if tm.task_info[task_id] then local id = se_obj.id local task = tm.task_info[task_id] local atsk = db.actor:get_task(task_id, true) task.target = id task.current_target = id atsk:change_map_location(task.spot, id) level.map_add_object_spot(id, waypoint_mapspot, "") set_current_waypoint(se_obj) xr_sound.set_sound_play(AC_ID, "pda_tips") else dl("task_info[task_id] is nil!") end waypoint_active = true elseif act == "wp_del" then end_waypoint_task() last_waypoint = nil elseif act == "pn_add" then dl("Received call to add new pin to map for %s (%s)",name,id) local icon = icons[ac.icon] or map_pin_icon local text = ts("st_paw_pin_default_name") if not script_zones[id] then text = name end init_new_pin(id,name,text,icon) add_mapspot(text,id,icon) open_pin_settings(id) elseif act == "pn_settings" then if not (id and pins[id]) then return end dl("Received call to open settings for pin %s (%s)",name,id) open_pin_settings(id) elseif act == "wp_settings" then dl("Received call to open waypoint settings") open_waypoint_settings() elseif act == "pn_del" then if not (id and pins[id]) then return end if syscall or not pins[id].locked then local icon = pins[id] and pins[id].icon show_pin_label(id,false,true) if hud_pin_objs[id] then vl("Removing pin %s from HUD",id) get_hud():RemoveDialogToRender(hud_pin_objs[id]) hud_pin_objs[id] = nil end vl("Pin %s (%s) deleted",pins[id].name,id) pins[id] = nil if icon then remove_mapspot(id,icon) end end elseif act == "lock_pin" then if not (id and pins[id]) then return end pins[id].locked = true elseif act == "unlock_pin" then if not (id and pins[id]) then return end pins[id].locked = false elseif act == "convert_to_pin" then local mapspot = get_mapspot(id) if can_convert_to_pin(id) then if not texture_data[mapspot] then get_mapspot_data(mapspot) end remove_stash_mapspots(id) local hint = safename(id) init_new_pin(id,name,hint,mapspot) add_mapspot(hint,id,mapspot) open_pin_settings(id) end elseif act == "pn_clr" then local wipe_all = args and args.wipe_all if not is_empty(hud_pin_objs) then for k,v in pairs(hud_pin_objs) do if wipe_all or not (pins[k] and pins[k].locked) then vl("Hiding HUD pin for %s",k) get_hud():RemoveDialogToRender(hud_pin_objs[k]) hud_pin_objs[k] = nil end end end for k,v in pairs(pins) do if wipe_all or not v.locked then local icon = v.icon remove_mapspot(k,icon) pins[k] = nil end end elseif act == "mping" then ping_area_around(se_obj) elseif act == "hud_vis_on" then if not (id and pins[id]) then return end if syscall or not pins[id].locked then pins[id].hud = true show_hud_pin(id) end elseif act == "hud_vis_off" then if not (id and pins[id]) then return end if syscall or not pins[id].locked then pins[id].hud = false hide_hud_pin(id) end elseif act == "show_pins" then show_all_pins(true) elseif act == "hide_pins" then show_all_pins(false) elseif act == "show_label" then if not (id and pins[id]) then return end show_pin_label(id,true) elseif act == "hide_label" then if not (id and pins[id]) then return end show_pin_label(id,false) elseif act == "cm_dbg" then local cls = se_obj and se_obj:clsid() or "empty" property_ui:AddItem("[PAW Debug] Mapspot for %s (id %s) has clsid %s)",name,id,cls) else dl("invalid action %s passed to do_paw_action",act) end return true end function add_context_option(property_ui,act,maptbl) local ac = action_codes[act] vl("add_context_option \"%s\" | cart_mode is %s",act,cart_mode) if ac and ac.enable then --printf("action %s is enabled",act) if type(ac.mode) ~= "number" then dl("WARNING: mode (%s) for action %s was not a number, resetting to 0",ac.mode,act) ac.mode = 0 end -- -1: debug mode only -- 0: disabled -- 1: enabled in normal view only -- 2: enabled in cartography mode only -- 4: enabled in both --[[ if ((ac.mode < 0) and debuglogs) or (ac.mode == 1 and not cart_mode) or ((ac.mode >= 2) and cart_mode) or (ac.mode >= 4) then -]] if feature_valid_in_mode(ac.mode) then local text = ts(ac.text) vl("add_context_option(%s): Adding context option %s",act,text) property_ui:AddItem(text) curr_menu_options[text] = maptbl end else vl("Action %s is missing or not enabled, ignoring",act) end end function nearest_smart_to_actor() vl("nearest_smart_to_actor") local levelid = alife():level_id() local smart_nearest,dist_nearest,name_nearest,result for name,smart in pairs(SIMBOARD.smarts_by_names) do if levelid == game_graph():vertex(smart.m_game_vertex_id):level_id() then -- vl("%s is same map as actor",name) local dist = smart.position:distance_to(db.actor:position()) if (SIMBOARD.smarts[smart.id]) and (not smart_nearest or (dist < dist_nearest)) then vl("%s (%s) is nearest so far at %s",name,smart.id,dist) smart_nearest = smart dist_nearest = dist name_nearest = name end end end if not smart_nearest then dl("!ERROR! No nearby smarts found to actor, this shouldn't be possible") return {id=0,name="oops"} end result = { smart = smart_nearest, id = smart_nearest.id or 0, dist = dist_nearest or 0, name = name_nearest or "oops", pos = smart_nearest.position, -- not used yet } vl("nearest_smart: %s at %s",name_nearest,dist_nearest) return result end function show_smarts(onoff) vl("show_smarts: %s",onoff) local toggle = onoff or false local levelid = alife():level_id() local smart_nearest,dist_nearest,name_nearest,result for name,smart in pairs(SIMBOARD.smarts_by_names) do if (SIMBOARD.smarts[smart.id]) then local text = name local id = smart.id local icon = smart_terrain_icon if toggle then -- vl("Adding temporary smart %s (%s) with icon %s",text,id,icon) level.map_add_object_spot(id,icon,text) else -- vl("Removing temporary smart %s (%s)",text,id) level.map_remove_object_spot(id,icon) end end end end function wp_target_obj() dl("wp_target_obj called") local obj = level.get_target_obj() local id = obj and obj:id() vl("ID returned by level.get_target_obj is %s | obj exists: %s",id,obj ~= nil) if id and (id > 0) then local se_obj = alife_object(id) if not (se_obj and (allow_non_wp_targets or valid_waypoint_target(se_obj))) then return end if placed_waypoint then if id == placed_waypoint.id then vl("Waypoint is currently active on %s (%s), disabling",se_obj:name(),id) --do_paw_action("wp_del",se_obj) toggle_waypoint() else vl("Waypoint is currently active, moving to %s (%s)",se_obj:name(),id) do_paw_action("wp_mov",se_obj) end else vl("No current waypoint, setting to target object %s (%s)",se_obj:name(),id) do_paw_action("wp_set",se_obj) end end end function get_smart_target_type(obj) --printf("get_smart_target_type(%s)",obj and obj:name()) local target_type if obj then if IsStalker(obj) then local acomm = get_actor_true_community() local comm = obj:character_community() or "stalker" local is_enemy = game_relations.is_factions_enemies(acomm,comm) --printf("Targeted object %s (%s) is stalker | actor comm %s | target comm %s | is_enemy %s",obj:character_name(),obj:id(),acomm,comm,is_enemy) if comm == acomm then target_type = "human_f" elseif is_enemy then target_type = "human_e" else target_type = "human_n" end elseif IsMonster(obj) then target_type = "monster" elseif IsInvbox(obj) then target_type = "stash" end end return target_type end function get_smart_icon_for_obj(obj,mapspot) local mapspot = mapspot or map_pin_icon local target_type = get_smart_target_type(obj) local target = target_type and smart_pins[target_type] mapspot = (target and icons[target.pin]) or mapspot vl("get_smart_icon_for_obj %s: best icon for %s was %s",obj and obj:character_name(),target_type,mapspot) return mapspot end function pin_target_obj(mapspot,notip,silent,smart) vl("pin_target_obj: mapspot %s | notip %s | silent %s | smart %s",mapspot,notip,silent,smart) local obj = level.get_target_obj() local id = obj and obj:id() if id and (id > 0) then if blacklisted_object(id) then return end if smart then mapspot = get_smart_icon_for_obj(obj) vl("pin_target_obj: pin %s is smart, assigned mapspot %s",id,mapspot) end local se_obj = alife_object(id) if not (se_obj and (allow_non_wp_targets or valid_waypoint_target(se_obj))) then return end local name = safename(id) if pins[id] then if not pins[id].locked then local hint = pins[id].text or pins[id].name or ts("st_paw_pin_default_name") local text = string.format(ts("st_paw_pin_removed_from_target"),name,hint) text = psk(text,text_colors) do_paw_action("pn_del",se_obj) dotip(text,5,nil,false) end else local hint = name or ts("st_paw_pin_default_name") drop_quick_pin(id,hint,mapspot,true,true) if pin_hud_icon_default and pins[id] then pins[id].hud = true show_hud_pin(id) end if not silent then play_ui_sound(snd_tag_target) end if not notip then local icon = ts("ui_mcm_lst_"..(mapspot or pawdata.curr_ico_name)) local nxtb = keybinds["ico_next"].text local prvb = keybinds["ico_prev"].text local text = string.format(ts("st_paw_pin_added_to_target"),name,icon,nxtb,prvb) text = psk(text,text_colors) dotip(text,5,nil,false) end end end return id end function create_smart_pin(notip,silent) --local mapspot = get_smart_icon_for_obj(obj) return pin_target_obj(nil,notip,silent,true) end function autotag_target(notip,silent) --if not valid_pda("autotag") then return end local id = create_smart_pin(true) set_pingspot_persistence(id,autotag_persistence) set_pin_persistence(id,autotag_persistence) return id end function drop_quick_pin(id,force_text,force_pin,no_tip,no_fast_travel) if not paw_enabled_check() then return end if next_pin > time_global() then return end next_pin = time_global() + pin_delay if not id then id = generate_scripted_mapspot_on(db.actor) end local se_obj = alife_object(id) if not se_obj then return end local name = se_obj:name() or "none" local icon = force_pin or map_pin_icon local mapspot_text = force_text or ts("st_paw_pin_quick_name") local tip_text = ts("st_paw_pin_dropped") init_new_pin(id,name,mapspot_text,icon) pins[id].quick = not no_fast_travel dl("calling add_mapspot with %s | %s | %s",mapspot_text,id,icon) play_ui_sound(snd_place_pin) add_mapspot(mapspot_text,id,icon) if not no_tip then dotip(tip_text,10000) end end function toggle_waypoint(force) dl("toggle_waypoint action called") if (force ~= nil) then waypoint_active = not force end if not waypoint_active then if last_waypoint then placed_waypoint = last_waypoint local se_obj = alife_object(placed_waypoint.id) if not se_obj then return end do_paw_action("wp_set",se_obj) else dotip("st_paw_no_last_wp") end else last_waypoint = placed_waypoint end_waypoint_task() end end function cartography_mode(onoff) if (onoff == nil) then cart_mode = not cart_mode else cart_mode = onoff or false end vl ("Setting cart_mode to %s and calling show_smarts to toggle",cart_mode) show_smarts(cart_shows_smarts and cart_mode) end function get_context_menu_options(property_ui,id,maptbl) empty_table(curr_menu_options) maptbl = maptbl or {} if not id then id = maptbl.object_id end if not paw_enabled then return end local se_obj = id and (id > 0) and alife_object(id) if not se_obj then return end local name = se_obj:name() or "undefined" local clsid = se_obj:clsid() or "unknown" local id = se_obj.id local pin = id and pins[id] local pinset = false vl("get_context_menu_options: %s (%s) | cls %s",name,id,clsid) add_context_option(property_ui,"mping",maptbl) if id and valid_waypoint_target(se_obj) then if pins[id] then vl("Pin exists") add_context_option(property_ui,"pn_settings",maptbl) pinset = true if pin.locked then add_context_option(property_ui,"unlock_pin",maptbl) else add_context_option(property_ui,"pn_del",maptbl) if pins[id].hud then add_context_option(property_ui,"hud_vis_off",maptbl) else add_context_option(property_ui,"hud_vis_on",maptbl) end if pins[id].show_label then add_context_option(property_ui,"hide_label",maptbl) else add_context_option(property_ui,"show_label",maptbl) end add_context_option(property_ui,"lock_pin",maptbl) end else vl("No pin") if can_convert_to_pin(id) then add_context_option(property_ui,"convert_to_pin",maptbl) else add_context_option(property_ui,"pn_add",maptbl) end end local name = se_obj:name() if placed_waypoint == nil then vl("No waypoint") add_context_option(property_ui,"wp_set",maptbl) else local wid = placed_waypoint.id or 0 local sid = se_obj.id or 0 if not pinset then -- If already showing the pin settings option, don't also clutter the menu with waypoint settings add_context_option(property_ui,"wp_settings",maptbl) end if wid ~= sid then vl("Waypoint exists elsewhere") add_context_option(property_ui,"wp_mov",maptbl) else vl("Waypoint exists here") end add_context_option(property_ui,"wp_del",maptbl) end end if pins_exist() then if show_pins then add_context_option(property_ui,"hide_all_pins",maptbl) else add_context_option(property_ui,"show_all_pins",maptbl) end end if not (pin and pin.locked) then add_context_option(property_ui,"pn_clr",maptbl) for k,v in pairs(action_codes) do local can_add = true if v.act == "pn_add" then can_add = (pins[id] == nil) end local mode = tonumber(v.mode) or 0 if can_add and v.enable and (mode > 0) and not managed_options[k] then add_context_option(property_ui,k,id,maptbl) end end end end function execute_context_menu_option(property_ui,id,level_name,prop) vl("passed id : %s",id) id = last_clicked_id last_clicked_id = nil vl("execute_context_menu_option called for id %s\n| prop: %s\n| action %s",id,prop,action[prop]) if not (paw_enabled and id and valid_pda("show_w")) then return end if not (id and (id > 0)) then return end local se_obj = alife_object(id) if not se_obj then return end local ac = action[prop] if ac then local act = action_codes[ac].act do_paw_action(act,se_obj,ac) end end function faction_body_icon(comm) vl("faction_body_icon(%s) called",comm) local icon = nil if comm and managed_factions[comm] then local bi = body_icon_mode or "badge" icon = "paw_"..patch_res..sep..comm vl("faction_body_icon generated from quality %s for faction %s: %s",patch_res,comm,icon) else vl("not a valid human faction, returning %s from bodies set",current_body_icon) end return icon or current_body_icon end function local_set_name(setname) return ts(setname or icon_sets[pawdata.curr_set_name].name) end function local_icon_name(iconame) return ts(iconame or "ui_mcm_lst_"..pawdata.curr_ico_name) end function icon_for_pin(pin_name) if icons and pin_name then return icons[pin_name] end end function pin_for_icon(icon) if icon and texture_data and texture_data[icon] then return texture_data[icon].id end end function texture_for_icon(icon) --printf("texture_for_icon(%s) called",icon) local tex = texture_data and texture_data[icon] and texture_data[icon].t --vl("texture_for_icon(%s) returned %s",icon,tex) return tex end function texture_for_set(setname) if not setname then return end local setdata = icon_sets[setname] if not (setdata and setdata.active_icon) then dl("WARNING: No active icon found for %s",setname) return end --vl("active_icon for %s: %s",setname,setdata.active_icon) local td = texture_data[setdata.active_icon] --vl("texture exists for %s: %s",setdata.active_icon,td ~= nil) local tex = td.t vl("texture_for_set(%s) returned %s",setname,tex) return tex end function get_current_texture(set_changed) return set_changed and texture_for_set(pawdata.curr_set_name) or texture_for_icon(pawdata.curr_ico_name) end function get_mapspot_data(spot) local attr_tex = "texture" local attr_minimap = "mini_map" local attr_levelmap = "level_map" xml:ParseFile("map_spots.xml") xml:NavigateToRoot() th = xml:ReadAttribute(spot,0,"height") tw = xml:ReadAttribute(spot,0,"width") xml:NavigateToNode(spot,0) vl("trying to find texture data for %s",spot) if xml:NodeExist(attr_tex,0) then tex = xml:ReadValue(attr_tex,0) vl("texture node found with value %s",tex) else local spotnode = "" local typ = "" if xml:NodeExist(attr_minimap) then spotnode = xml:ReadAttribute(attr_minimap,0,"spot") typ = attr_minimap end if xml:NodeExist(attr_levelmap) then spotnode = xml:ReadAttribute(attr_levelmap,0,"spot") typ = attr_levelmap end if spotnode ~= "" then xml:NavigateToRoot() th = xml:ReadAttribute(spotnode,0,"height") tw = xml:ReadAttribute(spotnode,0,"width") vl("ReadAttribute for %s: h %s w %s",spot,th,tw) xml:NavigateToNode(spotnode,0) tex = xml:ReadValue(attr_tex,0) vl("%s node found: %s",typ,spotnode) end end texture_data[spot] = { id = k, t = tex, h = th, w = tw, } local ind = #icon_index + 1 icon_index[ind] = { id = k, icon = spot, data = texture_data[spot], } vl("Texture for %s: %s (%sx%s)",spot,tex,tw,th) return texture_data[spot],ind end function set_or_icon(str) return str and ((str ~= "set") and (str ~= "icon")) end function set_icon_index(set) vl("set_icon_index called for set %s",set) local ii = set_index[set].ii return ii end function set_icon_index_rev(set) vl("set_icon_index_rev called for set %s",set) local ri = set_index[set].ri return ri end function set2ind(skey) local ind = nil vl("set2ind called for set %s, %s sets listed",skey,#set_index) for i=1,#set_index,1 do local k = set_index[i].id if k == skey then ind = i end end vl("set2ind(%s) returned %s",skey,ind) return ind end function curr_set_i() return pawdata.curr_set_ind end function next_set_i() return roll(curr_set_i() + 1, 1, #set_index) end function prev_set_i() return roll(curr_set_i() - 1, 1, #set_index) end function curr_ico_i() --printf("curr_ico_i returns %s",pawdata.curr_ico_ind) return pawdata.curr_ico_ind end function next_ico_i() local c_ind = curr_ico_i() local setdata = icon_sets[pawdata.curr_set_name] return roll(c_ind + 1, 1, #setdata.ii) end function prev_ico_i() local c_ind = curr_ico_i() local setdata = icon_sets[pawdata.curr_set_name] return roll(c_ind - 1, 1, #setdata.ii) end function root_patch_name(patch) --printf("root_patch_name called with mapspot %s",patch) local name = patch local pos_paw = string.find(name,"paw_badge_") local pos_qlt = string.find(name,"hr_") if pos_paw and pos_qlt then name = "paw_badge_"..string.sub(patch,pos_qlt+3) vl("root_patch_name strips quality string from %s to get %s",patch,name) else vl("root_patch_name returning %s as-is",patch) end return name end function active_set(newset) vl("active_set(%s) called",newset) if newset and type(newset) == "number" then pawdata.curr_set_ind = clamp(newset,1,#set_index) pawdata.curr_set_name = set_index[pawdata.curr_set_ind].id pawdata.curr_set_data = icon_sets[pawdata.curr_set_name] current_active_set = pawdata.curr_set_name local setdata = pawdata.curr_set_data pawdata.curr_ico_name = setdata.active_icon pawdata.curr_ico_ind = setdata.ri[root_patch_name(pawdata.curr_ico_name)] map_pin_icon = pawdata.curr_ico_name vl("active set is now %s (%s), current icon %s (%s)",pawdata.curr_set_name,pawdata.curr_set_ind,pawdata.curr_ico_name,pawdata.curr_ico_ind) end return pawdata.curr_set_name end function get_next_icon() local setdata = pawdata.curr_set_data return setdata.ii[next_ico_i()] end function get_prev_icon() local setdata = pawdata.curr_set_data return setdata.ii[prev_ico_i()] end function get_active_icon(newico) if newico and type(newico) == "number" then vl("Setting new icon to index %s",newico) local setdata = pawdata.curr_set_data pawdata.curr_ico_ind = clamp(newico,1,setdata.inum) pawdata.curr_ico_name = setdata.ii[pawdata.curr_ico_ind] setdata.active_icon = pawdata.curr_ico_name map_pin_icon = pawdata.curr_ico_name end return pawdata.curr_ico_name end function notify_icon_change(newset) local setdata = pawdata.curr_set_data local icon = setdata.active_icon local td = texture_data[icon] local tex = td.t local tiptext = ts("st_paw_change_icon").." "..ts("ui_mcm_lst_"..icon) if newset then local name = ts(setdata.name) local settext = string.format(ts("st_paw_change_set"),name) tiptext = settext .. tiptext end dotip(tiptext,2,nil,false,tex) end function cycle_icons(cycle_dir,dotip) --printf("cycle_icons: curr_set: %s (%s)",pawdata.curr_set_name,pawdata.curr_set_ind) if cycle_dir and type(cycle_dir) ~= "number" then return end local setdata = pawdata.curr_set_data local next_ind = roll(pawdata.curr_ico_ind + cycle_dir,1,setdata.inum) get_active_icon(next_ind) play_ui_sound(snd_cycle_blip) if dotip then notify_icon_change() end end function cycle_sets(cycle_dir,dotip) if cycle_dir and type(cycle_dir) ~= "number" then return end local next_ind = roll(pawdata.curr_set_ind + cycle_dir,1,#set_index) active_set(next_ind) play_ui_sound(snd_cycle_blip) if dotip then notify_icon_change(true) end end function icon_cycle(change,val) if not mwheel_enabled then return end if change then icon_cycle_active = val if not mwheel_notify then return icon_cycle_active end if icon_cycle_active then dotip(ts("st_paw_cycling_icons")) else dotip(ts("st_paw_mwheel_normal")) end end return icon_cycle_active end function set_cycle(change,val) if not mwheel_enabled then return end if change then set_cycle_active = val if not mwheel_notify then return icon_cycle_active end if set_cycle_active then dotip(ts("st_paw_cycling_sets")) else dotip(ts("st_paw_mwheel_normal")) end end return set_cycle_active end function cycle_items(cycle_dir,item_type,dotip) if (item_type == nil) then if icon_cycle() then item_type = "icon" elseif set_cycle() then item_type = "set" end end if not icoset_changed then icoset_changed = {} end if item_type == "icon" then icoset_changed.i = true vl("cycling icons: %s",cycle_dir) cycle_icons(cycle_dir,dotip) elseif item_type == "set" then icoset_changed.s = true vl("cycling sets: %s",cycle_dir) cycle_sets(cycle_dir,dotip) end end function show_all_pins(onoff) show_pins = onoff if onoff then vl("Showing all recorded pins") for id,pin in pairs(pins) do local icon = pin.icon local mapspot_text = get_tooltip_for_pin(id) vl("calling add_mapspot with %s | %s | %s",mapspot_text,id,icon) add_mapspot(mapspot_text,id,icon) end else vl("Hiding stored pins from map") for id,pin in pairs(pins) do remove_mapspot(id,pin.icon) end end end function load_mcm(pawpath,default) local val = ui_mcm and ui_mcm.get("pawsys/"..pawpath) if val ~= nil then return val else return default end end function marker_hint_shown(wpin) local mode = show_marker_hint and tonumber(show_marker_hint[wpin]) local shown = feature_valid_in_mode(mode) return shown end function marker_dist_shown(wpin) local mode = show_marker_dist and tonumber(show_marker_dist[wpin]) local shown = feature_valid_in_mode(mode) return shown end function mcm_reload_general_settings() if not ui_mcm then return end local old_paw_en = paw_enabled paw_enabled = load_mcm("pawgen/enabled",paw_enabled) if paw_enabled and not old_paw_en then on_game_start() dl("PAW was disabled and now is not, re-registering actor_on_update") elseif not paw_enabled then if old_paw_en then unregister_all_callbacks() end printf(logprefix.."Personal Adjustable Waypoint has been disabled, aborting all further action!") return false end disable_load_warning = load_mcm("pawgen/disable_load_warning",disable_load_warning) wp_hud_icon_enabled = load_mcm("pawgen/wp_hud_icon_enabled",wp_hud_icon_enabled) if wp_hud_icon_enabled then show_marker_dist["wp"] = tonumber(load_mcm("pawgen/show_dist_wp",show_marker_dist["wp"])) show_hud_waypoint() else hide_hud_waypoint() end show_marker_dist["pins"] = tonumber(load_mcm("pawgen/show_dist_pins",show_marker_dist["pins"])) show_marker_hint["pins"] = tonumber(load_mcm("pawgen/show_hint_pins",show_marker_hint["pins"])) pin_near_fade_dist = tonumber(load_mcm("pawgen/pin_near_fade_dist",pin_near_fade_dist)) pin_far_fade_dist = tonumber(load_mcm("pawgen/pin_far_fade_dist",pin_far_fade_dist)) pin_far_hide_dist = tonumber(load_mcm("pawgen/pin_far_hide_dist",pin_far_hide_dist)) wp_near_fade_dist = tonumber(load_mcm("pawgen/wp_near_fade_dist",wp_near_fade_dist)) wp_far_fade_dist = tonumber(load_mcm("pawgen/wp_far_fade_dist",wp_far_fade_dist)) wp_far_hide_dist = tonumber(load_mcm("pawgen/wp_far_hide_dist",wp_far_hide_dist)) wp_clear_dist = tonumber(load_mcm("pawgen/wp_clear_dist",wp_clear_dist)) tip_on_icoset_change = load_mcm("pawgen/tip_on_icoset_change",tip_on_icoset_change) welcome_msg_shown = load_mcm("pawgen/welcome_msg_shown",welcome_msg_shown) enable_wp_proxcheck = wp_clear_dist and wp_clear_dist > 0 mcm_update_throttle = load_mcm("pawgen/mcm_update_throttle",mcm_update_throttle) mwheel_poll_interval = load_mcm("pawgen/mwheel_poll_interval",mwheel_poll_interval) -- These last two options are not in the MCM menu, but can be manually set as overrides return true end function mcm_reload_keybind_table() if not ui_mcm then return end for k,v in pairs(keybinds) do local kb = load_mcm("pawbinds/bind_"..k) local km = load_mcm("pawbinds/modk_"..k) local vbkb = valid_bind(kb) local vbkm = valid_bind(km) if vbkb then keybinds[k].bind = kb end if vbkm then keybinds[k].mod = km end keybinds[k].text = bindtext(kb,km) end end function mcm_reload_keybind_settings() cartography_must_hold = not load_mcm("pawbinds/cartmode_toggle",cartography_must_hold) keybinds[cartmode].hold = cartography_must_hold cart_shows_smarts = load_mcm("pawbinds/cart_shows_smarts",cart_shows_smarts) mwheel_notify = load_mcm("pawbinds/mwheel_notify") cartmode_unfade = load_mcm("pawbinds/cartmode_unfade",cartmode_unfade) local tmp tmp = ui_mcm.get("pawsys/pawbinds/mwheel_override") if tmp ~= nil then dl("MCM override found for mwheel_avail (old = %s | new = %s)",mwheel_avail,tmp) mwheel_override = tmp if mwheel_override then mwheel_avail = true end end tmp = ui_mcm.get("pawsys/pawbinds/mwheel_enabled") if tmp and type(tmp) == "number" then tmp = (tmp ~= 0) end mwheel_enabled = tmp and mwheel_avail tmp = ui_mcm.get("pawsys/pawbinds/right_click_override") -- if set to true, will force on_map_right_click support on if tmp ~= nil then dl("MCM override found for right_click_avail (old = %s | new = %s)",right_click_avail,tmp) right_click_override = tmp if right_click_override then right_click_avail = true RegisterScriptCallback("on_map_right_click",on_map_right_click) end end tmp = ui_mcm.get("pawsys/pawgen/disable_mcmups") if tmp ~= nil then dl("MCM override found for disable_mcm_updates (old = %s | new = %s)",disable_mcm_updates,tmp) disable_mcm_updates = tmp end tmp = load_mcm("pawgen/pingsound") if tmp ~= nil then use_ping_snd = tmp end end function mcm_reload_pin_settings() patch_res = ui_mcm.get("pawsys/pawpins/patch_res") or "badge" local bi = ui_mcm.get("pawsys/pawpins/milpda_body_icon") local pig = ui_mcm.get("pawsys/pawpins/pin_icon_group") --local mp = ui_mcm.get("pawsys/pawpins/poi_icon_"..(pig or "")) local cbi = ui_mcm.get("pawsys/pawpins/poi_icon_bodies") for k,v in pairs(icons) do if managed_factions[k] then local newbadge = "paw_"..patch_res..sep..k vl("MCM: icon for %s was %s, is now %s",k,v,newbadge) icons[k] = newbadge end end load_active_icoset_data() local icon = pig and icon_sets[pig] and icon_sets[pig].active_icon or default_mapspot icon_sets["bodies"].active_icon = icons[cbi] dl("MCM: body icon mode %s | paw icon %s",bi,cbi) map_pin_icon = icon current_active_set = pig dl("MCM: icon %s | icon set: %s",icon,pig) --[[ use_custom_backpack_icon = load_mcm("pawpins/use_custom_backpack_icon",use_custom_backpack_icon) if use_custom_backpack_icon then local psicon = stash_icons["player"] local newicon = ui_mcm.get("pawsys/pawpins/custom_backpack_icon") if newicon ~= nil then stash_icons["player"] = icons[newicon] end dl("MCM: Overriding player stash icon with custom value: %s",stash_icons["player"]) end --]] local cp = ui_mcm.get("pawsys/pawpins/use_custom_pin_icon") or false if cp then custom_pin_icon = true map_pin_icon = ui_mcm.get("pawsys/pawpins/custom_pin_icon") or map_pin_icon dl("MCM: Overriding default icon with custom value: %s",map_pin_icon) else custom_pin_icon = false end if force_icon_override then dl("MCM: Script hardcoded to override map_pin_icon with %s",force_icon_override) map_pin_icon = force_icon_override default_mapspot = force_icon_override end if (bi and ish_kill_tracker) then body_icon_mode = bi current_body_icon = icons[cbi] or default_mapspot ish_kill_tracker.use_paw_icon = (bi == "set") or (bi == "fac") end widget_enabled = load_mcm("pawhud/widget_enable",widget_enabled) active_theme = load_mcm("pawhud/active_theme",active_theme) if not hud_themes[active_theme] then active_theme = "classicauto" end widget_custom_pos = { x = load_mcm("pawhud/pos_x"), y = load_mcm("pawhud/pos_y") } widget_use_custom_pos = load_mcm("pawhud/custompos") widget_hide_delay = (tonumber(load_mcm("pawhud/autohide",widget_hide_delay)) * 1000) use_ui_snd = load_mcm("pawhud/hudsound") --[[ local mdf = clamp(tonumber(ui_mcm.get("pawsys/pawpins/fave_recent")),0,10) if mdf and (mdf ~= max_dynamic_faves) then local old_mdf = max_dynamic_faves max_dynamic_faves = mdf dl("new max_dynamic_faves: %s (was %s)",mdf,old_mdf) if dynamic_faves and (not is_empty(dynamic_faves)) and (max_dynamic_faves > 0) then local df = {} for i = 1,max_dynamic_faves do if dynamic_faves[i] then vl("dynamic_fave[%s]: %s",i,dynamic_faves[i]) df[i] = dynamic_faves[i] end end dynamic_faves = df end end --]] end function mcm_reload_reticle_settings() if ui_mcm then mark_on_positive_id = load_mcm("pawret/enable_autotag",mark_on_positive_id) autotag_persistence = load_mcm("pawret/autotag_persistence",autotag_persistence) autotag_lifetime = load_mcm("pawret/autotag_lifetime",(autotag_lifetime / 6)) * 6 autotags_time_out = (autotag_lifetime or 0) > 0 pin_hud_icon_default = load_mcm("pawret/pin_auto_visible",pin_hud_icon_default) manual_smart_pins = load_mcm("pawret/manual_smart_pins",manual_smart_pins) reticle_mode = load_mcm("pawret/reticle_mode",reticle_mode) autotag_mode = load_mcm("pawret/reticle_mode",autotag_mode) reticle_mustzoom = load_mcm("pawret/reticle_mustzoom",reticle_mustzoom) reticle_color.a = load_mcm("pawret/reticle_alpha",reticle_color.a) ret_fade_attack_time = load_mcm("pawret/ret_fade_attack_time",ret_fade_attack_time) ret_fade_decay_time = load_mcm("pawret/ret_fade_decay_time",ret_fade_decay_time) vl("Autotag settings: mode %s | mustzoom %s | manual %s",autotag_mode,reticle_mustzoom,manual_smart_pins) for k,v in pairs(smart_pins) do smart_pins[k].pin = load_mcm("pawret/"..k,v.pin) smart_pins[k].enabled = load_mcm("pawret/"..k.."_enable",v.enabled) end -- Load Crook's facid settings, if present local did = ui_mcm.get("targetID/timeID") if did ~= nil then npc_ident.delay_id = did end did = ui_mcm.get("targetID/deadID") if did ~= nil then npc_ident.id_bodies = did end npc_ident.id_speed = tonumber(ui_mcm.get("targetID/speedID")) or 0.3 npc_ident.lenience = tonumber(ui_mcm.get("targetID/targL")) or 0.99 end end function on_option_change() -- Sync all MCM values and propagate changes language = ui_options.curr_localization() if ui_mcm and not mcm_killswitch then debuglogs = load_mcm("pawgen/debuglogs",debuglogs) catsy_paw_mcm.debuglogs = debuglogs dl("Setting for debuglogs loaded from MCM: %s",debuglogs) if load_mcm("pawgen/wipe_all") then ui_mcm.set("pawsys/pawgen/wipe_all",false) dl("MCM: Wiping all pin data per player request") local hudpins = hud_pin_objs for k,v in pairs(hudpins) do hide_hud_pin(k) end hud_pin_objs = {} for k,v in pairs(map_labels) do map_labels[k].box:Show(false) end map_labels = {} local args = {wipe_all=true,syscall=true} do_paw_action("pn_clr",nil,nil,args) end mcm_reload_general_settings() if not paw_enabled then printf(logprefix.."Personal Adjustable Waypoint has been disabled, aborting all further action!") return end mcm_reload_keybind_table() mcm_reload_keybind_settings() mcm_reload_pin_settings() mcm_reload_reticle_settings() for k,v in pairs(action_codes) do local m = load_mcm("pawmenu/"..k) if m ~= nil then action_codes[k].mode = tonumber(m) or 0 vl("MCM: Loading action code mode %s for %s",m,k) else vl("MCM: No value found for %s, leaving unchanged",k) end end ui_mcm.set("pawsys/pawgen/mcm_ver",script_version) end if ish_kill_tracker and item_milpda then ish_kill_tracker.on_option_change() end active_set(icon_sets[current_active_set].i) if db.actor and get_hud() then unsquish_ratio = (device().height / device().width) / (768 / 1024) reset_indicator() end end function load_active_icoset_data() if not ui_mcm then return end vl("Loading MCM values for sets and icons") local path = "pawsys/pawpins/poi_icon_" for k,v in pairs(icon_sets) do local val = ui_mcm.get(path..k) local icon = icons[val] --printf("loading "..path..k..": %s | icons %s | icons[%s]: %s",val,icons,val,icons[val]) vl("Loading active icon for %s: %s = %s",k,val,icon) icon_sets[k].default = val icon_sets[k].active_icon = icon end local setname = ui_mcm.get("pawsys/pawpins/pin_icon_group") if set_index[setname] then active_set(set_index[setname]) end end function update_mcm_icoset_data() if not ui_mcm then return end if icoset_changed then vl("Updating MCM settings after set/icon change",icoset_changed) local curr_set = pawdata.curr_set_name if icoset_changed and icoset_changed.s then ui_mcm.set("pawsys/pawpins/pin_icon_group",curr_set) end local path = "pawsys/pawpins/poi_icon_"..curr_set local active_pin = pin_for_icon(icon_sets[curr_set].active_icon) ui_mcm.set(path,active_pin) end icoset_changed = nil end function check_waypoint_proximity(id) local se_obj = id and alife_object(id) if (not se_obj) or not (se_obj and se_obj.online) then return end local szpos = se_obj.position local dist = szpos:distance_to(db.actor:position()) or 0 if dist <= wp_clear_dist then toggle_waypoint(false) end end function tick() if not paw_enabled then UnregisterScriptCallback("actor_on_update",actor_on_update) return end local now = time_global() if enable_wp_proxcheck and waypoint_active and (next_wp_proxcheck <= now) then next_wp_proxcheck = now + wp_proxcheck_interval check_waypoint_proximity(placed_waypoint and placed_waypoint.id) end if (next_gc_check <= now) then next_gc_check = now + garbcollect_interval if script_zone_changed then script_zone_cleanup() end temp_pin_cleanup() end if disable_mcm_updates then return end if (next_mcm_update > now) or not icoset_changed then return end next_mcm_update = now + mcm_update_throttle update_mcm_icoset_data() end function paw_welcome_message() if welcome_msg_shown then return true end vl("Welcome message not shown yet, displaying to player") welcome_msg_shown = true if ui_mcm then ui_mcm.set("pawsys/pawgen/welcome_msg_shown",true) end local player_name = alife():actor():character_name() local kb = keybinds toggle_bind = kb["wptoggle"].bind toggle_mod = kb["wptoggle"].mod quickpin_bind = kb["quickpin"].bind quickpin_mod = kb["quickpin"].mod cart_mode_hold = kb["cartmode"].bind cart_mode_mod = kb["cartmode"].mod local toggle_btxt = bindtext(toggle_bind,toggle_mod) local quickpin_btxt = bindtext(quickpin_bind,quickpin_mod) local cartmode_btxt = bindtext(cart_mode_hold,cart_mode_mod) local welcome_msg = string.format(ts("st_pawsys_welcome_msg"),toggle_btxt,quickpin_btxt,cartmode_btxt,player_name) welcome_msg = psk(welcome_msg,text_colors) dotip(welcome_msg,10000) return true end -- ====================================================================== -- UI ELEMENTS -- ====================================================================== function setwndpos(wnd,x,y) if not (wnd and x and y) then return end wnd:SetWndPos(vector2():set(x,y)) end -- ====================================================================== --[[ TEXT INPUT WINDOW Originally adapted from parts of the vanilla backpack stash script A more generic, parameterized version used to ship with PAW as its own mod (ui_get_input.script) -- ====================================================================]] GUI_WP = nil function open_waypoint_settings() dl("open_waypoint_settings called") tex = texdef or tex_defs args = args or {} if not GUI_WP then GUI_WP = UIWPSettings() end if GUI_WP and not GUI_WP:IsShown() then GUI_WP:ShowDialog(true) Register_UI("UIWPSettings","open_waypoint_settings") end end local function destroy_wp_window() if GUI_WP then GUI_WP:HideDialog() GUI_WP = nil end end -- ====================================================================== class "UIWPSettings" (CUIScriptWnd) function UIWPSettings:__init() super() vl("UIWPSettings:__init:\nTitle: %s\nIcon: %s\nExisting text: %s",title_txt,icon_tex,prepop) self.init_time = tostring(get_time_elapsed()) self.init_wp = placed_waypoint self.init_state = waypoint_active self.def_wpname = custom_name or ts("st_paw_placed_waypoint")..(": "..placed_waypoint.name or "") self.def_wpdesc = custom_desc or ts("st_paw_proceed") self.pin_text = title_txt self.icon_tex = "ui_inGame2_PDA_icon_Secondary_mission" self.prepop = prepop self.func = func self.args = args self.cap = {} self:InitControls() self:InitCallBacks() self.control_hints = { ["btn_wpreset"] = true, } self.hint_wnd = utils_ui.UIHint(self) end function UIWPSettings:__finalize() end function UIWPSettings:InitControls() self:SetWndRect(Frect():set(0,0,1024,768)) self:SetAutoDelete(true) local xml = CScriptXmlInit() local file = "paw_input_window.xml" local root = "wp_dialog" local rootand = root..":" local tbar = rootand.."titlebar" local paw = tbar..":paw" local text = tbar..":text" local icon = tbar..":icon" local reset = rootand.."btn_wpreset" local cancel = rootand.."btn_cancel" local input_n = rootand.."input_name" local input_d = rootand.."input_desc" local btn_ok = rootand.."btn_ok" local caption = rootand.."caption" xml:ParseFile (file) self.dialog = xml:InitStatic(root, self) local bg_top = xml:InitStatic(rootand.."bg_top",self.dialog) local bg_btm = xml:InitStatic(rootand.."bg_btm",self.dialog) self.bg_wpdesc = xml:InitStatic(rootand.."bg_desc",self.dialog) self.wpname = xml:InitEditBox(input_n,self.dialog) self:Register(self.wpname,"fld_input_name") self.wpname:SetText(self.def_wpname) self.wpname:SetWndSize(vector2():set(220,30)) --self.descbg = xml:InitStatic(input_d,self.dialog) --self.descbg:InitTexture("ui_paw_dialog_large_text_field") self.wpdesc = xml:InitEditBox(input_d, self.bg_wpdesc) self:Register(self.wpdesc,"fld_input_desc") self.wpdesc:SetText(self.def_wpdesc) --self.descbg:SetWndSize(vector2():set(171,188)) self.wpdesc:SetWndSize(vector2():set(220,30)) self.tbar = xml:InitStatic(tbar,self.dialog) local paw = xml:InitTextWnd(paw,self.tbar) paw:SetText("PAW".." "..script_version) paw:SetFont(GetFontSmall()) self.icon = xml:InitStatic(icon, self.tbar) if self.icon_tex then self.icon:InitTexture(self.icon_tex) else dl("Empty icon texture passed to input window") end self.titletxt = xml:InitTextWnd(text, self.tbar) self.titletxt:SetText(ts("ui_mcm_pawsys_pawmenu_wp_settings")) self.titletxt:SetFont(GetFontDI()) self.targetname = xml:InitStatic(rootand.."target",self.dialog) self.tartxt = ts("st_paw_target")..": " self.tarclr = "$clr_wht" if placed_waypoint and placed_waypoint.id then self.tarclr = "$clr_yel" local tartxt = self.tartxt..self.tarclr..safename(placed_waypoint.id) self.tartxt = psk(tartxt,text_colors) self.targetname:TextControl():SetText(self.tartxt) else self.targetname:TextControl():SetText(ts("st_paw_none")) end local function setup_checkbox(setting,pos_x,pos_y,captext,default) local checkopt = "check_"..setting vl("Initializing checkbox %s (%s) at %s,%s | %s",checkopt,setting,pos_x,pos_y,captext) self[checkopt] = xml:InitCheck(rootand.."check",self.dialog) self:Register(self[checkopt], checkopt..self.init_time) self[checkopt]:SetWndPos(vector2():set(pos_x,pos_y)) self[checkopt]:SetCheck(default) if captext then self.cap[checkopt] = xml:InitTextWnd(caption,self[checkopt]) self.cap[checkopt]:SetWndPos(vector2():set(30,8)) self.cap[checkopt]:SetText(captext) self.cap[checkopt]:SetVTextAlignment(1) end end function setup_button(button,pos_x,pos_y,xmlnode) local buttonx = "btn_"..button vl("Initializing button %s (%s) at %s,%s",buttonx,button,pos_x,pos_y) self[buttonx] = xml:Init3tButton(xmlnode,self.dialog) self:Register(self[buttonx],buttonx) self[buttonx]:SetWndPos(vector2():set(pos_x,pos_y)) end setup_checkbox( "wpactive", 15, 152, ts("st_paw_wp_active"), waypoint_active ) setup_checkbox( "wphud", 15, 178, ts("ui_mcm_pawsys_pawgen_wp_hud_icon_enabled"), wp_hud_icon_enabled ) self.check_wpactive:SetCheck(self.init_state) self.check_wphud:SetCheck(wp_hud_icon_enabled) setwndpos( paw, 215, 26 ) setwndpos( self.titletxt, 7, 7 ) setwndpos( self.icon, 17, 121 ) setwndpos( self.targetname, 50, 130 ) setwndpos( self.wpname, 25, 38 ) --setwndpos( self.descbg, 25, 90 ) setwndpos( self.wpdesc, 25, 4 ) setup_button( "wpreset", 20, 210, reset ) setup_button( "ok", 112, 210, btn_ok ) setup_button( "cancel", 183, 210, cancel ) end function UIWPSettings:InitCallBacks() self:AddCallback("btn_wpreset", ui_events.BUTTON_CLICKED, self.Reset, self) self:AddCallback("btn_ok", ui_events.BUTTON_CLICKED, self.OnAccept, self) self:AddCallback("btn_cancel", ui_events.BUTTON_CLICKED, self.Close, self) end function UIWPSettings:ShowHint(hint) if hint then hint = psk(hint,text_colors) --vl("* [%s] Showing hint_wnd text: %s",time_global(),hint) end self.hint_wnd:Update(hint) end function UIWPSettings:Reset() dl("Restoring default waypoint settings") --self.icon_tex = "ui_inGame2_PDA_icon_Secondary_mission" --self.icon:InitTexture(self.icon_tex) custom_name = nil custom_desc = nil self.wpname:SetText(self.def_wpname) self.wpdesc:SetText(self.def_wpdesc) wp_hud_icon_enabled = true self.check_wphud:SetCheck(true) end function UIWPSettings:Update() CUIScriptWnd.Update(self) self.hint = nil for ctrl,enable in pairs(self.control_hints) do if enable and self[ctrl] and self[ctrl]:IsCursorOverWindow() then local st_hint = hint_path..ctrl.."_desc" local hint = ts(st_hint) self.hint = hint --vl("Showing hint for %s: %s\n\"%s\"",ctrl,st_hint,hint) end end self:ShowHint(self.hint) end function UIWPSettings:OnAccept() local new_wpname = self.wpname:GetText() if new_wpname ~= self.def_wpname then custom_name = new_wpname end local new_wpdesc = self.wpdesc:GetText() if new_wpdesc ~= self.def_wpdesc then custom_desc = new_wpdesc end local new_wpstate = self.check_wpactive:GetCheck() if self.init_state ~= new_wpstate then toggle_waypoint() end local new_hudstate = self.check_wphud:GetCheck() if new_hudstate and not wp_hud_icon_enabled then wp_hud_icon_enabled = true show_hud_waypoint() elseif wp_hud_icon_enabled and not new_hudstate then wp_hud_icon_enabled = false hide_hud_waypoint() end dl("UIWPSettings:OnAccept | custom_name: %s | custom_desc: %s",custom_name,custom_desc) self:Close() end function UIWPSettings:OnKeyboard(dik, keyboard_action) local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action) if (res == false) then local bind = dik_to_bind(dik) if keyboard_action == ui_events.WINDOW_KEY_PRESSED then if dik == DIK_keys.DIK_ESCAPE then self:Close() end end end return res end function UIWPSettings:Close() self:HideDialog() Unregister_UI("UIWPSettings") destroy_wp_window() end -- ====================================================================== --[[ PIN SETTINGS WINDOW -- ====================================================================]] GUI_PinOpts = nil function pin_window_is_open() return GUI_PinOpts and GUI_PinOpts:IsShown() end function open_pin_settings(id,args) if not GUI_PinOpts then GUI_PinOpts = UIPinSettingsWnd(id,args) end if GUI_PinOpts and not GUI_PinOpts:IsShown() then GUI_PinOpts:Reset(id) GUI_PinOpts:ShowDialog(true) Register_UI("UIPinSettingsWnd","pin_settings") end end function destroy_pin_window() GUI_PinOpts = nil end -- ====================================================================== class "UIPinSettingsWnd" (CUIScriptWnd) function UIPinSettingsWnd:__init(id,args) super() vl("UIPinSettingsWnd:__init(%s,args passed: %s",id,args ~= nil) if not ((id and id > 0) and pins[id]) then return end if debuglogs and verbose then local pin = pins[id] printf(logprefix.."Initializing settings window for pin %s:\nName: %s\nLocked: %s | Show on HUD %s | Show Label %s | Custom ARGB: %s", id, pin.name, pin.lock, pin.hud, pin.show_label, pin.custom_colors ) if pins[id].custom_colors and pins[id].colors then local colors = pins[id].colors --printf(logprefix.."A: %s | R: %s | G: %s | B: %s",colors.a,colors.r,colors.g,colors.b) end end self.init_time = tostring(get_time_elapsed()) self.next_update = time_global() self.icon_refresh = true --self:BackupTempSettings() self:Reset(id,args) self:InitControls() self:HijackWidget() self:RefreshIcon() self.hint_wnd = utils_ui.UIHint(self) self:AllowMovement(true) end function UIPinSettingsWnd:__finalize() end function UIPinSettingsWnd:BackupTempSettings(restore) if not self.backups then self.backups = {} end if restore then dl("UIPinSettingsWnd | restoring old settings:\nwidget_enabled = %s\nwidget_hide_delay = %s | active_theme = %s",widget_enable,widget_hide_delay,active_theme) widget_enabled = self.backups.enable active_theme = self.backups.active or active_theme widget_hide_delay = self.backups.autohide or widget_hide_delay widget_use_custom_pos = self.backups.use_cust_pos or widget_use_custom_pos widget_custom_pos = self.backups.cust_pos or widget_custom_pos self.backups = {} else dl("UIPinSettingsWnd | storing current settings:\nwidget_enabled = %s\nwidget_hide_delay = %s | active_theme = %s",widget_enable,widget_hide_delay,active_theme) self.backups = { enabled = widget_enabled, autohide = widget_hide_delay, active = active_theme, use_cust_pos= widget_use_custom_pos, cust_pos = widget_custom_pos, } active_theme = "pin_sidebar" widget_hide_delay = 0 widget_use_custom_pos = true end end function UIPinSettingsWnd:HijackWidget() reset_indicator(true,self.dialog) end function UIPinSettingsWnd:InitControls() self:SetWndRect(Frect():set(0,0,1024,768)) self:SetAutoDelete(true) self.empty = "ui_paw_emptytex" self.cap = {} local pin = self.pin or self.pin_id and pins[self.pin_id] self.control_hints = { ["check_hud"] = true, ["check_text"] = true, ["check_lock"] = true, ["check_argb"] = true, ["input"] = true, ["input_a"] = true, ["input_r"] = true, ["input_g"] = true, ["input_b"] = true, ["btn_sidebar"] = true, ["btn_ok"] = true, ["btn_cancel"] = true, ["btn_delete"] = true, ["btn_sound"] = true, } local clrtxt ={ a = ts("st_paw_argb_a"), r = ts("st_paw_argb_r"), g = ts("st_paw_argb_g"), b = ts("st_paw_argb_b") } local xml = CScriptXmlInit() local xmlfile = "paw_input_window.xml" local xmlroot = "pin_dialog" local root = xmlroot..":" local tbar = root.."titlebar" local icon = root.."icon" local paw = tbar..":paw" local icopanel = icon.."_panel" local icoframe = icon.."_frame" local icowater = icon.."_watermark" local caption = root.."caption" local bg = root.."bg_settings" local dialog = root.."pin_settings" local chrome = root.."chrome" local input = root.."input_name" local input_a = root.."input_a" local input_r = root.."input_r" local input_g = root.."input_g" local input_b = root.."input_b" local btn_ok = root.."btn_ok" local cancel = root.."btn_cancel" local delete = root.."btn_delete" local check = root.."check" local listbox = root.."listbox" local opt_icon = root.."opt_icon" local opt_cap = opt_icon..":caption" local sidebar = root.."sidebar" local sb_btn = root.."btn_sidebar" local btn_sound = root.."btn_sound" local head_txt = self.title local lock_txt = ts("ui_mcm_pawsys_pawmenu_lock_pin") local label_txt = ts("ui_mcm_pawsys_pawmenu_show_label") local hud_txt = ts("ui_mcm_pawsys_pawmenu_hud_vis_on") local argb_txt = ts("ui_paw_use_custom_color") local mstt_txt = ts("ui_paw_mapspot_tooltip_mode") local set_curr = ts("ui_mcm_lst_pawsys_pg_"..pawdata.curr_set_name) local ico_curr = ts("ui_mcm_lst_"..pawdata.curr_ico_name) xml:ParseFile (xmlfile) --================================================================-- --Title bar if not self.dialog then self.dialog = xml:InitStatic(dialog, self) self.dialog:SetWndSize(vector2():set(400,300)) self.dialog:SetWndPos(vector2():set(392,200)) self.chrome = xml:InitStatic(chrome, self.dialog) self.chrome:InitTexture("ui_paw_dialog_classic_chrome") local titletab_sh = xml:InitStatic(caption, self.chrome) local titletab = xml:InitStatic(caption, self.chrome) titletab_sh:TextControl():SetText(head_txt) titletab_sh:TextControl():SetFont(GetFontDI()) titletab_sh:TextControl():SetTextColor( GetARGB(255,0,0,0) ) titletab_sh:SetWndPos(vector2():set(18,9)) titletab_sh:InitTexture("ui_paw_fade_cap_bg_full") titletab:TextControl():SetText(head_txt) titletab:TextControl():SetFont(GetFontDI()) titletab:SetWndPos(vector2():set(19,9)) self.tbar = xml:InitStatic(tbar,self.dialog) local paw = xml:InitTextWnd(paw,self.tbar) paw:SetText("PAW".." "..script_version) paw:SetFont(GetFontSmall()) paw:SetWndPos(vector2():set(195,34)) psw_sidebar_state = false show_indicator(false) end --================================================================-- -- Rename/relabel input box vl("Setting up name input box") self.input = xml:InitEditBox(input,self.dialog) self:Register(self.input,"fld_input"..self.init_time) self.input:SetWndSize(vector2():set(190,30)) self.input:SetWndPos(vector2():set(8,7)) vl("Setting name input field to %s",self.pin_text) self.input:SetText(self.pin_text) --==== Icon and Set area initialization ==========================-- -- Icon display if not self.icon_display_panel then self.icon_display_panel = xml:InitStatic(icopanel, self.dialog) self.icon_panel = xml:InitStatic(icowater, self.icon_display_panel) unsquish_aspect(self.icon_panel) local icoframe = xml:InitStatic(icoframe, self.icon_display_panel) unsquish_aspect(icoframe) self.icon = xml:InitStatic(icon, self.icon_display_panel) unsquish_aspect(self.icon) local w = self.icon:GetWidth() self.icon:SetWndPos(vector2():set(11.75,16)) end self:RefreshIcon(self.icon_tex) self.argb_controls = xml:InitStatic(root.."argb_chrome",self.dialog) --================================================================-- local function setup_argb_control(color,pos_x,pos_y) local inputx = "input_"..color vl("Initializing ARGB control %s for color %s at %s,%s",inputx,color,pos_x,pos_y) local xsh = color.."_sh" local xtx = color.."_tx" local xmlnode = root..":"..inputx self.argb_box = {} self.argb_box[color] = xml:InitStatic(xmlnode,self.argb_controls) self.argb_box[color]:SetWndSize(vector2():set(65,31)) unsquish_aspect(self.argb_box[color]) self.argb_box[color]:SetWndPos(vector2():set(pos_x,pos_y)) self.argb_box[color]:InitTexture("ui_paw_dialog_argb_single") self[inputx] = xml:InitEditBox(xmlnode,self.argb_box[color]) self:Register(self[inputx],"fld_input_"..color..self.init_time) --printf("Original method - %s | color: %s | self.colors[%s]",inputx,color,color,self.colors[color]) self.cap[color] = xml:InitStatic(opt_icon,self.argb_box[color]) self.cap[color]:InitTexture("ui_icons_paw_argb_"..color) unsquish_aspect(self.cap[color]) --self.cap[xsh] = xml:InitStatic(opt_cap.."_sh",self.cap[color]) --self.cap[xsh]:TextControl():SetTextColor( GetARGB(255,0,0,0) ) --self.cap[xtx] = xml:InitStatic(opt_cap,self.cap[color]) self:SetARGBVal(color,self.colors[color]) self[inputx]:SetWndPos(vector2():set(18.5,2.5)) local boxframe = xml:InitStatic(xmlnode,self.argb_box[color]) boxframe:SetWndSize(vector2():set(65,31)) unsquish_aspect(boxframe) --boxframe:SetWndPos(vector2():set(pos_x,pos_y)) boxframe:InitTexture("ui_paw_dialog_argb_single_nobg") end local function setup_checkbox(setting,pos_x,pos_y,captext) local checkopt = "check_"..setting vl("Initializing checkbox %s (%s) at %s,%s | %s",checkopt,setting,pos_x,pos_y,captext) self[checkopt] = xml:InitCheck(check,self.dialog) self:Register(self[checkopt], checkopt..self.init_time) self[checkopt]:SetWndPos(vector2():set(pos_x,pos_y)) if captext then self.cap[checkopt] = xml:InitTextWnd(caption,self[checkopt]) self.cap[checkopt]:SetWndPos(vector2():set(30,8)) self.cap[checkopt]:SetText(captext) self.cap[checkopt]:SetVTextAlignment(1) end end function setup_button(button,pos_x,pos_y,xmlnode) local buttonx = "btn_"..button vl("Initializing button %s (%s) at %s,%s",buttonx,button,pos_x,pos_y) self[buttonx] = xml:Init3tButton(xmlnode,self.dialog) self:Register(self[buttonx],buttonx) self[buttonx]:SetWndPos(vector2():set(pos_x,pos_y)) end --[[ -- not ready yet function setup_listbox(setting,pos_x,pos_y,captext,opts) local num_opts = #opts or 0 if num_opts < 1 then vl("Unable to initialize list %s, no opts passed",setting) return end local listopt = "lst_"..setting vl("Initializing list %s at %s,%s with %s options",listopt,pos_x,pos_y,num_opts) self[listopt] = xml:InitComboBox(listbox,self.dialog) self[listopt]:SetWndPos(vector2():set(pos_x,pos_y)) if captext then self.cap[listopt] = xml:InitTextWnd(caption,self[listopt]) self.cap[listopt]:SetWndPos(vector2():set(0,-10)) self.cap[listopt]:SetText(captext) self.cap[listopt]:SetVTextAlignment(1) end local def = pin.tooltip or pin_tooltip_mode local opt,tsopt for i=1,num_opts do tsopt = opts[i][2] tsopt = tsopt and "ui_mcm_lst_pawsys_"..tsopt opt = ts(tsopt) vl("+ Adding list option %s: %s (%s)\n+- %s",i,opts[i][2],opts[i][1],opt) self[listopt]:AddItem(opt,i) if opts[i][1] == def then vl("* +- %s is default value",def) self[listopt]:enable_id(def) self[listopt]:SetText(opt) end self:Register(self[listopt],listopt) end end --]] --================================================================-- -- Setup and place all elements --========================= X ===== Y ============================-- setup_argb_control( "a", 25, 13 ) setup_argb_control( "r", 75, 13 ) setup_argb_control( "g", 125, 13 ) setup_argb_control( "b", 180, 13 ) --[[ -- not ready yet setup_listbox("tooltip", 5, 70, mstt_txt, { {2, "msttmode_text"}, {1, "msttmode_obj"}, {0, "cmenu_off"}, } ) --]] setup_checkbox( "hud", 5, 85, hud_txt ) setup_checkbox( "text", 5, 110, label_txt ) setup_checkbox( "lock", 5, 135, lock_txt ) setup_checkbox( "argb", 5, 160, argb_txt ) self:RefreshChecks() setup_button( "sidebar", 230, -10, sb_btn ) setup_button( "delete", 5.5, 245.5, delete ) setup_button( "ok", 95, 245.5, btn_ok ) setup_button( "cancel", 154, 245.5, cancel ) setup_button( "sound", 235.5, 267.5, btn_sound) --================================================================-- widget_custom_pos = { x= 141, y= 46 } self.icon_display_panel:SetWndPos(vector2():set(150,77)) self.argb_controls:SetWndPos(vector2():set( -5.5, 192)) self:InitCallBacks() self.init_done = true --self.input:CaptureFocus(true) end function UIPinSettingsWnd:InitCallBacks() vl("UIPinSettingsWnd registering callbacks") self:AddCallback("btn_ok", ui_events.BUTTON_CLICKED, self.OnAccept, self) self:AddCallback("btn_cancel", ui_events.BUTTON_CLICKED, self.Close, self) self:AddCallback("btn_delete", ui_events.BUTTON_CLICKED, self.DeletePin, self) self:AddCallback("btn_sidebar", ui_events.BUTTON_CLICKED, self.ToggleSidebar, self) self:AddCallback("btn_sound", ui_events.BUTTON_CLICKED, self.ToggleSound, self) end function UIPinSettingsWnd:GetAnchorStatic() if self.dialog then return self.dialog else dl("ERROR self.dialog was nil!") end end function UIPinSettingsWnd:ApplyIconToPin(icon) icon = icon or pawdata.curr_ico_name pins[self.pin_id].text = pins[self.pin_id].text or pins[self.pin_id].name local text = get_tooltip_for_pin(self.pin_id) local old_icon = pins[self.pin_id].icon pins[self.pin_id].icon = icon change_mapspot(text,self.pin_id,icon,old_icon) self.new_icon = nil self.icon_refresh = true self:RefreshIcon() end function UIPinSettingsWnd:SetARGBVal(color,val) local inputx = "input_"..color --printf("SetARGB method - inputx: %s | color: %s | val %s",inputx,color,val) if color and val and self[inputx] then --printf("Current color for %s is %s, setting to value %s",color,self[inputx]:GetText(),val) self[inputx]:SetText(val) end end function UIPinSettingsWnd:RefreshIcon(texture) if not (texture or self.icon_refresh) then return end if not self.icon then return end vl("UIPinSettingsWnd:RefreshIcon(%s) %s",texture,self.icon_refresh and "| self.icon_refresh flagged" or "",self.icon_refresh) self.icon_curr = pins[self.pin_id].icon or self.icon_curr texture = texture or texture_for_icon(self.icon_curr) if texture then self.icon:InitTexture(texture) self.icon:SetTextureColor(GetARGB(self.colors.a,self.colors.r,self.colors.g,self.colors.b)) self.icon_refresh = false return true else dl("No texture found by UIPinSettingsWnd:RefreshIcon!") return end end function UIPinSettingsWnd:SetSoundIcon() if self.btn_sound then local bstate = use_ui_snd and "on" or "off" self.btn_sound:InitTexture("ui_paw_btn_sound"..bstate) end end function UIPinSettingsWnd:ToggleSound() use_ui_snd = not use_ui_snd vl("Toggling UI sfx to %s",use_ui_snd) self:SetSoundIcon() end function UIPinSettingsWnd:ToggleSidebar() psw_sidebar_state = not psw_sidebar_state vl("Toggling sidebar widget to %s",psw_sidebar_state) GUI_PinInd:ShowDialog(psw_sidebar_state) if psw_sidebar_state then self.btn_sidebar:InitTexture("ui_paw_btn_close_sidebar") else self.btn_sidebar:InitTexture("ui_paw_btn_open_sidebar") end --self.icoset_controls:Show(psw_sidebar_state) play_ui_sound(snd_cycle_blip) end --[[ function UIPinSettingsWnd:Callback_Input(color) vl("UIPinSettingsWnd:Callback_Input for color %s",color) -- largely lifted from MCM local is_color = self.colors[color] and true or false local value = ctrl:GetText() local default = self.default_all local min = 0 local max = 255 local ctrl = is_color and "input" or "input_"..color if not (value and value ~= "") then value = default elseif is_color then value = tonumber(value) if (not value) then ctrl:SetText(default) return end value = clamp(value, min, max) end printf("Input callback got value %s",value) if value ~= ctrl:GetText() then self.cust_argb = true end ctrl:SetText(value) self.colors[color] = value local a = self.colors.a local r = self.colors.r local g = self.colors.g local b = self.colors.b dl("Setting custom ARGB of %s|%s|%s|%s for %s",a,r,g,b) self.icon:SetTextureColor(GetARGB(a,r,g,b)) end --]] function UIPinSettingsWnd:RefreshChecks() if self.check_text then self.check_text:SetCheck(self.show_label) --vl("check_text after refresh: %s",self.check_text:GetCheck()) end if self.check_hud then self.check_hud:SetCheck(self.show_hud) --vl("check_hud after refresh: %s",self.check_hud:GetCheck()) end if self.check_lock then self.check_lock:SetCheck(self.locked) --vl("check_lock after refresh: %s",self.check_lock:GetCheck()) end if self.check_argb then self.check_argb:SetCheck(self.cust_argb) --vl("check_argb after refresh: %s",self.check_argb:GetCheck()) end self:SetSoundIcon() end function UIPinSettingsWnd:Reset(id,args) self.new_icon = nil self.args = args if args and args.wipe then self.icon_changed = false self.set_changed = false self.cust_argb = nil self.title = nil self.pin_id = nil self.pin = nil self.locked = nil self.show_hud = nil self.show_label = nil self.pin_text = nil self.init_time = nil self.icon_tex = nil self.colors = nil self.tooltip = nil self.icon:InitTexture(self.empty) return end self.title = args and args.title or ts("ui_mcm_pawsys_pawmenu_pn_settings") self.pin_id = id self.pin = pins[self.pin_id] self.cust_argb = self.pin.custom_colors self.locked = self.pin.locked self.show_hud = self.pin.hud self.show_label = self.pin.show_label self.tooltip = self.pin.tooltip self.pin_text = self.pin.text or self.pin.name self.init_time = tostring(get_time_elapsed()) self.icon_tex = texture_for_icon(self.pin.icon) self.colors = self.pin.colors if not self.colors then vl("no stored colors in pin %s, defaulting ARGB to 255",id) self.colors = { a = 255, r = 255, g = 255, b = 255 } end for k,v in pairs(self.colors) do self:SetARGBVal(k,v) end self:RefreshChecks() if self.input then self.input:SetText(self.pin_text) end self:RefreshIcon() end function UIPinSettingsWnd:Update() --printf("PAW: psw_sidebar_state: %s | widget_active: %s",psw_sidebar_state,widget_active) if not self.init_done then return end if time_global() < self.next_update then return end self.next_update = time_global() + 10 CUIScriptWnd.Update(self) self.hint = nil for ctrl,enable in pairs(self.control_hints) do if enable and self[ctrl] and self[ctrl]:IsCursorOverWindow() then local st_hint = hint_path..ctrl.."_desc" local hint = ts(st_hint) self.hint = hint --vl("Showing hint for %s: %s\n\"%s\"",ctrl,st_hint,hint) end end self:ShowHint(self.hint) -- locking self.cust_argb = self.check_argb:GetCheck() self.argb_controls:Show(self.cust_argb) self.last_lock = self.locked self.locked = self.check_lock:GetCheck() if self.locked ~= self.last_lock then self.last_lock = self.locked self.btn_delete:Enable(not self.locked) self.check_text:Enable(not self.locked) self.check_hud:Enable(not self.locked) self.check_argb:Enable(not self.locked) --self.input_a:Enable(self.cust_argb and not self.locked) --self.input_r:Enable(self.cust_argb and not self.locked) --self.input_g:Enable(self.cust_argb and not self.locked) --self.input_b:Enable(self.cust_argb and not self.locked) self.input_a:Enable(not self.locked) self.input_r:Enable(not self.locked) self.input_g:Enable(not self.locked) self.input_b:Enable(not self.locked) self.input:Enable(not self.locked) if GUI_PinInd then GUI_PinInd.btn_apply:Enable(not self.locked) end end local text = self.input:GetText() local a,r,g,b = clamp(tonumber(self.input_a:GetText()) or 255,0,255), clamp(tonumber(self.input_r:GetText()) or 255,0,255), clamp(tonumber(self.input_g:GetText()) or 255,0,255), clamp(tonumber(self.input_b:GetText()) or 255,0,255) if not self.colors then self.colors = {} end for c,v in pairs({["a"]=a,["r"]=r,["g"]=g,["b"]=b}) do if self.colors[c] ~= v then self.cust_argb = true end self.colors[c] = v end --self.hint_wnd:Update() self:RefreshIcon() end function UIPinSettingsWnd:OnAccept() local id = self.pin_id dl("UIPinSettingsWnd:OnAccept - applying changes to Pin %s",id) --local old_tooltip = get_tooltip_for_pin(id) --pins[id].tooltip = self["lst_tooltip"] and self["lst_tooltip"]:CurrentID() or pin_tooltip_mode --printf("Tooltip mode: %s",pins[id].tooltip) local new_text = self.input:GetText() local old_text = pins[id].text or pins[id].name pins[id].text = new_text --local hint_is_new = pins[id].tooltip ~= old_tooltip local has_new_text = old_text ~= new_text if has_new_text then --or self.new_icon then --or hint_is_new then --local old_icon = pins[id].icon --pins[id].icon = self.new_icon or pins[id].icon local mapspot_text = get_tooltip_for_pin(id) --dl("UIPinSettingsWnd:OnAccept: Pin %s has new icon or hint, calling change_mapspot\n* Icon: %s\n* Hint: %s",self.pin_id,pins[id].icon,mapspot_text) --change_mapspot(mapspot_text,id,pins[id].icon,old_icon) dl("UIPinSettingsWnd:OnAccept: Pin %s has new icon or hint, calling level.map_change_spot_hint",self.pin_id,pins[id].icon,mapspot_text) level.map_change_spot_hint(id,pins[id].icon,mapspot_text) end pins[id].custom_colors = self.cust_argb if self.cust_argb then local a,r,g,b = clamp(tonumber(self.input_a:GetText()) or 255,0,255), clamp(tonumber(self.input_r:GetText()) or 255,0,255), clamp(tonumber(self.input_g:GetText()) or 255,0,255), clamp(tonumber(self.input_b:GetText()) or 255,0,255) if not pins[id].colors then pins[id].colors = {} end pins[id].colors.a = a pins[id].colors.r = r pins[id].colors.g = g pins[id].colors.b = b set_mapspot_colors(id,pins[id].icon,a,r,g,b) vl("UIPinSettingsWnd:OnAccept: ARGB is %s|%s|%s|%s",a,r,g,b) end self.show_label = self.check_text:GetCheck() if map_labels[id] and new_name then show_pin_label(id,false,true) end show_pin_label(id,self.show_label) vl("UIPinSettingsWnd:OnAccept: show_label is %s",self.show_label) pins[id].hud = self.check_hud:GetCheck() if pins[id].hud then show_hud_pin(id) else hide_hud_pin(id) end vl("UIPinSettingsWnd:OnAccept: show_hud is %s",pins[id].hud) pins[id].locked = self.check_lock:GetCheck() vl("UIPinSettingsWnd:OnAccept: locked is %s",pins[id].locked) play_ui_sound(snd_place_pin) self:Close() end function UIPinSettingsWnd:ShowHint(hint) if hint then hint = psk(hint,text_colors) --vl("* [%s] Showing hint_wnd text: %s",time_global(),hint) end self.hint_wnd:Update(hint) end function UIPinSettingsWnd:OnKeyboard(dik, keyboard_action) local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action) if (res == false) then local bind = dik_to_bind(dik) if keyboard_action == ui_events.WINDOW_KEY_PRESSED then if dik == DIK_keys.DIK_ESCAPE then self:Close() end end end return res end function UIPinSettingsWnd:DeletePin() do_paw_action("pn_del",nil,nil,{syscall=true,id=self.pin_id}) self:Close() end function UIPinSettingsWnd:Close() self:HideDialog() self:Show(false) Unregister_UI("UIPinSettingsWnd") if GUI_PinInd then GUI_PinInd:SettingsMode(false) end reset_indicator() destroy_pin_window() end -- ====================================================================== --[[ ICON AND SET HUD INDICATOR See the hud_themes table and PAW's MCM menu for more info -- ====================================================================]] GUI_PinInd = nil class "UIPAWIndicator" (CUIScriptWnd) function UIPAWIndicator:__init(settings_mode,anchor) super() vl("PAW init called, active theme is %s | widget_active %s | widget_enabled %s | settingsmode %s",active_theme,widget_active,widget_enabled,settingsmode) self.anchor = anchor if settings_mode then self:SettingsMode(true) end if not widget_enabled then return end self.theme = hud_themes[active_theme] self:InitControls() self:DrawIndicator(self.theme) self:InitCallBacks() get_hud():AddDialogToRender(self) end function UIPAWIndicator:__finalize() end function UIPAWIndicator:InitControls() self.pda_active = item_device.is_pda_active() self.icon_curr = get_active_icon() self.set_curr = pawdata.curr_set_name vl("UIPAWIndicator:InitControls: active icon is %s",self.icon_curr) end function UIPAWIndicator:GetAnchorStatic() if self.dialog then dl("Passing widget handler back to calling func") return self.dialog else dl("ERROR self.dialog was nil!") end end function UIPAWIndicator:BackupRestoreSettings(restore) if not self.backups then self.backups = {} end if restore then local pos = self.backups.cust_pos dl("UIPinSettingsWnd | restoring old settings:\nwidget_enabled = %s\nwidget_hide_delay = %s | active_theme = %s | cust pos = %s,%s",widget_enable,widget_hide_delay,active_theme,pos.x,pos.y) widget_enabled = self.backups.enable active_theme = self.backups.active widget_hide_delay = self.backups.autohide widget_use_custom_pos = self.backups.use_cust_pos widget_custom_pos = self.backups.cust_pos self.anchor = self self.backups = {} else local pos = widget_custom_pos dl("UIPinSettingsWnd | storing current settings:\nwidget_enabled = %s\nwidget_hide_delay = %s | active_theme = %s | cust pos = %s,%s",widget_enable,widget_hide_delay,active_theme,pos.x,pos.y) self.backups = { enabled = widget_enabled, autohide = widget_hide_delay, active = active_theme, use_cust_pos= widget_use_custom_pos, cust_pos = widget_custom_pos, } end end function UIPAWIndicator:SettingsMode(onoff) self.settings_mode = onoff and true or false if onoff then self:BackupRestoreSettings() active_theme = "pin_sidebar" widget_hide_delay = 0 widget_use_custom_pos = true widget_enabled = true else self:BackupRestoreSettings(true) end end function UIPAWIndicator:Reset(theme) vl("UIPAWIndicator:Reset called with theme %s",theme and theme.name) if not theme then theme = self.theme end self.dialog:Show(false) self.dialog = nil self:InitControls() self:DrawIndicator(theme) end function UIPAWIndicator:Destroy() get_hud():RemoveDialogToRender(self) end function UIPAWIndicator:SetTheme(theme_name) vl("UIPAWIndicator:SetTheme called with theme_name %s",theme_name) local theme = hud_themes and hud_themes[theme_name] if theme then self.dialog:Show(false) active_theme = theme_name or active_theme self.theme = hud_themes[active_theme] self:Reset(theme) end end function UIPAWIndicator:actor_on_net_destroy() get_hud():RemoveDialogToRender(self) end function UIPAWIndicator:IconPrev() --printf("UIPAWIndicator:IconPrev fired") cycle_icons(-1,false) end function UIPAWIndicator:IconNext() --printf("UIPAWIndicator:IconNext fired") cycle_icons(1,false) end function UIPAWIndicator:SetPrev() --printf("UIPAWIndicator:SetPrev fired") cycle_sets(-1,false) end function UIPAWIndicator:SetNext() --printf("UIPAWIndicator:SetNext fired") cycle_sets(1,false) end function UIPAWIndicator:InitIcosetControls() local xml = CScriptXmlInit() xml:ParseFile("paw_ui_elements.xml") local icoset_c = "icoset_controls" local icoset = icoset_c..":" local btn_iprev = icoset.."btn_ico_prev" local btn_inext = icoset.."btn_ico_next" local btn_sprev = icoset.."btn_set_prev" local btn_snext = icoset.."btn_set_next" local btn_apply = icoset.."btn_apply" self.icoset_controls = xml:InitStatic(icoset_c, self.dialog) self.icon_controls = xml:InitStatic(icoset_c, self.icoset_controls) self.set_controls = xml:InitStatic(icoset_c, self.icoset_controls) --printf("icon name: %s | set name: %s",local_icon_name(),local_set_name()) function setup_icoset_ctrl(icoset_arg, pos_x, pos_y) local btnx_ = "btn_" local stub = string.sub(icoset_arg,1,3) local abbr = string.sub(icoset_arg,1,1) local mwi = "mwi_" local button_prev = btnx_..abbr.."prev" local button_next = btnx_..abbr.."next" local node_prev = icoset..btnx_..stub.."_prev" local node_next = icoset..btnx_..stub.."_next" local icoset_ctrl = icoset_arg.."_controls" local logo = "logo_"..icoset_arg --local text_cap = (abbr == "s") and -- local_set_name() or local_icon_name() --printf("setup_icoset_ctrl(%s,%s,%s) for %s\nstub = %s\nabbr = %s\nbutton_prev = %s\nbutton_next = %s\nnode_prev = %s\nnode_next = %s\nicoset_ctrl = %s\n",icoset_arg, pos_x, pos_y,text_cap,stub,abbr,button_prev,button_next,node_prev,node_next,icoset_ctrl) self[button_prev] = xml:Init3tButton(node_prev, self[icoset_ctrl]) self:Register(self[button_prev],button_prev) self[logo] = xml:InitStatic(icoset..logo, self[icoset_ctrl]) unsquish_aspect(self[logo]) self[button_next] = xml:Init3tButton(node_next, self[icoset_ctrl]) self:Register(self[button_next],button_next) self[mwi..icoset_arg] = xml:InitStatic("mwheel_ind",self.dialog) --self.cap[icoset_arg] = xml:InitStatic(caption,self[button_prev]) --self.cap[icoset_arg]:TextControl():SetText(text_cap) self[icoset_ctrl]:SetWndPos(vector2():set(pos_x,pos_y)) end function setup_button(button,pos_x,pos_y,xmlnode) local buttonx = "btn_"..button vl("Initializing button %s (%s) at %s,%s",buttonx,button,pos_x,pos_y) self[buttonx] = xml:Init3tButton(xmlnode, self.icoset_controls) self:Register(self[buttonx],buttonx) self[buttonx]:SetWndPos(vector2():set(pos_x,pos_y)) end setup_icoset_ctrl( "icon", 92, 34 ) setup_icoset_ctrl( "set", 109.5, 118 ) setup_button( "apply", 82.5, 64, btn_apply ) setwndpos( self.mwi_icon, 145, 8 ) setwndpos( self.mwi_set, 104, 131 ) --================================================================-- self.icoset_controls:Show(self.settings_mode) end function UIPAWIndicator:ApplyIconToPin() GUI_PinOpts:ApplyIconToPin() end function UIPAWIndicator:InitCallBacks() vl("UIPAWIndicator registering callbacks") RegisterScriptCallback("actor_on_net_destroy", self) self:AddCallback("btn_apply", ui_events.BUTTON_CLICKED, self.ApplyIconToPin, self) self:AddCallback("btn_iprev", ui_events.BUTTON_CLICKED, self.IconPrev, self) self:AddCallback("btn_inext", ui_events.BUTTON_CLICKED, self.IconNext, self) self:AddCallback("btn_sprev", ui_events.BUTTON_CLICKED, self.SetPrev, self) self:AddCallback("btn_snext", ui_events.BUTTON_CLICKED, self.SetNext, self) end function UIPAWIndicator:DrawIndicator(theme) vl("UIPAWIndicator:DrawIndicator called with theme %s | self %s",theme and theme.name,self.theme) local pos = theme.pos if widget_use_custom_pos then pos = widget_custom_pos end local xml = CScriptXmlInit() xml:ParseFile(theme.file) --self.dialog = xml:InitStatic(theme.node, self.anchor or self) local anchor = self.anchor or self--GUI_PinOpts and GUI_PinOpts.dialog self.dialog = xml:InitStatic(theme.node, anchor) self.dialog:SetWndPos(vector2():set(pos.x,pos.y)) if theme.tex then self.dialog:InitTexture(theme.tex) end self.box_curr = xml:InitStatic(theme.node..":curr", self.dialog) self.box_curr:InitTexture(texture_for_icon(self.icon_curr)) if theme.style ~= "compact" then self.box_prev = xml:InitStatic(theme.node..":prev", self.dialog) self.box_prev:InitTexture(texture_for_icon(get_prev_icon())) self.box_next = xml:InitStatic(theme.node..":next", self.dialog) self.box_next:InitTexture(texture_for_icon(get_next_icon())) self.box_head = xml:InitStatic(theme.node..":head",self.dialog) self.box_text = xml:InitTextWnd(theme.node..":head",self.box_head) self.box_prev:SetTextureColor(colors.dimmed) self.box_next:SetTextureColor(colors.dimmed) end if theme.style == "full" then self.box_text:SetText(ts("ui_mcm_lst_pawsys_pg_"..self.set_curr)) self.box_head2 = xml:InitStatic(theme.node..":head2",self.dialog) self.box_text2 = xml:InitTextWnd(theme.node..":head2",self.box_head2) self.box_text2:SetText(ts("ui_mcm_lst_"..self.icon_curr)) if self.settings_mode then self.box_text:SetWndPos(vector2():set(0,0)) self.box_text:SetTextAlignment(1) self.box_text2:SetWndPos(vector2():set(0,0)) self.box_text2:SetTextAlignment(1) self:InitIcosetControls() end elseif theme.style == "minimal" then self.box_text:SetText(ts("ui_mcm_lst_"..self.icon_curr)) end vl("UIPAWIndicator:DrawIndicator completed") end function UIPAWIndicator:Update(force) if self.killswitch then return end CUIScriptWnd.Update(self) self.dialog:Show(false) if not ((widget_enabled and main_hud_shown()) or pin_window_is_open()) then return end local theme = self.theme local pda_changed = false if not pin_window_is_open() then if self.pda_active ~= item_device.is_pda_active() then pda_changed = true end if item_device.is_pda_active() then local thm = theme.pda_thm if thm then theme = hud_themes[thm] end end if pda_changed then vl("PDA mode has changed, resetting theme") self:Reset(theme) end else widget_active = psw_sidebar_state end if theme.tex then self.dialog:InitTexture(theme.tex) end local pos = theme.pos if widget_use_custom_pos then pos = widget_custom_pos end self.dialog:SetWndPos(vector2():set(pos.x,pos.y)) local acticon = pawdata.curr_ico_name if (self.icon_curr ~= acticon) or (self.set_curr ~= pawdata.curr_set_name) then vl("UIPAWIndicator:Update - icon/set has changed (new curr icon: %s)",acticon) self.set_curr = pawdata.curr_set_name self.icon_curr = acticon self.box_curr:InitTexture(texture_for_icon(self.icon_curr)) if theme.style ~= "compact" then self.box_prev:InitTexture(texture_for_icon(get_prev_icon())) self.box_next:InitTexture(texture_for_icon(get_next_icon())) end if theme.style == "full" then self.box_text:SetText(ts("ui_mcm_lst_pawsys_pg_"..self.set_curr)) self.box_text2:SetText(ts("ui_mcm_lst_"..self.icon_curr)) elseif theme.style == "minimal" then self.box_text:SetText(ts("ui_mcm_lst_"..self.icon_curr)) end widget_last_used = time_global() widget_active = true end if widget_hide_delay > 0 then if time_global() > widget_last_used + widget_hide_delay then widget_active = false end end self.dialog:Show(widget_active) if widget_active and self.settings_mode then self.mwi_set:Show(false) self.mwi_icon:Show(false) for ctrl,icoset in pairs(hover_elements) do --printf("Checking hover state for %s element %s",icoset,ctrl) if self[ctrl]:IsCursorOverWindow() then self["mwi_"..icoset]:Show(true) end end end end function reset_indicator(force,anchor) vl("Resetting PAW indicator") if GUI_PinInd then GUI_PinInd:Destroy() end if not (force or widget_enabled) then return end vl("Re-initializing PAW indicator") GUI_PinInd = UIPAWIndicator(force,anchor) end function show_indicator(tf) if not (GUI_PinInd and widget_enabled) then return end if tf == nil then widget_active = not widget_active else widget_active = (tf == true) or false end GUI_PinInd:Show(widget_active) end -- ====================================================================== --[[ WAYPOINT AND PIN HUD ICONS Some pieces adapted from GT's "HUD Ubisoft Friendly" addon -- ====================================================================]] function unsquish_aspect(element) local w = element:GetWidth() local h = element:GetHeight() element:SetWndSize(vector2():set((w*unsquish_ratio),h)) end function dist_from_crosshair(position) local toPoint = vector():set(position):sub(device().cam_pos):normalize() return toPoint:dotproduct(device().cam_dir) end --[[ animate_texture( container, 500, {512.5,384.5}, {512.5,384.5}, {0,0}, {200, 200}, 1, 0, function() printf("anim_finished") end, function(x) return x^2 end ) --]] function easing_outquint(x) if not x then return end return 1 - math.pow(1 - x, 5) end function easing_inquint(x) if not x then return end return x * x * x * x * x end function abort_fade_in() return HUD_RET.fading_out or not HUD_RET.fading_in end function abort_fade_out() return HUD_RET.fading_in or not HUD_RET.fading_out end function animate_texture(element, duration, start_pos, end_pos, start_size, end_size, alpha_start, alpha_end , on_finish_func, anim_curve_modifier_func, r, g, b) r = r or 255 g = g or 255 b = b or 255 local anim_state = 0 local start_time = time_global() local end_time = time_global() + duration local anim_curve_modifier_func = anim_curve_modifier_func or function(x) return x end CreateTimeEvent("animate_texture", time_global(), 0, function() local x = anim_curve_modifier_func(anim_state) local pos_x = lerp(start_pos.x, end_pos.x, x) local pos_y = lerp(start_pos.y, end_pos.y, x) local size_x = lerp(start_size.x, end_size.x, x) local size_y = lerp(start_size.y, end_size.y, x) local alpha = lerp(alpha_start, alpha_end, x) if anim_state == 1 then if on_finish_func then on_finish_func() end return true end anim_state = math.min(1, normalize(time_global(), start_time, end_time)) end) end local active_anims = {} function anim_fade(box,alpha_start,alpha_end,duration,event_id,instanced, r, g, b, anim_curve_modifier_func, on_finish_func,abort_func) if not box then return end local fade_dir = (alpha_start > alpha_end) and "out" or "in" dl("anim_fade: start %s end %s dur %s | event_id %s | instanced %s ",alpha_start,alpha_end, duration,event_id,instanced) if (event_id == nil) then event_id = "anim_fade" end local anim_id = event_id.."_"..tostring(time_global()) r = r or 255 g = g or 255 b = b or 255 if active_anims[event_id] then alpha_start = active_anims[event_id].a dl("Repeat invocation of non-instanced fade event, aborting fade-%s and starting new at current alpha (%s)",fade_dir,alpha_start) active_anims[event_id] = nil if on_finish_func then on_finish_func(box,event_id,alpha_end,r,g,b) end --anim_fade(box,alpha_start,alpha_end,duration,event_id,instanced, r, g, b, anim_curve_modifier_func, on_finish_func,abort_func) --return true end local anim_state = 0 local start_time= time_global() local end_time = time_global() + duration local anim_curve_modifier_func = anim_curve_modifier_func or function(x) return x end active_anims[event_id] = { box = box, instanced = instanced and true or false, anim_id = anim_id, start_time = start_time, end_time = end_time, active = true, a_start = alpha_start, a_end = alpha_end, a = alpha_start, r = r, g = g, b = b, } --vl("Creating fade-%s time event %s | %s",fade_dir,event_id,anim_id) CreateTimeEvent("animate_fade"..event_id..anim_id, time_global(), 0, function() local abort = abort_func and abort_func() if abort then vl("Received abort for %s",event_id) anim_state = 1 else local anim = active_anims[event_id] if anim then local x = anim_curve_modifier_func(anim_state) local alpha = lerp(alpha_start, alpha_end, x) anim.a,anim.r,anim.g,anim.b = alpha,r,g,b box:SetTextureColor(GetARGB(alpha,r,g,b)) --vl("Fade-%s time event: setting transparency for %s to %s at time %s",fade_dir,event_id,alpha,time_global()) else vl("Event data for %s has cleared, aborting",event_id) anim_state = 1 end end if (anim_state == 1) then vl("Fade-%s animation %s completed at %s",fade_dir,event_id,time_global()) if on_finish_func then on_finish_func(box,event_id,alpha_end,r,g,b) end return true end anim_state = math.min(1, normalize(time_global(), start_time, end_time)) end) end function fade_by_dist(element,dist,min,mid,max,alpha,r,g,b,mode) -- 0 : fade Static element -- 1 : fade text in Static -- 2 : fade text in TextWnd alpha = alpha or 255 r = r or 255 g = g or 255 b = b or 255 if not (cart_mode and cartmode_unfade) then if (dist <= min) then if dist < min then alpha = alpha * (dist / min) end elseif dist > mid then if (mid > 0) then if (dist > (mid + max)) then alpha = 0 elseif dist > mid then alpha = alpha * (1 - ((dist - mid) / max)) end end end end if mode == 1 then element:TextControl():SetTextColor(GetARGB(alpha,r,g,b)) elseif mode == 2 then element:SetTextColor(GetARGB(alpha,r,g,b)) else element:SetTextureColor(GetARGB(alpha,r,g,b)) end end function scale_by_dist(element,dist,w,h,min,mid,max,unsquish) --printf("scale_by_dist for %sx%s element at dist %s | unsquish %s | %s - %s - %s ",w,h,dist,unsquish,min,mid,max) dist = clamp(dist,0,max) local nw = w local nh = h if (dist <= min) then nw = w + ((1 - (dist / min)) * (w * 0.5)) nh = h + ((1 - (dist / min)) * (h * 0.5)) elseif dist > mid then nw = w - (((dist - mid) / (max - mid)) * (w * 0.4)) -- trying 0.4 rather than 0.3 to bring down min size nh = h - (((dist - mid) / (max - mid)) * (h * 0.4)) end if unsquish then nw = nw * unsquish_ratio end --printf("scale_by_dist for %sx%s element:\nw %s -> nw %s | h %s -> nh %s | ratio: %s",element:GetWidth(),element:GetHeight(),w,nw,h,nh,unsquish_ratio) return nw,nh end local function zoom_delay_over() zoom_delay = false end local zoom_delay = false local last_zoom_coef = 1 local last_zoom_state = false function get_current_zoom_coef() if zoom_delay then return last_zoom_coef end local obj = db.actor:active_item() if (obj and IsWeapon(obj)) then local wpn = obj:cast_Weapon() end local is_zoomed = axr_main.binoc_is_zoomed or (wpn and wpn:IsZoomed()) if is_zoomed ~= last_zoom_state then last_zoom_state = is_zoomed zoom_delay = true CreateTimeEvent("zoomdelay","paw_zoom"..tostring(time_global()),0.25,tasks_placeable_waypoints.zoom_delay_over) return last_zoom_coef end local fov = device().fov local zoom_coef = fov / 85 last_zoom_coef = zoom_coef return zoom_coef end function get_screen_coords(id) local pos = nil local is_npc,is_place local obj = db.storage[id] and db.storage[id].object or level.object_by_id(id) local se_obj = alife_object(id) --printf(" get_screen_coords: id %s -> obj %s (%s) | se_obj %s",id,obj and obj:name(),obj and obj:id(),se_obj and se_obj:name()) if not (obj or se_obj) then local is_wp = (id == (placed_waypoint and placed_waypoint.id)) and (not waypoint_canceling) and end_waypoint_task(id) local pin = pins[id] and do_paw_action("pn_del",se_obj,nil,{syscall=true,id=id}) local exit = (is_wp and " canceling waypoint and") or (pin and " clearing pin and") or "" dl("get_screen_coords: no valid game or server object could be found for %s,%s exiting",id,exit) return end if (obj and IsStalker(obj) or IsMonster(obj)) then pos = obj and utils_obj.safe_bone_pos(obj,"bip01_head") is_npc = true --printf(" Pinned object %s is an npc",id) elseif (se_obj:clsid() == clsid.smart_terrain) or (se_obj:clsid() == clsid.script_zone) then pos = se_obj.position is_place = true --printf(" Pinned object %s is a place",id) elseif (obj and (type(obj.position) == "function")) then local tmp = obj:position() pos = tmp and vector():set(tmp.x,tmp.y,tmp.z) end if (pos == nil) then if not (se_obj and se_obj.online) then return nil elseif (se_obj:clsid() == clsid.online_offline_group_s) then local i = se_obj:commander_id() local npc = i and db.storage[i] and db.storage[i].object or level.object_by_id(i) pos = npc and utils_obj.safe_bone_pos(npc,"bip01_head") is_npc = true --printf(" Pinned object %s is an npc squad",id) else local tmp = se_obj and se_obj.position pos = tmp and vector():set(tmp.x,tmp.y,tmp.z) end end if not ((pos and pos.x) and (pos and pos.x) and (pos and pos.z)) then dl("WARNING: unable to get valid pos for %s",id) return nil end --printf(" pos found for id %s: %s,%s,%s",id,pos and pos.x,pos and pos.y,pos and pos.z) local dist = db.actor:position():distance_to(pos) local true_dist = dist -- store this local min_dist = 0 -- distance floor for proximity adjustment range local near_dist = 15 -- max dist where proximity adjustment range begins local mid_dist = 15 -- dist at which far adjustments begin local max_dist = 60 -- distance ceiling for the far adjustment range local near_adj = 0 -- max icon vertical position change from proximity local far_adj = 1.75 -- max icon vertical position change from distance local vert_adj = 0 -- icon position above object will be adjusted by this if is_place then near_adj = 1.2 -- location spots are usually on or in the ground elseif is_npc then min_dist = 2 -- to allow for npc bounding box near_adj = 0.45 -- max increase in height above npc head from proximity < 12m end local vert_adj = near_adj local dist_prog = 0 -- 0 to 1 progress from beginning to end of adjustment range local dist_coef = 1 -- 0 to 1 multiplier based on distance dist = clamp(dist,min_dist,max_dist) if (dist <= near_dist) then dist_prog = ((near_dist - min_dist) - (dist - min_dist)) * 0.1 dist_coef = math.abs(math.sqrt(math.pow(dist_prog, 2))) vert_adj = near_adj - (near_adj * dist_coef) elseif (dist > mid_dist) then local far_range = max_dist - mid_dist local far_dist = dist - mid_dist if dist >= max_dist then dist_prog = 1 else dist_prog = (far_dist / far_range) end dist_coef = dist_prog -- linear --dist_coef = math.cos(((1 - dist_prog) * math.pi) / 2) -- eastInSine - this one works ok --dist_coef = math.sin((dist_prog * math.pi) / 2) -- easeOutSine --dist_coef = math.abs(math.sqrt(math.pow(dist_prog, 2))) vert_adj = near_adj + (dist_coef * far_adj) end --local zoom_coef = get_current_fov_coef() or 1 local zoom_coef = (device().fov / 85) pos.y = pos.y + 0.575 + (vert_adj * zoom_coef) --printf("* Actor is %s from %s (near_dist %s | max_dist %s\n* dist_prog %s | dist_coef %s\n| Final vertical adjustment: %s",true_dist,id,near_dist,max_dist,dist_prog,dist_coef,vert_adj) local vec = pos and game.world2ui(vector():set(pos.x,pos.y,pos.z),false) --printf("final vec for %s: %s,%s",id,vec and vec.x,vec and vec.y) if (vec and (vec.x ~= -9999) and (vec.y ~= 0)) then return vec end return nil end -- ====================================================================== HUD_MT = nil function show_hud_waypoint() if not db.actor then return end if (HUD_MT == nil) then HUD_MT = UIPAWHUDWaypoint() get_hud():AddDialogToRender(HUD_MT) end end function hide_hud_waypoint() if not db.actor then return end if (HUD_MT ~= nil) then HUD_MT:ShowDialog(false) get_hud():RemoveDialogToRender(HUD_MT) HUD_MT = nil end end function update_hud_waypoint() if not db.actor then return end if (HUD_MT ~= nil) then HUD_MT:Update(true) end end -- ====================================================================== class "UIPAWHUDWaypoint" (CUIScriptWnd) function UIPAWHUDWaypoint:__init() super() self:InitControls() RegisterScriptCallback("actor_on_net_destroy", self) end function UIPAWHUDWaypoint:__finalize() end function UIPAWHUDWaypoint:InitControls() self:SetAutoDelete(true) self.xml = CScriptXmlInit() self.xml:ParseFile("paw_hud_wp_icon.xml") self.marker = self.xml:InitStatic("marker",self) self.dist = self.xml:InitStatic("marker:distance_sh:distance",self) self.dist:TextControl():SetTextColor( GetARGB(255,230,230,230) ) self.dist:TextControl():SetFont(GetFontSmall()) self.dist_sh = self.xml:InitStatic("marker:distance_sh",self.dist) self.dist_sh:TextControl():SetTextColor( GetARGB(255,0,0,0) ) self.dist_sh:TextControl():SetFont(GetFontSmall()) self.dist_sh:SetWndPos(vector2():set(1,1)) self.wref = self.xml:InitStatic("marker:distance_sh:distance",self.dist) self.wref:TextControl():SetFont(GetFontSmall()) self.wref:Show(false) end function UIPAWHUDWaypoint:Destroy() get_hud():RemoveDialogToRender(self) end function UIPAWHUDWaypoint:actor_on_net_destroy() get_hud():RemoveDialogToRender(self) end function UIPAWHUDWaypoint:ShowMarker(onoff) self.dist:Show(onoff and marker_dist_shown("wp")) self.dist_sh:Show(onoff and marker_dist_shown("wp")) self.marker:Show(onoff) end function UIPAWHUDWaypoint:Update() CUIScriptWnd.Update(self) if waypoint_active and (main_hud_shown() or axr_main.binoc_is_zoomed or axr_main.scoped_weapon_is_zoomed) and not waypoint_canceling then local vec = placed_waypoint and placed_waypoint.id and get_screen_coords(placed_waypoint.id) if (vec) then self.marker:SetWndPos(vec) local id = placed_waypoint and placed_waypoint.id local se_obj = id and alife_object(id) if se_obj then local dist = db.actor:position():distance_to(se_obj.position) local h,v = scale_by_dist(self.marker,dist,20,20,50,100,200,true) self.marker:SetWndSize(vector2():set(h,v)) local f_near = wp_near_fade_dist local f_far = wp_far_fade_dist local f_hide = wp_far_hide_dist fade_by_dist(self.marker, dist,f_near,f_far,f_hide) fade_by_dist(self.dist, dist,f_near,f_far,f_hide,255,230,230,230,1) fade_by_dist(self.dist_sh, dist,f_near,f_far,f_hide,255,0,0,0,1) if marker_dist_shown("wp") then local pos = self.marker:GetWndPos() --local dist_txt = string.format("%.1f",dist)..ts("st_paw_meters_abbr") local dist_txt = string.format("%.1f",dist).."m" self.dist:Show(false) self.dist_sh:Show(false) self.dist:TextControl():SetText(dist_txt) self.dist_sh:TextControl():SetText(dist_txt) self.wref:TextControl():SetText(dist_txt) self.wref:AdjustWidthToText() local tw = self.wref:GetWidth() self.dist:SetWndPos(vector2():set(pos.x - (tw / 2) - 1, pos.y + v - 8)) end self:ShowMarker(se_obj.online and true) end return end self:ShowMarker(false) else self:ShowMarker(false) end end -- ====================================================================== hud_pin_objs = {} function update_hud_pins() for k,v in pairs(hud_pin_objs) do v:Update(true) end end function hide_hud_pin(id) if not id then return end if hud_pin_objs[id] then get_hud():RemoveDialogToRender(hud_pin_objs[id]) hud_pin_objs[id] = nil end return true end function show_hud_pin(id) vl("show_hud_pin called for pin ID %s",id) if hud_pin_objs[id] then hide_hud_pin(id) end hud_pin_objs[id] = UIPAWHUDPin(id) get_hud():AddDialogToRender(hud_pin_objs[id]) return true end function init_hud_pins() for k,v in pairs(pins) do local id = v and v.id if id and v.hud then local se_obj = alife_object(id) if se_obj and se_obj.online then show_hud_pin(id) end end end end -- ====================================================================== class "UIPAWHUDPin" (CUIScriptWnd) function UIPAWHUDPin:__init(id) super() self.pin_id = id self.pin_se_obj = alife_object(id) self:InitControls(id) RegisterScriptCallback("actor_on_net_destroy", self) RegisterScriptCallback("on_localization_change", self) self.med_hint_font = (language == "rus") end function UIPAWHUDPin:__finalize() end function UIPAWHUDPin:InitControls(id) self:SetAutoDelete(true) self.xml = CScriptXmlInit() self.xml:ParseFile("paw_hud_pin_icon.xml") self.marker = self.xml:InitStatic("marker",self) local tex = texture_for_icon(pins[id].icon) --local name = pins[id].text or pins[id].name local name = pins[id].name self.marker:InitTexture(tex) local color = colors.active if pins[id].custom_colors then self.custom_argb = pins[id].colors local pcc = self.custom_argb vl("Pin %s has custom ARGB %s,%s,%s,%s",id,pcc.a,pcc.r,pcc.g,pcc.b) color = GetARGB(pcc.a,pcc.r,pcc.g,pcc.b) end self.marker:SetTextureColor(color) -- this may require logic adjustment for transparency interacting with fades self.label = self.xml:InitStatic("marker:label",self) self.label:TextControl():SetTextColor( GetARGB(255,230,230,230) ) self.label_sh = self.xml:InitStatic("marker:label_sh",self.label) self.label_sh:TextControl():SetTextColor( GetARGB(255,0,0,0) ) self.label_sh:SetWndPos(vector2():set(1,1)) self.lref = self.xml:InitStatic("marker:label",self.label) self.label:TextControl():SetText(name) self.label_sh:TextControl():SetText(name) self.lref:TextControl():SetFont(GetFontSmall()) self.lref:Show(false) self:on_localization_change() vl("Adding label %s to pin %s",name,self.pin_id) self.dist = self.xml:InitStatic("marker:distance",self) self.dist:TextControl():SetTextColor( GetARGB(255,230,230,230) ) self.dist:TextControl():SetFont(GetFontSmall()) self.dist_sh = self.xml:InitStatic("marker:distance_sh",self.dist) self.dist_sh:TextControl():SetTextColor( GetARGB(255,0,0,0) ) self.dist_sh:TextControl():SetFont(GetFontSmall()) self.dist_sh:SetWndPos(vector2():set(1,1)) self.wref = self.xml:InitStatic("marker:distance",self.dist) self.wref:TextControl():SetFont(GetFontSmall()) self.wref:Show(false) self.last_dist = nil --local np,dp = self.label:GetWndPos(),self.dist:GetWndPos() --local nx,ny,dx,dy = np.x,np.y,dp.x,dp.y --printf("Init: name pos %s,%s | dist pos %s,%s",nx,ny,dx,dy) end function UIPAWHUDPin:Destroy() hide_hud_pin(self.pin_id) get_hud():RemoveDialogToRender(self) end function UIPAWHUDPin:on_localization_change() self.med_hint_font = (language == "rus") if self.med_hint_font then self.label:TextControl():SetFont(GetFontMedium()) self.label_sh:TextControl():SetFont(GetFontMedium()) self.lref:TextControl():SetFont(GetFontMedium()) else self.label:TextControl():SetFont(GetFontSmall()) self.label_sh:TextControl():SetFont(GetFontSmall()) self.lref:TextControl():SetFont(GetFontSmall()) end end function UIPAWHUDPin:actor_on_net_destroy() get_hud():RemoveDialogToRender(self) end function UIPAWHUDPin:ShowMarker(onoff) self.dist:Show(onoff and marker_dist_shown("pins")) self.dist_sh:Show(onoff and marker_dist_shown("pins")) self.label:Show(onoff and marker_hint_shown("pins")) self.label_sh:Show(onoff and marker_hint_shown("pins")) self.marker:Show(onoff) end function UIPAWHUDPin:Update() --printf("UIPAWHUDPin:Update") if not (pins and pins[self.pin_id]) then self:Destroy() end if item_device.is_pda_active() or not (self.pin_se_obj and self.pin_se_obj.online) then self.marker:Show(false) return end CUIScriptWnd.Update(self) if (main_hud_shown() or axr_main.binoc_is_zoomed or axr_main.scoped_weapon_is_zoomed) then local vec = self.pin_id and get_screen_coords(self.pin_id) if (vec) then local pindist = db.actor:position():distance_to(self.pin_se_obj.position) local szx,szy = scale_by_dist(self.marker,pindist,24,24,10,20,50,true) self.marker:SetWndPos(vec) self.marker:SetWndSize(vector2():set(szx,szy)) local f_near = pin_near_fade_dist local f_far = pin_far_fade_dist local f_hide = pin_far_hide_dist local colors = self.custom_argb or {a=255,r=255,g=255,b=255} fade_by_dist(self.marker, pindist,f_near,f_far,f_hide,colors.a,colors.r,colors.g,colors.b) fade_by_dist(self.label, pindist,f_near,f_far,f_hide,255,230,230,230,1) fade_by_dist(self.label_sh, pindist,f_near,f_far,f_hide,255,0,0,0,1) fade_by_dist(self.dist, pindist,f_near,f_far,f_hide,255,230,230,230,1) fade_by_dist(self.dist_sh, pindist,f_near,f_far,f_hide,255,0,0,0,1) local name = pins[self.pin_id].text local maxd = 100 local disty = pindist if pindist >= maxd then disty = maxd end local yadj = (disty / maxd * 6) local pos = self.marker:GetWndPos() local nx,ny,dx,dy,tw if marker_hint_shown("pins") then self.label:TextControl():SetText(name) self.label_sh:TextControl():SetText(name) self.lref:TextControl():SetText(name) self.lref:AdjustWidthToText() tw = self.lref:GetWidth() nx = pos.x - (tw * .5) ny = pos.y - 22 + yadj --printf("Container for pin %s is %sx%s",name,nx,ny) self.label:SetWndPos(vector2():set(nx, ny)) end if marker_dist_shown("pins") then self.last_dist = pindist --local dist_txt = string.format("%.1f",pindist)..ts("st_paw_meters_abbr") local dist_txt = string.format("%.1f",pindist).."m" self.dist:TextControl():SetText(dist_txt) self.dist_sh:TextControl():SetText(dist_txt) self.wref:TextControl():SetText(dist_txt) self.wref:AdjustWidthToText() tw = self.wref:GetWidth() nx = pos.x - (tw * .5) ny = pos.y + szy - 11 + yadj self.dist:SetWndPos(vector2():set(nx, ny)) end self:ShowMarker(true) return end self:ShowMarker(false) else self:ShowMarker(false) end end -- ====================================================================== -- HUD RETICLE (see ui_paw_reticle.script) -- ====================================================================== HUD_RET = nil function enable_vo_crosshair() if not db.actor then return end if (HUD_RET == nil) then HUD_RET = ui_paw_reticle and ui_paw_reticle.UIPAWReticle() if HUD_RET then get_hud():AddDialogToRender(HUD_RET) else printf(logprefix.."ERROR: Unable to load ui_paw_reticle.script or initialize its HUD class") end end end function disable_vo_crosshair() if not db.actor then return end if (HUD_RET ~= nil) then HUD_RET:ShowDialog(false) get_hud():RemoveDialogToRender(HUD_RET) HUD_RET = nil end end function update_vo_crosshair() if not db.actor then return end if (HUD_RET ~= nil) then HUD_RET:Update(true) end end -- ====================================================================== -- TASK FUNCTORS -- ====================================================================== task_functor.waypoint_task_target_functor = function (task_id,field,p,tsk) if not paw_enabled then return end -- vl("waypoint_task_target_functor called with task_id %s | field %s",task_id,field) if placed_waypoint == nil then dl("WARNING: waypoint_task_target_functor called but placed_waypoint is nil, cancelling waypoint task to avoid crash") end_waypoint_task() return default_id end if (field == "target") then tsk.target = placed_waypoint.id tsk.current_target = placed_waypoint.id return placed_waypoint.id end end task_functor.waypoint_task_text_functor = function (task_id,field,p,tsk) if not (paw_enabled and placed_waypoint) then return end -- vl("waypoint_task_text_functor called with task_id %s | field %s",task_id,field) if (field == "title") then -- vl("custom_name: %s") return custom_name or ts("st_paw_placed_waypoint")..(": "..placed_waypoint.name or "") elseif (field == "descr") then -- vl("custom_desc: %s") return custom_desc or ts("st_paw_proceed") end end -- ====================================================================== -- CALLBACKS -- ====================================================================== function npc_on_death_callback(victim,killer) vl("npc_on_death_callback: %s (%s) | clear_pin_on_death: %s",victim:name(),victim:id(),clear_pin_on_death) if not clear_pin_on_death then return end local id = safeid(victim) if not (id and (id > 0)) then return end if not (id and pins[id]) then return end dl("Clearing existing pin for dead body id %s",id) local se_obj = alife_object(id) do_paw_action("pn_del",se_obj,nil,{syscall=true}) end function update_hud_on_show_hide() update_hud_waypoint() update_vo_crosshair() update_hud_pins() end local mod_keys = { [DIK_keys.DIK_LSHIFT] = {pressed=false,code=1}, [DIK_keys.DIK_LCONTROL] = {pressed=false,code=2}, [DIK_keys.DIK_LMENU] = {pressed=false,code=3}, } local mod_key_codes = { [1] = DIK_keys.DIK_LSHIFT, [2] = DIK_keys.DIK_LCONTROL, [3] = DIK_keys.DIK_LMENU, } local function mod_key_pressed(key) if ui_mcm then return ui_mcm.get_mod_key(key) else return mod_keys and mod_keys[key] and mod_keys[key].pressed end end local function mod_key_held() return mod_keys[DIK_keys.DIK_LSHIFT].pressed or mod_keys[DIK_keys.DIK_LCONTROL].pressed or mod_keys[DIK_keys.DIK_LMENU].pressed end function on_key_press(key) --if not active_binds[key] then return end --printf("Monitored keybind %s pressed",key) local args if mod_keys[key] then mod_keys[key].pressed = true return end --local keyname = DIK_name(key) --vl("on_key_press(%s) : %s\nModifiers held: Shift %s | Ctrl %s | Alt %s",key,keyname,mod_key_pressed(1),mod_key_pressed(2),mod_key_pressed(3)) for k,v in pairs(keybinds) do if v and v.enabled then --local bindtxt = bindtext(v.bind,v.mod) --vl("** Checking keybind %s (bind %s | mod %s | hold %s)",k,bindtxt,v.bind,v.mod,v.hold) if (key == v.bind) and mod_key_pressed(v.mod) then --vl("on_key_press(%s): keybind %s matched %s with bind %s and mod %s",key,k,bindtxt,v.bind,mod) if v.hold then args = true end v.action(args) return end end end --vl("on_key_press: no matching keybind for %s",keyname) end function on_key_release(key) local do_action = false if mod_keys[key] then mod_keys[key].pressed = false end --local keyname = DIK_name(key) --vl("on_key_release(%s) : %s\nModifiers held: Shift %s | Ctrl %s | Alt %s",key,keyname,mod_key_pressed(1),mod_key_pressed(2),mod_key_pressed(3)) for k,v in pairs(keybinds) do if v and v.enabled and v.hold then --local bindtxt = bindtext(v.bind,v.mod) --vl("** Checking keybind %s: %s (bind %s | mod %s | hold %s)",k,bindtxt,v.bind,v.mod,v.hold) if (key == v.bind) and mod_key_pressed(v.mod) then --vl("on_key_release(%s): keybind %s matched %s with bind %s and mod %s",key,k,bindtxt,v.bind,mod) v.action(false) return end end end end function ctrl_alt_rclick(se_obj,map_table) -- not currently implemented - considering uses end function on_map_right_click(property_ui, map_table) if not map_table then return end if map_table.object_id ~= 65535 then return end local pos = map_table.pos local se_obj = register_script_zone(pos,map_table.lvid,map_table.gvid) map_table.object_id = se_obj and se_obj.id if not se_obj then dl(" Right-click capture failed - unable to find or create valid se_obj") return else if mod_key_pressed(1) then -- LSHIFT : unused elseif mod_key_pressed(2) then --if mod_key_pressed(3) then -- CTRL+ALT (unused at this time) -- ctrl_alt_rclick(se_obj,map_table) -- return --end -- LCTRL : Set/Move Waypoint if waypoint_active then do_paw_action("wp_mov",se_obj) else do_paw_action("wp_set",se_obj) end return elseif mod_key_pressed(3) then -- LALT : Add or edit Pin if pins[se_obj.id] then open_pin_settings(se_obj.id) else do_paw_action("pn_add",se_obj) return end end dl(" Passing right-click to get_context_menu_options with id %s and pos %s",se_obj.id,pos) last_clicked_id = se_obj.id get_context_menu_options(property_ui,se_obj.id,map_table) end end function map_spot_menu_add_property(property_ui,id) last_clicked_id = id get_context_menu_options(property_ui,id) end function map_spot_menu_property_clicked(property_ui,id,level_name,prop) execute_context_menu_option(property_ui,id,level_name,prop) end function load_file_data() dl(" Loading file data and populating tables") local attr_tex = "texture" local minimap = "mini_map" local levelmap = "level_map" local ind = 0 xml = CScriptXmlInit() xml:ParseFile("map_spots.xml") for k,v in pairs(icons) do local tex,th,tw local spot = v xml:NavigateToRoot() th = xml:ReadAttribute(spot,0,"height") tw = xml:ReadAttribute(spot,0,"width") xml:NavigateToNode(spot,0) vl("trying to find texture data for %s | %s",k,v) if xml:NodeExist(attr_tex,0) then tex = xml:ReadValue(attr_tex,0) vl("texture node found with value %s",tex) else local spotnode = "" local typ = "" if xml:NodeExist(minimap) then spotnode = xml:ReadAttribute(minimap,0,"spot") typ = minimap end if xml:NodeExist(levelmap) then spotnode = xml:ReadAttribute(levelmap,0,"spot") typ = levelmap end if spotnode ~= "" then xml:NavigateToRoot() th = xml:ReadAttribute(spotnode,0,"height") tw = xml:ReadAttribute(spotnode,0,"width") vl("ReadAttribute for %s: h %s w %s",v,th,tw) xml:NavigateToNode(spotnode,0) tex = xml:ReadValue(attr_tex,0) vl("%s node found: %s",typ,spotnode) end end if spot == "paw_pin_redpush32" then if not tex then return false end end texture_data[spot] = { id = k, t = tex, h = th, w = tw, } ind = ind + 1 icon_index[ind] = { id = k, icon = spot, data = texture_data[spot], } vl("Texture for %s: %s (%sx%s)",spot,tex,tw,th) end current_ico_max = ind for k,v in pairs(texture_data) do dl("texture_data(%s): tex %s | id %s | %sx%s",k,v.t,v.id,v.w,v.h) end ind = 0 for w, _ in pairs(iconset_ltx) do local iconset_ltx_prefix = "iconset_" local icons_pos = string.find(w,iconset_ltx_prefix) if icons_pos == 1 then local group = string.sub(w,icons_pos+string.len(iconset_ltx_prefix)) local set_cfg = icon_sets_ini:collect_section(w) local name = set_cfg.name local def = set_cfg.default local sec_list = group.."_icons" ind = ind + 1 icon_sets[group] = { name = name, group = group, default = def, active_icon = icons[def], i = ind, } local setdata = icon_sets[group] set_index[ind] = { id = group, data = setdata, } dl("Found icon set %s with group %s, section %s",setdata.name,group,sec_list) set_index[ind].ii = {} setdata.ii = {} setdata.ri = {} local iconlist = {} local il = icon_sets_ini:collect_section(sec_list) local i = 0 for k,_ in pairs(il) do i = i + 1 local spot = icons[k] vl("set %s iconlist: icons[%s] = %s",group,k,spot) iconlist[spot] = texture_data[spot].t set_index[ind].ii[i] = spot setdata.ii[i] = spot setdata.ri[spot] = i end setdata.inum = i setdata.icons = iconlist end end current_set_max = ind dl("Loading menu actions from actions_ini") for w, _ in pairs(actions_ltx) do action_codes[w] = actions_ini:collect_section(w) local ac = action_codes[w] local opt_loc = ts(ac.text) dl(" ac.mode: %s | type: %s | as num: %s | >0: %s",ac and ac.mode,ac and type(ac.mode),ac and tonumber(ac.mode),ac and (tonumber(ac.mode) > 0)) ac.mode = clamp(tonumber(ac and ac.mode) or 0,0,4) ac.enable = (ac.enable == "true") action[opt_loc] = w if verbose then vl("Initializing menu action:\n%s = %s",opt_loc,action[opt_loc]) for k,v in pairs(ac) do vl("%s = %s",k,v) end end end dl("Loading and parsing valid clsids (%s)",size_table(clsids_ltx)) for k,v in pairs(clsids_ltx) do dl("%s = %s",k,v) if v == "number" then local k1 = tonumber(k) if k1 and k1 > 0 then vl("Found valid clsid number value: %s",k1) valid_clsids[tonumber(k1)] = true end elseif v == "string" then if k and clsid[k] then local k1 = tonumber(clsid[k]) vl("Found valid clsid string value: %s = %s",k,k1) valid_clsids[tonumber(k1)] = true end end end table.sort(icons) table.sort(icon_sets) catsy_paw_mcm.icon_sets = icon_sets table.sort(action_codes) dl(logprefix.."All file data loaded") --utils_data.print_table(icon_sets) started = true local load_set_curr = axr_main.config:r_value("mcm", "pawsys/pawpins/pin_icon_group", {val=0}) or "pins" local load_pin_curr = axr_main.config:r_value("mcm", "pawsys/pawpins/poi_icon_"..load_set_curr, {val=0}) printf(logprefix.."Personal Adjustable Waypoint %s started at %s",script_version,time_global()) return true end function load_state(data) dl("load_state: Checking for saved PAW data") local ps = data.pawsys local pd = data.pawdata if ps then vl("load_state: pawsys found, loading") placed_waypoint = ps.current_waypoint last_waypoint = ps.last_waypoint custom_name = ps.custom_name custom_desc = ps.custom_desc pins = ps.pins if placed_waypoint then waypoint_active = true end welcome_msg_shown = ps.welcome_msg_shown end if pd and pd.valid then dl("load_state: pawdata found, loading") pawdata = pd pins = pawdata.pins else dl("load_state: pawdata not found, initializing") pawdata.curr_set_name = "pins" pawdata.curr_set_data = icon_sets[pawdata.curr_set_name] local setdata = pawdata.curr_set_data pawdata.curr_set_data = setdata pawdata.curr_set_ind = setdata.i pawdata.curr_ico_name = setdata.active_icon pawdata.curr_ico_ind = setdata.ri[root_patch_name(setdata.active_icon)] vl("pawdata: set %s (%s) | icon %s (%s)",pawdata.curr_set_name,pawdata.curr_set_ind,pawdata.curr_ico_name,pawdata.curr_ico_ind) end if pins_exist() then for k,v in pairs(pins) do pins[k].label = nil end end end function save_state(data) update_mcm_icoset_data() dl("save_state: Cleaning up script zones and temp markers") script_zone_cleanup() dl("save_state: Saving waypoint and config data") data.paw_waypoint_info = nil data.pawsys = {} data.pawsys.current_waypoint = placed_waypoint data.pawsys.last_waypoint = last_waypoint data.pawsys.custom_name = custom_name data.pawsys.custom_desc = custom_desc data.pawsys.dynamic_faves = dynamic_faves data.pawsys.welcome_msg_shown = welcome_msg_shown data.pawsys.pins = {} pawdata.valid = true data.pawdata = pawdata dl("save_state: Saving player's pins") data.pawsys.pins = pins end function actor_on_update() tick() end function actor_on_first_update() on_option_change() if not paw_enabled then UnregisterScriptCallback("actor_on_update",actor_on_update) return end temp_pin_cleanup(true) active_set(icon_sets[current_active_set].i) show_all_pins(true) if waypoint_active then local id = placed_waypoint.id local se_obj = alife_object(id) if not se_obj then return end local savelast = last_waypoint do_paw_action("wp_mov",se_obj) last_waypoint = savelast end reset_indicator() if wp_hud_icon_enabled then show_hud_waypoint() else hide_hud_waypoint() end if (reticle_mode > 0) or (autotag_mode > 0) then enable_vo_crosshair() else disable_vo_crosshair() end init_hud_pins() if not welcome_msg_shown then CreateTimeEvent("paw_welcome_message",0,5,paw_welcome_message) end end function actor_on_net_destroy() script_zone_cleanup() hide_hud_waypoint() empty_table(hud_pin_objs) end function on_mouse_wheel(scroll_dir, flags) if not mwheel_enabled then UnregisterScriptCallback("on_mouse_wheel",on_mouse_wheel) return end local now = time_global() if now < mwheel_next_poll then return end mwheel_next_poll = now + mwheel_poll_interval local cycling_active = (icon_cycle_active or set_cycle_active) local invalid_uistate = actor_menu.inventory_opened() or not cycling_active local pin_wnd_open = pin_window_is_open() --printf("on_mouse_wheel: cycling_active %s | invalid_uistate %s | pin_wnd_open %s",cycling_active,invalid_uistate,pin_wnd_open) if invalid_uistate and not pin_wnd_open then --printf("Invalid UI state for mouse wheel use, aborting") return end flags.ret_value = false local cycle_dir = scroll_dir if cycle_dir == 0 then cycle_dir = -1 end vl("scroll_dir: %s | cycle_dir: %s",scroll_dir,cycle_dir) if pin_wnd_open and GUI_PinInd then for ctrl,icoset in pairs(hover_elements) do --printf("Checking hover state for GUI_PinInd[%s]",ctrl) if GUI_PinInd[ctrl]:IsCursorOverWindow() then cycle_items(cycle_dir,icoset) return end end if not cycling_active then flags.ret_value = true return end end cycle_items(cycle_dir) end --[[ function actor_on_stash_create(data) -- figure out why this causes busyhands with hideout furniture if not use_custom_backpack_icon then return end local id = data and data.stash_id if not id then return end local hint = data and data.stash_name local mapspot = stash_icons["player"] or "treasure" dl("Player placed backpack stash %s (%s), replacing mapspot with %s",hint,id,mapspot) function replace_spot() remove_mapspot(id,stash_icons["basic"]) if init_backpack_as_pin then local sec = data.stash_section init_new_pin(id,sec,hint,mapspot) level.map_add_object_spot(id,mapspot,hint) else level.map_add_object_spot_ser(id,mapspot,hint) end end -- For some reason the backpack UI script will shit itself if you don't delay messing with the mapspots CreateTimeEvent("pawdelay","paw"..tostring(time_global()),0.125,replace_spot) end function actor_on_stash_remove(data) if not use_custom_backpack_icon then return end local id = data and data.stash_id if not id then return end local mapspot = stash_icons["player"] or "treasure" dl("Player picked up backpack stash %s, removing custom mapspot %s",id,mapspot) if init_backpack_as_pin then do_paw_action("pn_del",nil,nil,{syscall=true,id=id}) else remove_mapspot(id,mapspot) end end --]] function unregister_all_callbacks() UnregisterScriptCallback("actor_on_update",actor_on_update) --UnregisterScriptCallback("actor_on_stash_create",actor_on_stash_create) --UnregisterScriptCallback("actor_on_stash_remove",actor_on_stash_remove) UnregisterScriptCallback("actor_on_net_destroy",actor_on_net_destroy) UnregisterScriptCallback("actor_on_first_update",actor_on_first_update) UnregisterScriptCallback("load_state",load_state) UnregisterScriptCallback("save_state",save_state) UnregisterScriptCallback("map_spot_menu_add_property",map_spot_menu_add_property) UnregisterScriptCallback("map_spot_menu_property_clicked",map_spot_menu_property_clicked) UnregisterScriptCallback("on_key_press",on_key_press) UnregisterScriptCallback("on_key_release",on_key_release) UnregisterScriptCallback("GUI_on_show",update_hud_on_show_hide) UnregisterScriptCallback("GUI_on_hide",update_hud_on_show_hide) UnregisterScriptCallback("monster_on_death_callback",npc_on_death_callback) UnregisterScriptCallback("npc_on_death_callback",npc_on_death_callback) if mwheel_avail then UnregisterScriptCallback("on_mouse_wheel",on_mouse_wheel) end if right_click_avail then UnregisterScriptCallback("on_map_right_click",on_map_right_click) end end function register_all_callbacks() RegisterScriptCallback("actor_on_update",actor_on_update) --RegisterScriptCallback("actor_on_stash_create",actor_on_stash_create) --RegisterScriptCallback("actor_on_stash_remove",actor_on_stash_remove) RegisterScriptCallback("actor_on_net_destroy",actor_on_net_destroy) RegisterScriptCallback("actor_on_first_update",actor_on_first_update) RegisterScriptCallback("load_state",load_state) RegisterScriptCallback("save_state",save_state) RegisterScriptCallback("map_spot_menu_add_property",map_spot_menu_add_property) RegisterScriptCallback("map_spot_menu_property_clicked",map_spot_menu_property_clicked) RegisterScriptCallback("on_key_press",on_key_press) RegisterScriptCallback("on_key_release",on_key_release) RegisterScriptCallback("GUI_on_show",update_hud_on_show_hide) RegisterScriptCallback("GUI_on_hide",update_hud_on_show_hide) RegisterScriptCallback("monster_on_death_callback",npc_on_death_callback) RegisterScriptCallback("npc_on_death_callback",npc_on_death_callback) if mwheel_avail then RegisterScriptCallback("on_mouse_wheel",on_mouse_wheel) end if right_click_avail then RegisterScriptCallback("on_map_right_click",on_map_right_click) end end function on_game_start() if load_failed then return end RegisterScriptCallback("on_option_change",on_option_change) register_all_callbacks() on_option_change() end -- ====================================================================== -- LOAD FILE DATA - KILLSWITCH AND NOTIFY ON FAILURE -- ====================================================================== if not load_file_data() then disable_mcm_updates = true load_failed = true paw_enabled = false started = false unregister_all_callbacks() if not disable_load_warning then local tiperr = psk(game.translate_string("st_paw_texture_load_error_tip"),text_colors) RegisterScriptCallback("actor_on_first_update", function() CreateTimeEvent("pawerror",0,5,( function() db.actor:give_game_news("PAW System",tiperr,"ui_inGame2_Mesta_evakuatsii",0,15000) xr_sound.set_sound_play(AC_ID,"pda_tips") return true end )) end ) end local errstr = game.translate_string("st_paw_texture_load_error1").."\n\n"..game.translate_string("st_paw_texture_load_error2") assert(not load_failed, "\n\n".. "~ ------------------------------------------------------------------------\n".. errstr.."\n".. "~ ------------------------------------------------------------------------\n" ) end -- ======================================================================