Hangglider rewrite and improvements (#11)

* hangglider rewrite

* correct job name and use faster luacheck action

* more powerful airbrake and less antigravity

* optimize textures

* Update .luacheckrc

* update item texture to match actual appearance

* up to 256 colors of glider with only one recipe

* fix mtg dye colors not working

* almost forgot to add a readme

* export ignore

* Update mod.conf

* Update README.md
This commit is contained in:
OgelGames 2023-08-03 16:33:36 +10:00 committed by GitHub
parent 6f837e9e44
commit c2f339c0a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 353 additions and 261 deletions

5
.gitattributes vendored Normal file
View File

@ -0,0 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
# Exclusions for release export
.* export-ignore

View File

@ -1,13 +1,10 @@
name: luacheck
on: [push, pull_request]
jobs:
build:
luacheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./
- name: Checkout
uses: actions/checkout@master
- name: Luacheck
uses: lunarmodules/luacheck@master

View File

@ -1,13 +1,11 @@
globals = {
"hangglider",
"areas"
"areas",
}
read_globals = {
-- Minetest
"minetest",
"vector", "ItemStack",
-- Mod deps
"player_monoids"
"player_monoids",
"unifieddyes",
}

33
README.md Normal file
View File

@ -0,0 +1,33 @@
# Hang Glider [hangglider]
[![luacheck](https://github.com/mt-mods/hangglider/workflows/luacheck/badge.svg)](https://github.com/mt-mods/hangglider/actions)
[![ContentDB](https://content.minetest.net/packages/mt-mods/hangglider/shields/downloads/)](https://content.minetest.net/packages/mt-mods/hangglider/)
Adds a functional hang glider for exploring. Also works as a parachute to save yourself when falling.
Rewritten and improved fork of https://notabug.org/Piezo_/minetest-hangglider.
![](screenshot.png?raw=true)
## Usage
To deploy the hang glider, hold the item in your hand and use it (left-click). The same action also closes the hang glider when it's deployed.
While deployed you can glide around just like walking in the air. Your decent will be slowed until you land on a safe node, which can be any solid node, or a safe liquid like water. Upon landing the hang glider will automatically close.
## Coloring
You can color the hang glider by crafting it with any dye. Also supports all `unifieddyes` colors.
Note that the color will only be visible on the item if you are using Minetest 5.8.0 or above.
## Repairing
The hang glider will wear out every time you use it. The hang glider can be repaired by crafting it with wool or paper, or any other method used to repair tools.
## Area Flak
If the `areas` mod is installed, airspace restrictions can be added to areas using the `/area_flak` command.
When using a hang glider in an area with flak enabled, you will get shot down a few seconds after entering the area, this reduces your HP to 1 and destroys your hang glider.

106
crafts.lua Normal file
View File

@ -0,0 +1,106 @@
local has_unifieddyes = minetest.get_modpath("unifieddyes")
local dye_colors = {
white = "ffffff",
grey = "888888",
dark_grey = "444444",
black = "111111",
violet = "8000ff",
blue = "0000ff",
cyan = "00ffff",
dark_green = "005900",
green = "00ff00",
yellow = "ffff00",
brown = "592c00",
orange = "ff7f00",
red = "ff0000",
magenta = "ff00ff",
pink = "ff7f9f",
}
local function get_dye_color(name)
local color
if has_unifieddyes then
color = unifieddyes.get_color_from_dye_name(name)
end
if not color then
color = string.match(name, "^dye:(%w+)$")
if color then
color = dye_colors[color]
end
end
return color
end
local function get_color_name(name)
name = string.gsub(name, "^dye:", "")
name = string.gsub(name, "_", " ")
name = string.gsub(name, "(%l)(%w*)", function(a, b) return string.upper(a)..b end)
return name
end
-- This recipe is just a placeholder
do
local item = ItemStack("hangglider:hangglider")
item:get_meta():set_string("description", "Colored Glider")
minetest.register_craft({
output = item:to_string(),
recipe = {"hangglider:hangglider", "group:dye"},
type = "shapeless",
})
end
-- This is what actually creates the colored hangglider
minetest.register_on_craft(function(crafted_item, _, old_craft_grid)
if crafted_item:get_name() ~= "hangglider:hangglider" then
return
end
local wear, color, color_name
for _,stack in ipairs(old_craft_grid) do
local name = stack:get_name()
if name == "hangglider:hangglider" then
wear = stack:get_wear()
elseif minetest.get_item_group(name, "dye") ~= 0 then
color = get_dye_color(name)
color_name = get_color_name(name)
end
end
if wear and color and color_name then
if color == "ffffff" then
return ItemStack({name = "hangglider:hangglider", wear = wear})
end
local meta = crafted_item:get_meta()
meta:set_string("description", color_name.." Glider")
meta:set_string("inventory_image", "hangglider_item.png^(hangglider_color.png^[multiply:#"..color..")")
meta:set_string("hangglider_color", color)
crafted_item:set_wear(wear)
return crafted_item
end
end)
-- Repairing
minetest.register_craft({
output = "hangglider:hangglider",
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"},
},
})
-- Main craft
minetest.register_craft({
output = "hangglider:hangglider",
recipe = {
{"wool:white", "wool:white", "wool:white"},
{"default:stick", "", "default:stick"},
{"", "default:stick", ""},
}
})

430
init.lua
View File

@ -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 = "<ID>",
description = "Toggle airspace restrictions for area <ID>",
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,185 +111,135 @@ 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.1,
})
if vel < -5 then
-- Extra airbrake when falling too fast
player:add_velocity(vector.new(0, math.min(5, math.abs(vel / 10.0)), 0))
end
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))
local color = stack:get_meta():get("hangglider_color")
if color then
entity:set_properties({
textures = {"wool_white.png^[multiply:#"..color, "default_wood.png"}
})
end
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"
},
})
minetest.register_craft({
output = "hangglider:hangglider",
recipe = {
{"wool:white", "wool:white", "wool:white"},
{"default:stick", "", "default:stick"},
{"", "default:stick", ""},
}
})
dofile(minetest.get_modpath("hangglider").."/crafts.lua")

View File

@ -1,4 +1,5 @@
name = hangglider
depends = default, wool
optional_depends = areas, player_monoids
min_minetest_version = 5.0
description = Adds a functional hang glider for exploring
depends = default, wool, dye
optional_depends = areas, player_monoids, unifieddyes
min_minetest_version = 5.0

0
models/glider.obj → models/hangglider.obj Executable file → Normal file
View File

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB