--TODO: --remove dependency on fdda. Ether move all funcs to patches or add more checks --move instant unequip code from outfit_animations.script into this script ---------------------------------------- -- action example ---------------------------------------- -- function get_action() -- local action = { -- sec = "", --section with animation info -- anm = "", --animation name from animation section, if empty "anm_use" will be used by default -- cam = "", --camera motion name from animation section, if empty "cam" will be used by default -- snd = "", --sound path name from animation section, if empty "snd" will be used by default -- params = { -- delay = 0, --delay before animation will be played -- length = 0, --set custom action length independant of animation length. Example: play idle animation while some audio plays -- can_walk = true, --self explanatory -- hud_fov = 0.6, --self explanatory -- }, -- fnc = { -- [1] = "", -- [2] = "", -- } -- } -- return action -- end ---------------------------------------- -- moneky patches ---------------------------------------- --makes that you can't open backpack while animation palys if zzz_ea_addon_backpack then local originalBMO = zzz_ea_addon_backpack.backpack_m_open zzz_ea_addon_backpack.backpack_m_open = function(name) if lam.is_playing() then return end originalBMO(name) end end --makes that you can't pick up items while animation plays. Was causeing crash to desktop if take_item_anim then local originalAOIBP = take_item_anim.actor_on_item_before_pickup function take_item_anim.actor_on_item_before_pickup(item, flags) if is_playing() then flags.ret_value = false return end originalAOIBP(item, flags) end end ---------------------------------------- -- action list ---------------------------------------- local actions_first = 0 local actions_last = -1 local actions = {} local snd_obj = nil local current_action = nil function add_action(action) actions_last = actions_last + 1 actions[actions_last] = action -- printf("first %s | last %s | length %s", actions_first, actions_last, actions_count()) on_action_added() end function get_next_action() if actions_first > actions_last then return nil end --if list of action is empty local val = actions[actions_first] actions[actions_first] = nil actions_first = actions_first + 1 return val end function actions_count() local count = 0 for i, v in pairs(actions) do count = count + 1 end return count end ---------------------------------------- -- public functions (sorta api i guess) ---------------------------------------- local _is_enabled = false local _is_playing = false function is_enabled() return _is_enabled end function stop_current_action() if is_playing() then game.stop_hud_motion() level.remove_cam_effector(2190) if snd_obj then snd_obj:stop() end if current_action then local fnc = current_action.fnc if fnc then for i,v in pairs(fnc) do RemoveTimeEvent("liz_animation_manager", "play_action_func_" .. i .. "_te") end end RemoveTimeEvent("liz_animation_manager", "play_action_end_te") if current_action.params.can_walk then game.only_allow_movekeys(false) else level.enable_input() end if current_action.params.hud_fov then lam_fov_manager.restore_fov() end end current_action = nil snd_obj = nil on_action_end() end end function stop_all_actions() if is_playing() then --clear action list actions = {} actions_first = 0 actions_last = -1 stop_current_action() end end function is_playing() return _is_playing end function is_able_to_play() --all safety checks go here if not _is_enabled then return false end --script is disabled if not db.actor:alive() then return end --player is dead if has_alife_info("BAR_ARENA_FIGHT") then return end --is fighting on arena if enhanced_animations and enhanced_animations.used_item then return end --possible compatibility issues? return true end --add action to play queue if nothing other plays function try_play_action(action) if not is_enabled() then return false end if is_playing() then return false end add_action(action) return true end --add action to paly queue no matter if something already palys function add_action_to_queue(action) if not is_enabled() then return end add_action(action) end ---------------------------------------- -- callbacks ---------------------------------------- function on_game_start() RegisterScriptCallback("actor_on_first_update", actor_on_first_update) -- RegisterScriptCallback("on_before_key_press", on_before_key_press) end function actor_on_first_update() CreateTimeEvent("liz_animation_manager", "initialize_te", 3, function () _is_enabled = true return true end) end -- function on_before_key_press(dik, bind, dis, flags) -- if is_playing() then -- if dik == DIK_keys.DIK_K then -- stop_current_action() -- elseif dik == DIK_keys.DIK_L then -- stop_all_actions() -- end -- if _lock_keys_allow_movement then -- if -- dik ~= DIK_keys.DIK_W -- and dik ~= DIK_keys.DIK_A -- and dik ~= DIK_keys.DIK_S -- and dik ~= DIK_keys.DIK_D -- then -- flags.ret_value = false -- return -- end -- elseif _lock_keys_all then -- flags.ret_value = false -- return -- end -- end -- end function on_action_added() if is_playing() then return end process_next_action() end function on_action_end() process_next_action() end ---------------------------------------- -- main ---------------------------------------- local cur_slot local det_active function process_next_action() --out of actions show weapon set playing flag to false if actions_count() == 0 then show_weapons() _is_playing = false return end --semething else don't allow to play action, then skip it if not is_able_to_play() then get_next_action() process_next_action() return end if not is_playing() then _is_playing = true hide_weapons() CreateTimeEvent("liz_animation_manager", "wait_for_free_hands_te", 0, function() if db.actor:active_slot() == 0 and not db.actor:active_detector() and (not enhanced_animations or not enhanced_animations.used_item) then -- play_action() CreateTimeEvent("liz_animation_manager", "wait_for_free_hands_delay_te", 0, function () --"fix" for 1.5.1 why it works I do not know ¯\(°_o)/¯ play_action() return true end) return true end return false end) return end CreateTimeEvent("liz_animation_manager", "play_next_action_te", 0.01, function() play_action() return true end) end function hide_weapons() --fdda compatibility if enhanced_animations and (zzz_ea_addon_backpack and zzz_ea_addon_backpack.anim_enabled and zzz_ea_addon_backpack.backpack_open_flag) then cur_slot = zzz_ea_addon_backpack.active_slot det_active = zzz_ea_addon_backpack.det_active zzz_ea_addon_backpack.active_slot = nil zzz_ea_addon_backpack.det_active = nil else cur_slot = db.actor:active_slot() det_active = db.actor:active_detector() or nil if det_active then det_active:switch_state(2) end end hide_hud_inventory() db.actor:activate_slot(0) end function show_weapons() db.actor:activate_slot(cur_slot or 0) if det_active then det_active:switch_state(1) end end -- local ae = actor_effects --sortcut cuz i'm lazy to type full script name every time function play_action() local action = get_next_action() current_action = action local params = action.params or {} local length = params.length or game.get_motion_length(action.sec, action.anm or "anm_use", 1) / 1000 local delay = params.delay or 0 --handle params start if params.can_walk then game.only_allow_movekeys(true) else level.disable_input() end if params.hud_fov then lam_fov_manager.set_fov(0.6) end --handle params end --play animation CreateTimeEvent("liz_animation_manager", "play_action_te", delay, function() game.play_hud_motion(2, action.sec, action.anm or "anm_use", false, 1) local cam = ini_sys:r_string_ex(action.sec, action.cam or "cam") if cam then level.add_cam_effector(cam, 2190, false, "") end local snd = ini_sys:r_string_ex(action.sec, action.snd or "snd") if snd then -- xr_effects.play_snd(db.actor, nil, { [1] = snd }) snd_obj = sound_object(snd) snd_obj:play(db.actor, 0, sound_object.s2d) end return true end) --create time events for all additional actions local last_fnc = 0 local animation_end = delay + length local fnc = action.fnc if fnc then for i,v in pairs(fnc) do if i > last_fnc then last_fnc = i end CreateTimeEvent("liz_animation_manager", "play_action_func_" .. i .. "_te", i/1000, function () assert(loadstring(v))() --this bound to blowup in my face at some point return true end) end end last_fnc = last_fnc / 1000 local action_end = animation_end > last_fnc and animation_end or last_fnc --callback after action ends CreateTimeEvent("liz_animation_manager", "play_action_end_te", action_end, function () --handle params start if params.can_walk then game.only_allow_movekeys(false) else level.enable_input() end if params.hud_fov then lam_fov_manager.restore_fov() end --handle params end current_action = nil snd_obj = nil on_action_end() return true end) -- is it good idea to piggyback on actor_effects code? -- if (time_global() > ae.time_disabled) then -- local tg = tostring(time_global()) -- local te = (delay + length) * 1000 -- ae.item_not_in_use = false -- ae.item_in_use[tg] = action.fnc or {} -- should actually find last object in array. if it fiers after animation then add callback after it -- if params.can_walk then -- ae.item_in_use[tg][te] = "game.only_allow_movekeys(false)" -- else -- ae.item_in_use[tg][te] = "level.disable_input()" -- end -- ae.item_in_use[tg][te + 1] = "liz_anim_manager.on_action_end()" -- for i,v in pairs(ae.item_in_use[tg]) do -- printf("%s = %s", i, v) -- end -- end end -- local _lock_keys_allow_movement = false -- local _lock_keys_all = false