From 7644e3a80f2013207015e75a69725cb27b6eb63c Mon Sep 17 00:00:00 2001 From: OgelGames Date: Tue, 1 Aug 2023 22:32:21 +1000 Subject: [PATCH] hangglider rewrite --- init.lua | 418 ++++++++---------- mod.conf | 2 +- models/{glider.obj => hangglider.obj} | 0 settingtypes.txt | 12 +- sounds/{bedsheet.ogg => hanggliger_equip.ogg} | Bin .../{glider_item.png => hangglider_item.png} | Bin ...ider_struts.png => hangglider_overlay.png} | Bin 7 files changed, 199 insertions(+), 233 deletions(-) rename models/{glider.obj => hangglider.obj} (100%) mode change 100755 => 100644 rename sounds/{bedsheet.ogg => hanggliger_equip.ogg} (100%) rename textures/{glider_item.png => hangglider_item.png} (100%) mode change 100755 => 100644 rename textures/{glider_struts.png => hangglider_overlay.png} (100%) diff --git a/init.lua b/init.lua index 84bab35..1c06a3d 100644 --- a/init.lua +++ b/init.lua @@ -1,76 +1,20 @@ --- Hangglider mod for Minetest --- Original code by Piezo_ (orderofthefourthwall@gmail.com) --- 2018-11-14 --- Modifications by David G (kestral246@gmail.com) --- 2018-11-24 --- For Minetest 5.x, glider's set_attach needs to be offset by 1 node --- Switch to alternate commented line below with correct offset. --- Additional tuning of parameters. --- Commented out debug hud display code, prefixed with "--debug:". +local has_player_monoids = minetest.get_modpath("player_monoids") +local has_areas = minetest.get_modpath("areas") --- 2018-11-22 --- Give visual indication that hangglider is equiped. --- Display simple overlay with blurred struts when equiped. --- Issue: don't know how to disable overlay in third person view. --- Also Unequip hangglider when landing on water. --- Attempt to linearize parabolic flight path. --- Start gravity stronger, but gradually reduce it as descent velocity increases. --- Don't use airstopper when equipped from the ground (descent velocity is low). --- Slightly increase flight speed to 1.25. --- Unequip/equip cycling mid-flight should not fly farther than continuous flight. --- When equipping mid-air (descent velocity higher), use airstopper but increase descent slope afterwards. --- Create airbreak flag so all equips mid-flight use faster descent. --- Reset airbreak flag only when land (canExist goes false). --- Issue: it wouldn't reset if land in water, use fly, and launch from air, before I added test for water, --- not sure if there are other such cases. --- Temporarily add hud debug display to show descent velocity, gravity override, and airbreak flag. --- Still in process of tuning all the parameters. +local enable_hud_overlay = minetest.settings:get_bool("hangglider.enable_hud_overlay", true) +local enable_flak = has_areas and minetest.settings:get_bool("hangglider.enable_flak", true) +local flak_warning_time = tonumber(minetest.settings:get("hangglider.flak_warning_time")) or 2 +local hangglider_uses = tonumber(minetest.settings:get("hangglider.uses")) or 250 +local flak_warning = "You have entered restricted airspace!\n".. + "You will be shot down in "..flak_warning_time.." seconds by anti-aircraft guns!" --- Modifications by Piezo_ --- 2018-11-25 --- hud overlay and debug can be enabled/disabled --- Added blender-rendered overlay for struts using the actual model. --- Reduced airbreak penalty severity --- gave glider limited durability. --- Improved gravity adjustment function. --- Changed airbreaking process --- Removed airbreak penalty, as any 'advantage' seems minimal after new adjustments --- Removed airbreak until minetest devs are smart enough to implement better serverside players. --- Simplified liquid check. +local hanggliding_players = {} +local hud_overlay_ids = {} --- Modifications by gpcf --- 2018-12-09 --- get shot down while flying over protected areas marked as no-fly-zones (flak, from German Flugabwehrkanone) --- set these areas with the /area_flak command - --- Modifications by SpaghettiToastBook --- 2018-12-29 --- Physics overrides use player_monoids mod if available - --- Modifications by SwissalpS --- 2022-05-16 --- Add Z-index to theoretically be behind hotbar and practically behind other HUDs - -local HUD_Overlay = true -- show glider struts as overlay on HUD -local debug = false -- show debug info in top-center of hud -local warning_time = tonumber(minetest.settings:get("hangglider.flak_warning_time")) or 2 -hangglider = {} -- Make this global, so other mods can tell if hangglider exists. -hangglider.use = {} - -if HUD_Overlay then - hangglider.id = {} -- hud id for displaying overlay with struts -end - -if debug then -- hud id for debug data - hangglider.debug = {} -end - -if minetest.get_modpath("areas") then - hangglider.flak = true - -- chat command definition essentially copied from areas mod. - minetest.register_chatcommand("area_flak",{ +if enable_flak then + minetest.register_chatcommand("area_flak", { params = "", description = "Toggle airspace restrictions for area ", func = function(name, param) @@ -78,7 +22,6 @@ if minetest.get_modpath("areas") then if not id then return false, "Invalid usage, see /help area_flak." end - if not areas:isAreaOwner(id, name) then return false, "Area "..id.." does not exist or is not owned by you." end @@ -86,30 +29,81 @@ if minetest.get_modpath("areas") then -- Save false as nil to avoid inflating the DB. areas.areas[id].flak = open or nil areas:save() - return true, ("Area's airspace %s."):format(open and "closed" or "opened") + return true, "Area "..id.." airspace "..(open and "closed" or "opened") end }) end -hangglider.can_fly = function (pname, pos) - -- Checks if the player will get shot down at the position - if areas and hangglider.flak then - local flak = false - local owners = {} - for _, area in pairs(areas:getAreasAtPos(pos)) do - if area.flak then - flak = true - end - owners[area.owner] = true +local function set_hud_overlay(player, name, image) + if not enable_hud_overlay then + return + end + if not hud_overlay_ids[name] then + hud_overlay_ids[name] = player:hud_add({ + hud_elem_type = "image", + text = image, + position = {x = 0, y = 0}, + scale = {x = -100, y = -100}, + alignment = {x = 1, y = 1}, + offset = {x = 0, y = 0}, + z_index = -150 + }) + else + player:hud_change(hud_overlay_ids[name], "text", image) + end +end + +local function set_physics_overrides(player, overrides) + if has_player_monoids then + for name, value in pairs(overrides) do + player_monoids[name]:add_change(player, value, "hangglider:glider") end - if flak and not owners[pname] then - return false + else + player:set_physics_override(overrides) + end +end + +local function remove_physics_overrides(player) + for _, name in pairs({"jump", "speed", "gravity"}) do + if has_player_monoids then + player_monoids[name]:del_change(player, "hangglider:glider") + else + player:set_physics_override({[name] = 1}) end end +end + +local function can_fly(pos, name) + if not enable_flak then + return true + end + local flak = false + local owners = {} + for _, area in pairs(areas:getAreasAtPos(pos)) do + if area.flak then + flak = true + end + owners[area.owner] = true + end + if flak and not owners[name] then + return false + end return true end -hangglider.shot_sound = function (pos) +local function safe_node_below(pos) + local node = minetest.get_node_or_nil(vector.new(pos.x, pos.y - 0.5, pos.z)) + if not node then + return false + end + local def = minetest.registered_nodes[node.name] + if def and (def.walkable or (def.liquidtype ~= "none" and def.damage_per_second <= 0)) then + return true + end + return false +end + +local function shoot_flak_sound(pos) minetest.sound_play("hangglider_flak_shot", { pos = pos, max_hear_distance = 30, @@ -117,177 +111,141 @@ hangglider.shot_sound = function (pos) }, true) end -local has_player_monoids = minetest.get_modpath("player_monoids") - -local physics_attrs = {"jump", "speed", "gravity"} - -local function apply_physics_override(player, overrides) - if has_player_monoids then - for _, attr in pairs(physics_attrs) do - if overrides[attr] then - player_monoids[attr]:add_change(player, overrides[attr], "hangglider:glider") +local function hangglider_step(self, dtime) + local gliding = false + local player = self.object:get_attach("parent") + if player then + local pos = player:get_pos() + local name = player:get_player_name() + if hanggliding_players[name] then + if not safe_node_below(pos) then + gliding = true + local vel = player:get_velocity().y + if vel < 0 and vel > -3 then + set_physics_overrides(player, { + speed = math.abs(vel / 2.0) + 1.0, + gravity = (vel + 3) / 20, + }) + elseif vel <= -3 then + set_physics_overrides(player, { + speed = 2.5, + gravity = -0.25, + }) + player:add_velocity(vector.new(0, 1, 0)) + else -- vel > 0 + set_physics_overrides(player, { + speed = 1.0, + gravity = 0.25, + }) + end end + if not can_fly(pos, name) then + if not self.flak_timer then + self.flak_timer = 0 + shoot_flak_sound(pos) + minetest.chat_send_player(name, flak_warning) + else + self.flak_timer = self.flak_timer + dtime + end + if self.flak_timer > flak_warning_time then + player:set_hp(1, {type = "set_hp", cause = "hangglider:flak"}) + player:get_inventory():remove_item("main", ItemStack("hangglider:hangglider")) + shoot_flak_sound(pos) + gliding = false + end + end + if not gliding then + remove_physics_overrides(player) + hanggliding_players[name] = nil + set_hud_overlay(player, name, "blank.png") + end + end + end + if not gliding then + self.object:set_detach() + self.object:remove() + end +end + +local function hangglider_use(stack, player) + if type(player) ~= "userdata" then + return -- Real players only + end + local pos = player:get_pos() + local name = player:get_player_name() + if not hanggliding_players[name] then + minetest.sound_play("hanggliger_equip", {pos = pos, max_hear_distance = 8, gain = 1.0}, true) + local entity = minetest.add_entity(pos, "hangglider:glider") + if entity then + entity:set_attach(player, "", vector.new(0, 10, 0), vector.new(0, 0, 0)) + set_hud_overlay(player, name, "hangglider_overlay.png") + set_physics_overrides(player, {jump = 0, gravity = 0.25}) + hanggliding_players[name] = true + if hangglider_uses > 0 then + stack:add_wear(65535 / hangglider_uses) + end + return stack end else - player:set_physics_override(overrides) + set_hud_overlay(player, name, "blank.png") + remove_physics_overrides(player) + hanggliding_players[name] = nil end end -local function remove_physics_override(player, overrides) - for _, attr in pairs(physics_attrs) do - if overrides[attr] then - if has_player_monoids then - player_monoids[attr]:del_change(player, "hangglider:glider") - else - player:set_physics_override({[attr] = 1}) - end - end +minetest.register_on_dieplayer(function(player) + local name = player:get_player_name() + hanggliding_players[name] = nil + remove_physics_overrides(player) +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + hanggliding_players[name] = nil + hud_overlay_ids[name] = nil + remove_physics_overrides(player) +end) + +minetest.register_on_player_hpchange(function(player, hp_change, reason) + local name = player:get_player_name() + if hanggliding_players[name] and reason.type == "fall" then + -- Stop all fall damage when hanggliding + return 0, true end -end + return hp_change +end, true) minetest.register_entity("hangglider:glider", { visual = "mesh", visual_size = {x = 12, y = 12}, collisionbox = {0,0,0,0,0,0}, - mesh = "glider.obj", + mesh = "hangglider.obj", immortal = true, static_save = false, textures = {"wool_white.png", "default_wood.png"}, - on_step = function(self, dtime) - local canExist = false - if self.object:get_attach() then - local player = self.object:get_attach("parent") - if player then - local pos = player:get_pos() - local pname = player:get_player_name() - if hangglider.use[pname] then - local mrn_name = minetest.registered_nodes[minetest.get_node(vector.new(pos.x, pos.y-0.5, pos.z)).name] - if mrn_name then - if not (mrn_name.walkable or mrn_name.liquidtype ~= "none") then - canExist = true - local step_v = player:get_velocity().y - if step_v < 0 and step_v > -3 then - apply_physics_override(player, {speed = math.abs(step_v/2) + 0.75}) - elseif step_v <= -3 then -- Cap our gliding movement speed. - apply_physics_override(player, {speed = 2.25}) - else - remove_physics_override(player, {speed = 1}) - end - if debug then - player:hud_change(hangglider.debug[pname].id, "text", step_v..', '.. - player:get_physics_override().gravity) - end - apply_physics_override(player, {gravity = ((step_v + 3)/20)}) - end - end - end - if not hangglider.can_fly(pname,pos) then - if not self.warned then -- warning shot - self.warned = 0 - hangglider.shot_sound(pos) - minetest.chat_send_player(pname, "Protected area! You will be shot down in " .. - warning_time .. " seconds by anti-aircraft guns!") - end - self.warned = self.warned + dtime - if self.warned > warning_time then -- shoot down - player:set_hp(1) - player:get_inventory():remove_item("main", ItemStack("hangglider:hangglider")) - hangglider.shot_sound(pos) - canExist = false - end - end - if not canExist then - remove_physics_override(player, {gravity = 1, jump = 1, speed = 1}) - hangglider.use[pname] = false - if HUD_Overlay then - player:hud_change(hangglider.id[pname], "text", "blank.png") - end - end - end - end - if not canExist then - self.object:set_detach() - self.object:remove() - end - end + on_step = hangglider_step, }) -minetest.register_on_dieplayer(function(player) - remove_physics_override(player, { - gravity = 1, - jump = 1, - }) - hangglider.use[player:get_player_name()] = false -end) - -minetest.register_on_joinplayer(function(player) - local pname = player:get_player_name() - remove_physics_override(player, { - gravity = 1, - jump = 1, - }) - hangglider.use[pname] = false - if HUD_Overlay then - hangglider.id[pname] = player:hud_add({ - hud_elem_type = "image", - text = "blank.png", - position = {x = 0, y = 0}, - scale = {x = -100, y = -100}, - alignment = {x = 1, y = 1}, - offset = {x = 0, y = 0}, - z_index = -150 - }) - end - if debug then - hangglider.debug[pname] = {id = player:hud_add({hud_elem_type = "text", - position = {x = 0.5, y= 0.1 }, - text = "-", - number = 0xFF0000}), -- red text - -- ht = {50,50,50}, - } - end -end) - -minetest.register_on_leaveplayer(function(player) - local pname = player:get_player_name() - hangglider.use[pname] = nil - if HUD_Overlay then hangglider.id[pname] = nil end - if debug then hangglider.debug[pname] = nil end -end) - minetest.register_tool("hangglider:hangglider", { description = "Glider", - inventory_image = "glider_item.png", - stack_max = 1, - on_use = function(itemstack, player) - if not player or player.is_fake_player then - -- player does not exist or is created from an automated machine (fake_player) - return - end - local pos = player:get_pos() - local pname = player:get_player_name() - if not hangglider.use[pname] then -- Equip - minetest.sound_play("bedsheet", {pos = pos, max_hear_distance = 8, gain = 1.0}, true) - if HUD_Overlay then player:hud_change(hangglider.id[pname], "text", "glider_struts.png") end - minetest.add_entity(pos, "hangglider:glider"):set_attach(player, "", {x=0,y=10,z=0}, {x=0,y=0,z=0}) - hangglider.use[pname] = true - apply_physics_override(player, {jump = 0}) - itemstack:set_wear(itemstack:get_wear() + 255) - return itemstack - elseif hangglider.use[pname] then -- Unequip - if HUD_Overlay then player:hud_change(hangglider.id[pname], "text", "default_wood.png^[colorize:#0000:255") end - hangglider.use[pname] = false - end - end, + inventory_image = "hangglider_item.png", sound = {breaks = "default_tool_breaks"}, + on_use = hangglider_use, }) minetest.register_craft({ - type = "shapeless", output = "hangglider:hangglider", - recipe = {"default:paper", "default:paper", "default:paper", - "default:paper", "hangglider:hangglider", "default:paper", - "default:paper", "default:paper", "default:paper" + recipe = { + {"default:paper", "default:paper", "default:paper"}, + {"default:paper", "hangglider:hangglider", "default:paper"}, + {"default:paper", "default:paper", "default:paper"}, + }, +}) + +minetest.register_craft({ + output = "hangglider:hangglider", + recipe = { + {"hangglider:hangglider", "wool:white"}, }, }) diff --git a/mod.conf b/mod.conf index 34c5994..36218d7 100644 --- a/mod.conf +++ b/mod.conf @@ -1,4 +1,4 @@ name = hangglider depends = default, wool optional_depends = areas, player_monoids -min_minetest_version = 5.0 \ No newline at end of file +min_minetest_version = 5.0 diff --git a/models/glider.obj b/models/hangglider.obj old mode 100755 new mode 100644 similarity index 100% rename from models/glider.obj rename to models/hangglider.obj diff --git a/settingtypes.txt b/settingtypes.txt index 6aef999..40e85d2 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -1,3 +1,11 @@ -# How long (in seconds) before hang gliders get shot down when flying over -# protected areas +# The number of times the hangglider can be used before breaking. Set to 0 for infinite uses. +hangglider.uses (Hangglider uses) int 250 + +# If true, an image of the hangglider struts is shown on the hud when gliding. +hangglider.enable_hud_overlay (Enable overlay) bool true + +# If enabled, and the `areas` mod is installed, enables airspace restrictions to be added to areas. +hangglider.enable_flak (Enable flak) bool true + +# Time in seconds before hanggliders get shot down when flying in restricted airspace hangglider.flak_warning_time (Flak warning time) float 2 diff --git a/sounds/bedsheet.ogg b/sounds/hanggliger_equip.ogg similarity index 100% rename from sounds/bedsheet.ogg rename to sounds/hanggliger_equip.ogg diff --git a/textures/glider_item.png b/textures/hangglider_item.png old mode 100755 new mode 100644 similarity index 100% rename from textures/glider_item.png rename to textures/hangglider_item.png diff --git a/textures/glider_struts.png b/textures/hangglider_overlay.png similarity index 100% rename from textures/glider_struts.png rename to textures/hangglider_overlay.png