diff --git a/mods/ITEMS/mcl_bows/bow.lua b/mods/ITEMS/mcl_bows/bow.lua index 174208c3c..6ae64a14e 100644 --- a/mods/ITEMS/mcl_bows/bow.lua +++ b/mods/ITEMS/mcl_bows/bow.lua @@ -33,6 +33,15 @@ local bow_load = {} -- Another player table, this one stores the wield index of the bow being charged local bow_index = {} +-- define FOV modifier(s) +mcl_fovapi.register_modifier({ + name = "bowcomplete", + fov_factor = 0.8, + time = 1, + reset_time = 0.3, + is_multiplier = true, +}) + function mcl_bows.shoot_arrow(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack, collectable) local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity") if power == nil then @@ -183,6 +192,9 @@ end -- Resets the bow charging state and player speed. To be used when the player is no longer charging the bow local function reset_bow_state(player, also_reset_bows) + -- clear the FOV change from the player. + mcl_fovapi.remove_modifier(player, "bowcomplete") -- for the complete zoom in FOV Modifier. + bow_load[player:get_player_name()] = nil bow_index[player:get_player_name()] = nil if minetest.get_modpath("playerphysics") then @@ -314,6 +326,9 @@ controls.register_on_hold(function(player, key, time) end bow_load[name] = minetest.get_us_time() bow_index[name] = player:get_wield_index() + + -- begin Bow Zoom. + mcl_fovapi.apply_modifier(player, "bowcomplete") else if player:get_wield_index() == bow_index[name] then if type(bow_load[name]) == "number" then diff --git a/mods/ITEMS/mcl_bows/mod.conf b/mods/ITEMS/mcl_bows/mod.conf index 7b174826a..0fdd666a3 100644 --- a/mods/ITEMS/mcl_bows/mod.conf +++ b/mods/ITEMS/mcl_bows/mod.conf @@ -1,6 +1,6 @@ name = mcl_bows author = Arcelmi description = This mod adds bows and arrows for MineClone 2. -depends = controls, mcl_particles, mcl_enchanting, mcl_init, mcl_util, mcl_shields +depends = controls, mcl_particles, mcl_enchanting, mcl_init, mcl_util, mcl_shields, mcl_fovapi optional_depends = awards, mcl_achievements, mcl_core, mcl_mobitems, playerphysics, doc, doc_identifier, mesecons_button diff --git a/mods/ITEMS/mcl_spyglass/init.lua b/mods/ITEMS/mcl_spyglass/init.lua index fa1a82339..56b71b961 100644 --- a/mods/ITEMS/mcl_spyglass/init.lua +++ b/mods/ITEMS/mcl_spyglass/init.lua @@ -17,6 +17,15 @@ minetest.register_craft({ } }) +mcl_fovapi.register_modifier({ + name = "spyglass", + fov_factor = 8, + time = 0.1, + reset_time = 0, + is_multiplier = false, + exclusive = true, +}) + local spyglass_scope = {} local function add_scope(player) @@ -37,7 +46,8 @@ local function remove_scope(player) player:hud_remove(spyglass_scope[player]) spyglass_scope[player] = nil player:hud_set_flags({wielditem = true}) - player:set_fov(86.1) + mcl_fovapi.remove_modifier(player, "spyglass") -- use the api to remove the FOV effect. + -- old code: player:set_fov(86.1) end end @@ -55,7 +65,8 @@ controls.register_on_hold(function(player, key, time) if key ~= "RMB" then return end local wielditem = player:get_wielded_item() if wielditem:get_name() == "mcl_spyglass:spyglass" then - player:set_fov(8, false, 0.1) + mcl_fovapi.apply_modifier(player, "spyglass") -- apply the FOV effect. + -- old code: player:set_fov(8, false, 0.1) if spyglass_scope[player] == nil then add_scope(player) end diff --git a/mods/ITEMS/mcl_spyglass/mod.conf b/mods/ITEMS/mcl_spyglass/mod.conf index c13b281e1..6a78e86a5 100644 --- a/mods/ITEMS/mcl_spyglass/mod.conf +++ b/mods/ITEMS/mcl_spyglass/mod.conf @@ -1,4 +1,4 @@ name = mcl_spyglass author = NO11 description = This mod adds a spyglass, which is an item that can be used for zooming in on specific locations. -depends = mcl_core, controls +depends = mcl_core, controls, mcl_fovapi diff --git a/mods/PLAYER/mcl_fovapi/api.md b/mods/PLAYER/mcl_fovapi/api.md new file mode 100644 index 000000000..d4a9cb1fb --- /dev/null +++ b/mods/PLAYER/mcl_fovapi/api.md @@ -0,0 +1,82 @@ +### FOV API + + +* [FOV API](#fov-api) + * [Description](#description) + * [Troubleshooting](#troubleshooting) + * [Modifier Definition](#modifier-definition-) + * [Global MCL_FOVAPI Tables](#global-mclfovapi-tables) + * [Namespaces](#namespaces) + * [Functions](#functions) + + +#### Description +This API defines and applies different Field Of View effects to players via MODIFIERS. + +#### Troubleshooting +In the `init.lua` file for this module, there is a `DEBUG` variable at the top that will turn on logging. +Use it to see what is going on. + +#### Modifier Definition +```lua +def = { + name = name, + fov_factor = fov_factor, + time = time, + reset_time = reset_time, + is_multiplier = is_multiplier, + exclusive = exclusive, + on_start = on_start, + on_end = on_end, +} +``` +* Name: The name of the Modifier, used to identify the specific modifier. Case sensitive. +* FOV Factor: A float value defining the FOV to apply. Can be an absolute or percentage, depending on Exclusive and + Is_Multiplier. +* Time: A float value defining the number of seconds to take when applying the FOV Factor. + Used to smoothly move between FOVs. Use 0 for an immediate FOV Shift. (Transition time.) +* Reset Time: A float value defining the number of seconds to take when removing the FOV Factor. + Used to smoothly move between FOVs. Use 0 for an immediate FOV Shift. (Reset transition time.) + Defaults to `time` if not defined. +* Is Multiplier: A bool value used to specify if the FOV Factor is an absolute FOV value or if it should be a percentage + of the current FOV. Defaults to `true` if not defined. +* Exclusive: A bool value used to specify whether the modifier will override all other FOV modifiers. An example of this + is how the spy glass sets the FOV to be a specific value regardless of any other FOV effects applied. Defaults to + `false` if not defined. +* On Start: the `on_start` is a callback function `on_start(player)` that is called if defined. The parameter `player` + is a ref to the player that had the modifier applied. Called from `mcl_fovapi.apply_modifier` immediately after + the FOV Modifier has been applied. +* On End: the `on_end` is a callback function `on_end(player)` that is called if defined. The parameter `player` + is a ref to the player that had the modifier applied. Called from `mcl_fovapi.remove_modifier` immediately after + the FOV Modifier has been removed. + +Note: passing incorrect values in the definition will have unintended consequences. + +#### Global MCL_FOVAPI Tables +There are three tables that are accessible via the API. They are `registered_modifiers` and `applied_modifiers`. + +`mcl_fovapi.registered_modifiers` has the definitions of all the registered FOV Modifiers. Indexed by Modifier Name. +And, `mcl_fovapi.applied_modifiers` is indexed by the Player Name. It contains the names of all the modifiers applied to the +player. + +#### Namespaces +`mcl_fovapi` is the default API Namespace. + +#### Functions +`mcl_fovapi.register_modifier(def)` + +Used to register a new FOV Modifier for use. Must be called before applying said modifier to a player. +See Modifier Definition for what the parameters are. + +`mcl_fovapi.apply_modifier(player, modifier_name)` + +Used to apply a registered FOV modifier to a player. Takes a reference to the player and the modifier's name (string). + +`mcl_fovapi.remove_modifier(player, modifier_name)` + +Used to remove a specific FOV modifier from a Player. Takes a reference to the player and the modifier's name (string). +Removed immediately. + +`mcl_fovapi.remove_all_modifiers(player)` + +Used to remove all FOV modifiers from a Player. Takes a reference to the Player. FOV change is instantaneous. diff --git a/mods/PLAYER/mcl_fovapi/init.lua b/mods/PLAYER/mcl_fovapi/init.lua new file mode 100644 index 000000000..92815d833 --- /dev/null +++ b/mods/PLAYER/mcl_fovapi/init.lua @@ -0,0 +1,232 @@ +--- +--- Copyright 2023, Michieal. +--- License: GPL3. (Default Mineclone2 License) +--- Created by michieal. +--- DateTime: 12/2/23 5:47 AM +--- + +-- Locals (and cached) +local DEBUG = false -- debug constant for troubleshooting. +local pairs = pairs + +-- Globals +mcl_fovapi = {} + +mcl_fovapi.registered_modifiers = {} +mcl_fovapi.applied_modifiers = {} + +minetest.register_on_joinplayer(function(player) + local player_name = player:get_player_name() + + -- initialization + mcl_fovapi.applied_modifiers[player_name] = {} +end) +minetest.register_on_leaveplayer(function(player) + local player_name = player:get_player_name() + + -- handle clean up + mcl_fovapi.applied_modifiers[player_name] = nil +end) + +function mcl_fovapi.register_modifier(def) + if type(def.name) ~= "string" then + error("Modifier name must be a string") + end + if type(def.fov_factor) ~= "number" then + error("FOV factor must be a number") + end + if type(def.time) ~= "number" then + error("Transition time must be a number") + end + if def.reset_time ~= nil and type(def.reset_time) ~= "number" then + error("Reset time, if provided, must be a number") + end + + if def.on_start ~= nil and type(def.on_start) ~= "function" then + error("Callback on_start must be a function") + end + if def.on_end ~= nil and type(def.on_end) ~= "function" then + error("Callback on_end must be a function") + end + + local mdef = {} + + mdef.fov_factor = def.fov_factor + mdef.time = def.time + mdef.reset_time = def.reset_time or def.time + + if def.is_multiplier == false then mdef.is_multiplier = false + else mdef.is_multiplier = true end + if def.exclusive == true then mdef.exclusive = true + else mdef.exclusive = false end + + mdef.on_start = def.on_start + mdef.on_end = def.on_end + + if DEBUG then + minetest.log("FOV::Modifier Definition Registered:\n" .. dump(def)) + end + + mcl_fovapi.registered_modifiers[def.name] = mdef + +end + +minetest.register_on_respawnplayer(function(player) + mcl_fovapi.remove_all_modifiers(player) +end) + +function mcl_fovapi.apply_modifier(player, modifier_name) + if not player or not modifier_name then + return + end + if mcl_fovapi.registered_modifiers[modifier_name] == nil then + return + end + local player_name = player:get_player_name() + if mcl_fovapi.applied_modifiers and mcl_fovapi.applied_modifiers[player_name] and mcl_fovapi.applied_modifiers[player_name][modifier_name] then + return + end + + for k, _ in pairs(mcl_fovapi.applied_modifiers[player_name]) do + if mcl_fovapi.registered_modifiers[k].exclusive == true then return end + end + + local modifier = mcl_fovapi.registered_modifiers[modifier_name] + if modifier.on_start then + modifier.on_start(player) + end + + mcl_fovapi.applied_modifiers[player_name][modifier_name] = true -- set the applied to be true. + + if DEBUG then + minetest.log("FOV::Player Applied Modifiers :" .. dump(mcl_fovapi.applied_modifiers[player_name])) + end + + if DEBUG then + minetest.log("FOV::Modifier applied to player:" .. player_name .. " modifier: " .. modifier_name) + end + + -- modifier apply code. + if modifier.exclusive == true then + -- if exclusive, reset the player's fov, and apply the new fov. + if modifier.is_multiplier then + player:set_fov(0, false, 0) + end + player:set_fov(modifier.fov_factor, modifier.is_multiplier, modifier.time) + else + -- not exclusive? let's apply it in the mix. + local fov_factor, is_mult = player:get_fov() + if fov_factor == 0 then + fov_factor = 1 + is_mult = true + end + if modifier.is_multiplier or is_mult then + fov_factor = fov_factor * modifier.fov_factor + else + fov_factor = (fov_factor + modifier.fov_factor) / 2 + end + if modifier.is_multiplier and is_mult then + player:set_fov(fov_factor, true, modifier.time) + else + player:set_fov(fov_factor, false, modifier.time) + end + end + +end + +function mcl_fovapi.remove_modifier(player, modifier_name) + if not player or not modifier_name then + return + end + + local player_name = player:get_player_name() + if not mcl_fovapi.applied_modifiers[player_name] + or not mcl_fovapi.applied_modifiers[player_name][modifier_name] then + return + end + + if DEBUG then + minetest.log("FOV::Player: " .. player_name .. " modifier: " .. modifier_name .. "removed.") + end + + mcl_fovapi.applied_modifiers[player_name][modifier_name] = nil + local modifier = mcl_fovapi.registered_modifiers[modifier_name] + + -- check for other fov modifiers, and set them up, or reset to default. + + local applied = {} + for k, _ in pairs(mcl_fovapi.applied_modifiers[player_name]) do + applied[k] = mcl_fovapi.registered_modifiers[k] + end + + local elem = next + if elem(applied) == nil then + player:set_fov(0, false, modifier.reset_time) + return + end + local exc = false + for k, _ in pairs(applied) do + if applied[k].exclusive == true then + exc = applied[k] + break + end + end + + -- handle exclusives. + if exc ~= false then + player:set_fov(exc.fov_factor, exc.is_multiplier, 0) -- we want this to be immediate. + else + -- handle normal fov modifiers. + local fov_factor = 1 + local non_multiplier_added = false + for _, x in pairs(applied) do + if not x.is_multiplier then + if non_multiplier_added then + fov_factor = (fov_factor + x.fov_factor) / 2 + else + non_multiplier_added = true + fov_factor = fov_factor * x.fov_factor + end + else + fov_factor = fov_factor * x.fov_factor + end + end + player:set_fov(fov_factor, not non_multiplier_added, modifier.reset_time) + end + + if mcl_fovapi.registered_modifiers[modifier_name].on_end then + mcl_fovapi.registered_modifiers[modifier_name].on_end(player) + end +end + +function mcl_fovapi.remove_all_modifiers(player) + if not player then + return + end + + local player_name = player:get_player_name() + if DEBUG then + minetest.log("FOV::Player: " .. player_name .. " modifiers have been reset.") + end + + for name, x in pairs(mcl_fovapi.applied_modifiers[player_name]) do + x = nil + if mcl_fovapi.registered_modifiers[name].on_end then + mcl_fovapi.registered_modifiers[name].on_end(player) + end + end + + player:set_fov(0, false, 0) +end + +--[[ +Notes: +set_fov(fov, is_multiplier, transition_time): Sets player's FOV + + fov: FOV value. + is_multiplier: Set to true if the FOV value is a multiplier. Defaults to false. + transition_time: If defined, enables smooth FOV transition. Interpreted as the time (in seconds) to reach target FOV. + If set to 0, FOV change is instantaneous. Defaults to 0. + Set fov to 0 to clear FOV override. + +--]] diff --git a/mods/PLAYER/mcl_fovapi/mod.conf b/mods/PLAYER/mcl_fovapi/mod.conf new file mode 100644 index 000000000..86f174c41 --- /dev/null +++ b/mods/PLAYER/mcl_fovapi/mod.conf @@ -0,0 +1,4 @@ +name = mcl_fovapi +author = Michieal, Herowl +description = An API for handling FOV changes. +depends = mcl_player diff --git a/mods/PLAYER/mcl_player/init.lua b/mods/PLAYER/mcl_player/init.lua index 288b697e1..f3cc782d5 100644 --- a/mods/PLAYER/mcl_player/init.lua +++ b/mods/PLAYER/mcl_player/init.lua @@ -177,7 +177,7 @@ minetest.register_on_joinplayer(function(player) player_textures[name] = { "character.png", "blank.png", "blank.png" } --player:set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, 30) - player:set_fov(86.1) -- see >>> +-- player:set_fov(86.1) -- see >>> end) minetest.register_on_leaveplayer(function(player) diff --git a/mods/PLAYER/mcl_sprint/init.lua b/mods/PLAYER/mcl_sprint/init.lua index 7449ad18c..3d9ef984c 100644 --- a/mods/PLAYER/mcl_sprint/init.lua +++ b/mods/PLAYER/mcl_sprint/init.lua @@ -64,40 +64,24 @@ local function cancelClientSprinting(name) players[name].clientSprint = false end +mcl_fovapi.register_modifier({ + name = "sprint", + fov_factor = 1.1, + time = 0.15, + is_multiplier = true, +}) + local function setSprinting(playerName, sprinting) --Sets the state of a player (0=stopped/moving, 1=sprinting) if not sprinting and not mcl_sprint.is_sprinting(playerName) then return end local player = minetest.get_player_by_name(playerName) - local controls = player:get_player_control() if players[playerName] then players[playerName].sprinting = sprinting - local fov_old = players[playerName].fov - local fov_new = fov_old - local fade_time = .15 - if sprinting == true - or controls.RMB - and string.find(player:get_wielded_item():get_name(), "mcl_bows:bow") - and player:get_wielded_item():get_name() ~= "mcl_bows:bow" then - if sprinting == true then - fov_new = math.min(players[playerName].fov + 0.05, 1.2) - else - fov_new = .7 - players[playerName].fade_time = .3 - end - if sprinting == true then - playerphysics.add_physics_factor(player, "speed", "mcl_sprint:sprint", mcl_sprint.SPEED) - end - elseif sprinting == false - and player:get_wielded_item():get_name() ~= "mcl_bows:bow_0" - and player:get_wielded_item():get_name() ~= "mcl_bows:bow_1" - and player:get_wielded_item():get_name() ~= "mcl_bows:bow_2" then - fov_new = math.max(players[playerName].fov - 0.05, 1.0) - if sprinting == false then - playerphysics.remove_physics_factor(player, "speed", "mcl_sprint:sprint") - end - end - if fov_new ~= fov_old then - players[playerName].fov = fov_new - player:set_fov(fov_new, true, fade_time) + if sprinting then + playerphysics.add_physics_factor(player, "speed", "mcl_sprint:sprint", mcl_sprint.SPEED) + mcl_fovapi.apply_modifier(player, "sprint") + else + playerphysics.remove_physics_factor(player, "speed", "mcl_sprint:sprint") + mcl_fovapi.remove_modifier(player, "sprint") end return true end diff --git a/mods/PLAYER/mcl_sprint/mod.conf b/mods/PLAYER/mcl_sprint/mod.conf index 0d20f80a3..9b9a7366b 100644 --- a/mods/PLAYER/mcl_sprint/mod.conf +++ b/mods/PLAYER/mcl_sprint/mod.conf @@ -1,4 +1,5 @@ name = mcl_sprint author = GunshipPenguin -description = Allows the player to sprint by pressing the “Use” key (default: E). -depends = mcl_playerinfo, playerphysics, mcl_hunger +description = Allows the player to sprint by pressing the “AUX” key (default: E). +depends = mcl_playerinfo, playerphysics, mcl_hunger, mcl_fovapi +optional = mcl_bows \ No newline at end of file