------------------ -- Private data -- ------------------ -- Locally cache rifle defs for fast and easy access local rifles = {} -- Keep track of players who are scoping in, and their wielded item local scoped = {} -- Timer for scope-check globalstep local timer = 0.2 -- List of IDs for players scoped, for use in hide_scope(). NOTE: for HUD overlay local scoped_hud_id = {} -- Scale constant, for crosshair. This is to ensure the crosshair will be centered. local scale_const = 6 local default_physics_overrides = { speed = 0.1, jump = 0 } ------------- -- Helpers -- ------------- local function show_scope(name, item_name, fov_mult) local player = minetest.get_player_by_name(name) if not player then return end scoped[name] = item_name scoped_hud_id[name] = player:hud_add({ hud_elem_type = "image", position = {x = 0.5, y = 0.5}, offset = {x = (-65*scale_const)/2, y = (-65*scale_const)/2}, text = "rifle_crosshair.png", scale = {x = scale_const, y = scale_const}, alignment = {x = 1, y = 1}, }) -- e.g. if fov_mult == 8, then FOV = 1/8 * current_FOV, a.k.a 8x zoom player:set_fov(1 / fov_mult, true) physics.set(name, "sniper_rifles:scoping", rifles[item_name].physics_overrides) player:hud_set_flags({ wielditem = false }) end local function hide_scope(name) local player = minetest.get_player_by_name(name) if not player then return end scoped[name] = nil player:hud_remove(scoped_hud_id[name]) scoped_hud_id[name] = nil player:set_fov(0) physics.remove(name, "sniper_rifles:scoping") player:hud_set_flags({ wielditem = true }) end -- Be absolutely certain crosshair HUD gets removed on death minetest.register_on_dieplayer(function(player) if scoped_hud_id[player:get_player_name()] then hide_scope(player:get_player_name()) end end) local function on_use(stack, user, pointed) if scoped[user:get_player_name()] then -- shooter checks for the return value of def.on_use, and executes -- the rest of the code only if this function returns non-nil return stack end end local function on_rclick(item, placer, pointed_thing) local name = placer:get_player_name() -- Prioritize on "un-scoping", if player is using the scope if not scoped[name] and pointed_thing.type == "node" then return minetest.item_place(item, placer, pointed_thing) end if scoped[name] then hide_scope(name) else -- Remove _loaded suffix added to item name by shooter local item_name = item:get_name():gsub("_loaded", "") local fov_mult = shooter.registered_weapons[item_name].fov_mult show_scope(name, item_name, fov_mult) end end ------------------ -- Scope-check -- ------------------ -- Hide scope if currently wielded item is not the same item -- player wielded when scoping local time = 0 minetest.register_globalstep(function(dtime) time = time + dtime if time < timer then return end time = 0 for name, original_item in pairs(scoped) do local player = minetest.get_player_by_name(name) if not player then scoped[name] = nil else local wielded_item = player:get_wielded_item():get_name():gsub("_loaded", "") if wielded_item ~= original_item then hide_scope(name) end end end end) ---------------------------- -- Rifle registration API -- ---------------------------- sniper_rifles = {} function sniper_rifles.register_rifle(name, def) assert(def.fov_mult, "Rifle definition must contain FOV multiplier (fov_mult)!") -- Override on_use to allow firing weapon only when using the scope def.on_use = on_use shooter.register_weapon(name, def) -- Manually add extra fields to itemdef that shooter doesn't allow -- Also modify the _loaded variant local overrides = { on_place = on_rclick, on_secondary_use = on_rclick, wield_scale = vector.new(2, 2, 1.5) } minetest.override_item(name, overrides) minetest.override_item(name .. "_loaded", overrides) def.physics_overrides = def.physics_overrides or default_physics_overrides rifles[name] = table.copy(def) end local old_is_protected = minetest.is_protected local r = ctf.setting("flag.nobuild_radius") + 5 local rs = r * r function minetest.is_protected(pos, name, info, ...) if r <= 0 or rs == 0 or not info or not info.is_gun then return old_is_protected(pos, name, info, ...) end local flag, distSQ = ctf_flag.get_nearest(pos) if flag and pos.y >= flag.y - 1 and distSQ < rs then hud_event.new(name, { name = "sniper_rifles:hit_base", color = "warning", value = "You can't shoot blocks within " .. r .. " nodes of a flag!", }) return true else return old_is_protected(pos, name, info, ...) end end dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/rifles.lua")