Merge branch 'canc_reload' into 'main'

Cancel reloading when switching to another weapon and start to reload again when re-grabbing the previous weapon

Closes #2

See merge request zughy-friends-minetest/weapons_lib!3
This commit is contained in:
Zughy 2024-08-24 14:40:05 +00:00
commit 60af7e3a81
2 changed files with 114 additions and 27 deletions

View File

@ -34,6 +34,7 @@ Then there are the built-in weapons_lib parameters:
* `slow_down_user`: (boolean) whether the player should be slowed down when attacking and reloading. Default is `false`.
* **Beware**: changing player's velocity whilst slowed down will break their speed if a fixed value is used. Weapons_lib *cannot* know if there are any external mods interfering, so either rely on multipliers or be very careful when you perform such changes (meaning don't do it if you think they might have a weapon). Related: `wl_slowed_down` metadata
* `can_use_weapon`: (function(player, \<action>)) additional checks about whether the weapon can be used (reloading included). Return `true` to proceed
* Not called with `"zoom"` actions. Zoom is always allowed (TODO remove it?)
* reloading doesn't pass any action
* actions with `continuous_fire` run the check before every bullet/hit
* `can_alter_speed`: (function(player)) additional checks about whether to prevent slowing down/speeding up again the player
@ -47,7 +48,8 @@ Then there are the built-in weapons_lib parameters:
* `total_damage`: the sum of the damage that should have been inflicted to all the targets. weapons_lib cannot know if an external mod altered the damage of the weapon (e.g. via `minetest.register_on_punchplayer(..)`)
* `on_recovery_end`: (function(player, weapon, action)) additional behaviour for when the weapon is ready to be used again
* `on_reload`: (function(player, weapon)) additional behaviour when reloading
* `on_reload_end`: (function(player, weapon)) additional behaviour when the weapon has successfully finished reloading
* `on_reload_end`: (function(player, weapon, skip_refill)) additional behaviour when the weapon has finished reloading
* `skip_refill` is a boolean passed when reloading is interrupted (e.g. when switching to another weapon)
* `action1`: (table) action on left click
* `action2`: (table) action on right click
* `action1_hold`: (table, melee only) action on left click if kept pressed for 0.3s. NOT YET IMPLEMENTED, [waiting for MT](https://gitlab.com/zughy-friends-minetest/weapons_lib/-/issues/7)

View File

@ -16,6 +16,7 @@ local function attack_end() end
local function recovery_end() end
local function zoom() end
local function reload() end
local function reload_cancel() end
local function reload_end() end
local function draw_particles() end
local function wl_debug() end
@ -32,8 +33,9 @@ local debug_p_list = weapons_lib.settings.DEBUG_PLAYERS_LIST
local slow_down_func = {} -- KEY: p_name; VALUE: timer func
local reload_func = {} -- KEY: p_name; VALUE: reload func
local recovery_func = {} -- KEY: p_name; VALUE: recover_func
local data = {} -- KEY: p_name; VALUE: {current_weapon, action_in_progress_weapon = {(string) w_name, (string) actn_name},
-- magazine = {w_name1 = amount, w_name2 = amount}, last_physics = {MT physics params, _was_slowed_down}}
local data = {} -- KEY: p_name; VALUE: {current_weapon, current_stack, action_in_progress_weapon = {(string) w_name, (string) actn_name},
-- magazine = {w_name1 = amount, w_name2 = amount}, last_physics = {MT physics params, _was_slowed_down},
-- reload_timestamp = {time, prev_state, prev_actn}}
local registered_weaps = {} -- KEY: w_name; VALUE: {(int) magazine}
@ -84,7 +86,17 @@ minetest.register_globalstep(function(dtime)
weapons_lib.deactivate_zoom(player, minetest.registered_nodes[prev_w_name])
end
-- (da implementare) se stava caricando o si stava parando, annulla l'azione
-- interrompo eventuale ricarica
if weap_state == 4 then
reload_cancel(pl_name, minetest.registered_nodes[prev_w_name], pl_data.current_stack)
end
-- se il nuovo oggetto è un'arma e sta a secco, comincio la ricarica
if is_weapon and registered_weaps[w_name].magazine and data[pl_name].magazine[w_name] == 0 then
reload(player, minetest.registered_nodes[w_name], wld_item)
end
-- se stava caricando o si stava parando, annulla l'azione
-- TODO: probabilmente meglio far questi controlli sulle funzioni corrispettive,
-- una volta che queste funzionalità saranno presenti. Qua attack_end rischia
-- di far danni come già faceva con weap_state == 2 (con controllo infatti
@ -92,10 +104,17 @@ minetest.register_globalstep(function(dtime)
if weap_state == 1 or weap_state == 5 then
local prev_weap_def = minetest.registered_nodes[prev_w_name]
local actn_name = pl_data.action_in_progress_weapon.actn_name
attack_end(player, prev_weap_def, weapon, prev_weap_def[actn_name]) -- TODO non esiste weapon e non so manco se possa ottenerlo in qualche modo
attack_end(player, prev_weap_def, pl_data.current_stack, prev_weap_def[actn_name])
end
if not is_weapon then
pl_data.current_weapon = nil
pl_data.current_stack = nil
else
pl_data.current_weapon = w_name
pl_data.current_stack = wld_item
end
pl_data.current_weapon = is_weapon and w_name or nil
weapons_lib.HUD_crosshair_update(pl_name, w_name)
end
@ -103,6 +122,12 @@ minetest.register_globalstep(function(dtime)
else
if is_weapon then
pl_data.current_weapon = w_name
pl_data.current_stack = wld_item
-- se la nuova arma sta a secco, comincio la ricarica
if registered_weaps[w_name].magazine and data[pl_name].magazine[w_name] == 0 then
reload(player, minetest.registered_nodes[w_name], wld_item)
end
-- se un'arma stava caricando, il mirino mostralo rosso
weapons_lib.HUD_crosshair_update(pl_name, w_name)
@ -237,17 +262,12 @@ end
function weapons_lib.refill(p_name, weap_def)
function weapons_lib.refill(p_name, weapon)
local weap_def = minetest.registered_nodes[weapon:get_name()]
local p_meta = minetest.get_player_by_name(p_name):get_meta()
if p_meta:get_int("wl_weapon_state") == 4 then
reload_func[p_name]:cancel()
if weap_def.sound_reload then
weapons_lib.stop_sound(weap_def.sound_reload, p_name)
end
reload_end(minetest.get_player_by_name(p_name), weap_def)
reload_cancel(p_name, weap_def, weapon, true)
end
data[p_name].magazine[weap_def.name] = weap_def.magazine
@ -859,8 +879,15 @@ function reload(player, weap_def, weapon) -- TODO: prob da esporre in futuro
weapons_lib.play_sound(weap_def.sound_reload, p_name)
end
local p_data = data[p_name]
local prev_actn = p_data.action_in_progress_weapon and p_data.action_in_progress_weapon.actn_name
-- il marcatore temporale serve in caso la ricarica venga annullata, per capire
-- se delay di prev_actn è finito o meno (e capire se si possono eseguire già altre azioni)
p_data.reload_timestamp = {time = minetest.get_us_time(), prev_state = p_meta:get_int("wl_weapon_state"), prev_actn = prev_actn}
p_data.action_in_progress_weapon = {w_name = weap_def.name, actn_name = nil}
p_meta:set_int("wl_weapon_state", 4)
data[p_name].action_in_progress_weapon = {w_name = weap_def.name, actn_name = nil}
-- rimuovo eventuale zoom
if p_meta:get_int("wl_zooming", 1) then
@ -885,40 +912,98 @@ function reload(player, weap_def, weapon) -- TODO: prob da esporre in futuro
reload_func[p_name] = minetest.after(reload_time, function()
if not minetest.get_player_by_name(p_name) or p_meta:get_int("wl_weapon_state") == 0 then return end -- se è morto nel mentre (meta), annullo
reload_end(player, weap_def)
reload_end(player, weap_def, weapon)
end)
end
function reload_end(player, weap_def)
function reload_cancel(p_name, weap_def, weapon, refill_weapon)
wl_debug(p_name, "Cancel reloading | (" .. weap_def.description .. ")")
reload_func[p_name]:cancel()
if weap_def.sound_reload then
weapons_lib.stop_sound(weap_def.sound_reload, p_name)
end
reload_end(minetest.get_player_by_name(p_name), weap_def, weapon, not refill_weapon)
end
function reload_end(player, weap_def, weapon, skip_refill)
local p_meta = player:get_meta()
local p_name = player:get_player_name()
p_meta:set_int("wl_weapon_state", 0)
wl_debug(p_name, "Terminate reloading | (" .. weap_def.description .. ")")
if weap_def.slow_down_user
and (not weap_def.can_alter_speed or weap_def.can_alter_speed(player))
and p_meta:get_int("wl_is_speed_locked") == 0 then
speed_up(player, weap_def.mod)
local p_data = data[p_name]
-- se stava ricaricando subito dopo lo sparo e cambia subito arma (quindi ricarica
-- annullata), deve rimanere in fase di recupero così da non permettere alle altre
-- armi di sparare immediatamente, oltre che a mantenere la velocità rallentata
if skip_refill and p_data.reload_timestamp.prev_actn then
local timestamp = p_data.reload_timestamp
local time_passed = (minetest.get_us_time() - timestamp.time) / 1000000
local action = weap_def[timestamp.prev_actn]
-- TODO: in verità se prev_state è 1 o 3 bisogna sottrarre il tempo già trascorso
-- per vedere quanto ritardo rimane ancora. Per farlo serve prendere dal processo
-- il tempo già passato tramite https://github.com/minetest/minetest/issues/15047.
-- Al momento, invece, parte sempre da 0, creando un ritardo leggermente maggiore
-- se x es. si ricarica, si aspetta qualche decimo e si cambia arma (si andrà
-- lentɜ di quei decimi in più). Preferisco questo ritardo maggiorato al non fare
-- affatto il controllo, che porterebbe lə giocante a velocizzarsi subito appena
-- cambia arma, anche se stava in fase di caricamento/recupero
local delay = get_val(weapon, action, "delay") -- Non considero un math.max(delay, 0.5) perché sono tempistiche irrisorie che complicherebbero soltanto il codice
-- passo in fase di recupero
if time_passed < delay then
p_meta:set_int("wl_weapon_state", 3)
minetest.after(delay-time_passed, function()
if not minetest.get_player_by_name(p_name) then return end
recovery_end(player, weap_def, action)
end)
else
-- TODO: semplifica qui in modo da non ripetere anche in basso
p_meta:set_int("wl_weapon_state", 0)
if weap_def.slow_down_user
and (not weap_def.can_alter_speed or weap_def.can_alter_speed(player))
and p_meta:get_int("wl_is_speed_locked") == 0 then
speed_up(player, weap_def.mod)
end
end
else
p_meta:set_int("wl_weapon_state", 0)
if weap_def.slow_down_user
and (not weap_def.can_alter_speed or weap_def.can_alter_speed(player))
and p_meta:get_int("wl_is_speed_locked") == 0 then
speed_up(player, weap_def.mod)
end
end
-- TODO: bisognerebbe controllare e disattivare durante la rimozione dell'arma,
-- ma questa può avvenire in più modi (via codice, spostamento inventario, Q)
if not player:get_inventory():contains_item("main", weap_def.name) then return end
local curr_weap = data[p_name].current_weapon
if not skip_refill then
p_data.magazine[weap_def.name] = weap_def.magazine
end
local curr_weap = p_data.current_weapon
data[p_name].magazine[weap_def.name] = weap_def.magazine
weapons_lib.HUD_crosshair_update(p_name, curr_weap, false)
if weap_def.on_reload_end then
weap_def.on_reload_end(player, weap_def)
weap_def.on_reload_end(player, weap_def, skip_refill)
end
data[p_name].action_in_progress_weapon = nil
p_data.action_in_progress_weapon = nil
end