142 lines
6.5 KiB
Lua
142 lines
6.5 KiB
Lua
Guns4d.control_handler = {
|
|
--[[example:
|
|
controls = {
|
|
reload = {
|
|
conditions = { --the list of controls (see lua_api.txt) to call
|
|
"shift",
|
|
"zoom"
|
|
},
|
|
timer = .3,
|
|
mode = "toggle", "hybrid", "hold"
|
|
call_before_timer = false,
|
|
loop = false,
|
|
func=function(active, interrupted, data, busy_controls)
|
|
}
|
|
}
|
|
]]
|
|
ads = false,
|
|
}
|
|
--data table:
|
|
--[[
|
|
{
|
|
continue = bool
|
|
timer = float
|
|
active_ticks = int
|
|
current_mode = "toggle", "hold"
|
|
}
|
|
]]
|
|
local controls = Guns4d.control_handler
|
|
--[[-modify controls (future implementation if needed)
|
|
function controls.modify()
|
|
end]]
|
|
--this function always ends up a mess. I rewrote it here 2 times,
|
|
--and in 3dguns I rewrote it at least 3 times. It's always just...
|
|
--impossible to understand. So if you see ALOT of comments, that's why.
|
|
function controls:update(dt)
|
|
assert(self.instance, "attempt to call object method on a class")
|
|
self.player_pressed = self.player:get_player_control()
|
|
local pressed = self.player_pressed
|
|
local call_queue = {} --so I need to have a "call" queue so I can tell the functions the names of other active controls (busy_list)
|
|
local busy_list = self.busy_list or {} --list of controls that have their conditions met. Has to be reset at END of update, so on_use and on_secondary_use can be marked
|
|
if not (self.gun.rechamber_time > 0 and self.gun.ammo_handler.ammo.next_bullet == "empty") then --check if the gun is being charged.
|
|
for i, control in pairs(self.actions) do
|
|
if not (i=="on_use") and not (i=="on_secondary_use") then
|
|
local def = control
|
|
local data = control.data
|
|
local conditions_met = true
|
|
--check no conditions are false
|
|
for _, key in pairs(control.conditions) do
|
|
if not pressed[key] then conditions_met = false break end
|
|
end
|
|
if conditions_met then
|
|
if (def.mode == "toggle") or (def.mode == "hybrid") then
|
|
data.time_held = data.time_held + dt
|
|
if (not data.toggle_lock) and (data.toggled or (data.time_held > Guns4d.config.control_held_toggle_threshold)) then
|
|
data.toggled = not data.toggled
|
|
data.toggle_lock = true --so it can only be toggled once when conditions are met for a period of time
|
|
data.current_mode = "toggle"
|
|
end
|
|
if (data.current_mode ~= "hold") and (data.time_held > Guns4d.config.control_hybrid_toggle_threshold) and (def.mode=="hybrid") then
|
|
data.current_mode = "hold"
|
|
data.toggled = false
|
|
end
|
|
end
|
|
else
|
|
if def.mode=="hyrbid" then
|
|
data.current_mode = "toggle"
|
|
end
|
|
data.time_held = 0
|
|
data.toggle_lock = false
|
|
end
|
|
--minetest.chat_send_all(data.current_mode)
|
|
--minetest.chat_send_all(tostring((conditions_met and not def.toggle) or data.toggled))
|
|
if (conditions_met and data.current_mode == "hold") or (data.toggled and data.current_mode == "toggle") then
|
|
busy_list[i] = true
|
|
data.timer = data.timer - dt
|
|
--when time is over, if it wasnt continue (or loop is active) then reset and call the function.
|
|
--continue indicates wether the function was called (as active) before last step.
|
|
if data.timer <= 0 and ((not data.continue) or def.loop) then
|
|
data.continue = true
|
|
table.insert(call_queue, {control=def, active=true, interrupt=false, data=data})
|
|
if data.current_mode == "toggle" then
|
|
data.toggled = false
|
|
end
|
|
elseif def.call_before_timer and not data.continue then --this is useful for functions that need to play animations for their progress.
|
|
table.insert(call_queue, {control=def, active=false, interrupt=false, data=data})
|
|
end
|
|
else
|
|
data.continue = false
|
|
--detect interrupts, check if the timer was in progress
|
|
if data.timer ~= def.timer then
|
|
table.insert(call_queue, {control=def, active=false, interrupt=true, data=data})
|
|
data.timer = def.timer
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for i, tbl in pairs(call_queue) do
|
|
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, self.handler.gun, self.handler)
|
|
end
|
|
self.busy_list = {}
|
|
elseif self.busy_list then
|
|
self.busy_list = nil
|
|
end
|
|
end
|
|
function controls:on_use(itemstack, pointed_thing)
|
|
assert(self.instance, "attempt to call object method on a class")
|
|
if self.actions.on_use then
|
|
self.actions.on_use(itemstack, self.handler, pointed_thing)
|
|
end
|
|
end
|
|
function controls:on_secondary_use(itemstack, pointed_thing)
|
|
assert(self.instance, "attempt to call object method on a class")
|
|
if self.actions.on_secondary_use then
|
|
self.actions.on_secondary_use(itemstack, self.handler, pointed_thing)
|
|
end
|
|
end
|
|
---@diagnostic disable-next-line: duplicate-set-field
|
|
function controls.construct(def)
|
|
if def.instance then
|
|
assert(def.actions, "no actions provided")
|
|
assert(def.player, "no player provided")
|
|
def.actions = Guns4d.table.deep_copy(def.actions)
|
|
def.busy_list = {}
|
|
def.handler = Guns4d.players[def.player:get_player_name()]
|
|
def.mode = def.mode or "hold"
|
|
for i, action in pairs(def.actions) do
|
|
if not (i=="on_use") and not (i=="on_secondary_use") then
|
|
action.timer = action.timer or 0
|
|
action.data = {
|
|
timer = action.timer,
|
|
continue = false,
|
|
time_held = 0,
|
|
current_mode = (def.mode=="hybrid" and "toggle") or def.mode
|
|
}
|
|
end
|
|
end
|
|
table.sort(def.actions, function(a,b)
|
|
return #a.conditions > #b.conditions
|
|
end)
|
|
end
|
|
end
|
|
Guns4d.control_handler = Instantiatable_class:inherit(Guns4d.control_handler) |