1
0
mirror of https://github.com/mt-mods/hangglider.git synced 2025-11-20 09:05:32 +01:00

1 Commits

Author SHA1 Message Date
Luke aka SwissalpS
86c7d34d50 add area FLAK for priv and xp protected areas 2024-04-18 12:08:33 +02:00
9 changed files with 196 additions and 405 deletions

View File

@@ -1,20 +1,13 @@
globals = {
"areas",
"hangglider",
}
read_globals = {
"minetest",
"core",
"vector", "ItemStack",
"player_monoids",
"pova",
"priv_protector",
"unifieddyes",
"xcompat",
"xp_redo",
}
ignore = {
"541", -- Empty "do end" block warning
}

View File

@@ -1,91 +0,0 @@
local S = hangglider.translator
local module = {}
local has_unifieddyes = core.get_modpath("unifieddyes")
local dye_prefix_pattern_universal = "^.*dyes?:" -- Known dye prefix matches: dyes, mcl_dyes, mcl_dye, fl_dyes.
local dye_suffix_pattern_farlands = "_dye$" -- A suffix appended to dye names in the Farlands game.
local dye_colors = {
black = "111111",
blue = "0000ff",
brown = "592c00",
cyan = "00ffff",
dark_green = "005900",
dark_grey = "444444",
green = "00ff00",
grey = "888888",
light_blue = "258ec9",
lime = "60ac19",
magenta = "ff00ff",
orange = "ff7f00",
pink = "ff7f9f",
purple = "6821a0",
red = "ff0000",
silver = "818177",
violet = "8000ff",
white = "ffffff",
yellow = "ffff00",
}
local translated_colors = {
black = S("Black"),
blue = S("Blue"),
brown = S("Brown"),
cyan = S("Cyan"),
dark_green = S("Dark Green"),
dark_grey = S("Dark Grey"),
green = S("Green"),
grey = S("Grey"),
light_blue = S("Light Blue"),
lime = S("Lime"),
magenta = S("Magenta"),
orange = S("Orange"),
pink = S("Pink"),
purple = S("Purple"),
red = S("Red"),
silver = S("Light Grey"),
violet = S("Violet"),
white = S("White"),
yellow = S("Yellow"),
}
function module.get_dye_name(name)
-- Remove prefix and potential suffix
name = string.gsub(name, dye_suffix_pattern_farlands, "")
name = string.match(name, dye_prefix_pattern_universal.."(.+)$")
return name
end
function module.get_dye_color(name)
local color
if has_unifieddyes then
color = unifieddyes.get_color_from_dye_name(name)
end
if not color then
color = module.get_dye_name(name)
if color then
color = dye_colors[color]
end
end
return color
end
function module.get_color_name(name)
return translated_colors[module.get_dye_name(name)]
end
function module.get_color_name_from_color(color)
for name, color_hex in pairs(dye_colors) do
if color == color_hex then
return translated_colors[name]
end
end
return nil
end
return module

View File

@@ -1,102 +1,120 @@
local S = hangglider.translator
local colors = assert(dofile(core.get_modpath("hangglider").."/colors.lua"))
local repair_items = {"group:wool", xcompat.materials.paper}
local repair_percentage = 100
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",
}
-- Placeholder repairing recipes (Doesn't directly apply repair, see handler)
core.register_craft({
output = "hangglider:hangglider",
recipe = {
{xcompat.materials.paper, xcompat.materials.paper, xcompat.materials.paper},
{xcompat.materials.paper, "hangglider:hangglider", xcompat.materials.paper},
{xcompat.materials.paper, xcompat.materials.paper, xcompat.materials.paper},
},
})
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:(.+)$")
if color then
color = dye_colors[color]
end
end
return color
end
core.register_craft({
output = "hangglider:hangglider",
recipe = {"hangglider:hangglider", "group:wool"},
type = "shapeless",
})
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
-- Placeholder color recipe (Doesn't drectly apply color, see handler)
local function get_color_name_from_color(color)
for name, color_hex in pairs(dye_colors) do
if color == color_hex then
return name
end
end
end
-- This recipe is just a placeholder
do
local item = ItemStack("hangglider:hangglider")
item:get_meta():set_string("description", S("Colored Glider"))
core.register_craft({
item:get_meta():set_string("description", "Colored Glider")
minetest.register_craft({
output = item:to_string(),
recipe = {"hangglider:hangglider", "group:dye"},
type = "shapeless",
})
end
-- Recipe handler (This is what applies color and repair)
local function crafting_callback_handle_placeholder_recipe(crafted_item, _, old_craft_grid)
-- 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
-- Function called for an unrelated crafting recipe
return
end
-- Get existing state and present materials
local wear, repaired, dye_name, color, color_name = 0, false, nil, nil, nil
local wear, color, color_name
for _,stack in ipairs(old_craft_grid) do
local name = stack:get_name()
if not name or name == "" then
do end -- The stack is empty, do nothing and skip all checks for this stack.
elseif name == "hangglider:hangglider" then
wear = stack:get_wear()
color = stack:get_meta():get("hangglider_color")
color_name = colors.get_color_name_from_color(color)
elseif core.get_item_group(name, "dye") ~= 0 then
dye_name = name
else
for _,repair_item in ipairs(repair_items) do
if name == repair_item
or core.get_item_group(name, string.match(repair_item, "^group:(.*)$")) ~= 0
then
repaired = true
end
end
if name == "hangglider:hangglider" then
wear = stack:get_wear()
color = stack:get_meta():get("hangglider_color")
color_name = get_color_name_from_color(color)
elseif minetest.get_item_group(name, "dye") ~= 0 then
color = get_dye_color(name)
color_name = get_color_name(name)
elseif "wool:white" == stack:get_name()
or "default:paper" == stack:get_name()
then
wear = 0
end
end
-- Overwrite color with dye if present
if dye_name then
color = colors.get_dye_color(dye_name)
color_name = colors.get_color_name(dye_name)
end
-- Repair if any repair item present
if repaired then
wear = wear - (65535 * (repair_percentage / 100))
if wear < 0 then wear = 0 end
end
-- Apply item changes if valid
if wear and color and color_name then
if color == "ffffff" then
-- Return an uncolored glider
return ItemStack({name = "hangglider:hangglider", wear = wear})
end
local meta = crafted_item:get_meta()
meta:set_string("description", S("@1 Glider", color_name))
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
-- Register handler as a callback for any crafting action
core.register_on_craft(crafting_callback_handle_placeholder_recipe)
end)
-- Hangglider recipe
core.register_craft({
-- Repairing
minetest.register_craft({
output = "hangglider:hangglider",
recipe = {
{"group:wool", "group:wool", "group:wool"},
{xcompat.materials.stick, "", xcompat.materials.stick},
{"", xcompat.materials.stick, ""},
{"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", ""},
}
})

231
init.lua
View File

@@ -1,169 +1,147 @@
hangglider = {
translator = core.get_translator('hangglider'),
}
local S = hangglider.translator
local has_player_monoids = minetest.get_modpath("player_monoids")
local has_areas = minetest.get_modpath("areas")
local has_priv_protector = minetest.get_modpath("priv_protector")
and minetest.global_exists("priv_protector")
and priv_protector.get_area_priv
-- older versions of priv_protector and xp_redo don't have
-- the global and/or the functions yet
local has_xp_redo = minetest.get_modpath("xp_redo")
and minetest.global_exists("xp_redo")
and xp_redo.get_area_xp_limits and xp_redo.get_xp
local has_player_monoids = core.get_modpath("player_monoids")
local has_pova = core.get_modpath("pova")
local has_areas = core.get_modpath("areas")
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 enable_hud_overlay = core.settings:get_bool("hangglider.enable_hud_overlay", true)
local enable_flak = has_areas and core.settings:get_bool("hangglider.enable_flak", true)
local flak_warning_time = tonumber(core.settings:get("hangglider.flak_warning_time")) or 2
local hangglider_uses = tonumber(core.settings:get("hangglider.uses")) or 250
local flak_warning = S("You have entered restricted airspace!@n"
.. "You will be shot down in @1 seconds by anti-aircraft guns!",
flak_warning_time)
local flak_warning = "You have entered restricted airspace!\n"..
"You will be shot down in "..flak_warning_time.." seconds by anti-aircraft guns!"
local hanggliding_players = {}
local physics_overrides = {}
local hud_overlay_ids = {}
if enable_flak then
core.register_chatcommand("area_flak", {
params = S("<ID>"),
description = S("Toggle airspace restrictions for area <ID>."),
minetest.register_chatcommand("area_flak", {
params = "<ID>",
description = "Toggle airspace restrictions for area <ID>",
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help area_flak.")
return false, "Invalid usage, see /help area_flak."
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist or is not owned by you.", id)
return false, "Area "..id.." does not exist or is not owned by you."
end
local open = not areas.areas[id].flak
-- Save false as nil to avoid inflating the DB.
areas.areas[id].flak = open or nil
areas:save()
return true, S("Area @1 airspace is @2.", id,
open and S("closed") or S("opened"))
return true, "Area "..id.." airspace "..(open and "closed" or "opened")
end
})
end
function hangglider.is_gliding(player)
if not player then
return false
end
return hanggliding_players[player:get_player_name()]
end
local function set_hud_overlay(player, name, show)
local function set_hud_overlay(player, name, image)
if not enable_hud_overlay then
return
end
if not hud_overlay_ids[name] and show == true then
if not hud_overlay_ids[name] then
hud_overlay_ids[name] = player:hud_add({
[core.features.hud_def_type_field and "type" or "hud_elem_type"] = "image",
text = "hangglider_overlay.png",
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
})
elseif hud_overlay_ids[name] and show == false then
player:hud_remove(hud_overlay_ids[name])
hud_overlay_ids[name] = nil
else
player:hud_change(hud_overlay_ids[name], "text", image)
end
end
local function set_physics_overrides(player, overrides)
local player_name = player:get_player_name()
if has_player_monoids then
for name, value in pairs(overrides) do
player_monoids[name]:add_change(player, value, "hangglider:glider")
end
elseif has_pova then
pova.add_override(player_name, "hangglider:glider",
{jump = 0, speed = overrides.speed, gravity = overrides.gravity})
pova.do_override(player)
else
local def = player:get_physics_override()
if not physics_overrides[player_name] then
physics_overrides[player_name] = {
physics = {
speed = def.speed,
jump = def.jump,
gravity = def.gravity,
},
deltas = {speed = 0, jump = 0, gravity = 0},
}
end
-- Compute the new delta to apply (relative to current physics)
local delta = {
speed = (overrides.speed or def.speed) - def.speed,
jump = (overrides.jump or def.jump) - def.jump,
gravity = (overrides.gravity or def.gravity) - def.gravity,
}
-- Track the sum of all deltas for this session.
physics_overrides[player_name].deltas.speed = physics_overrides[player_name].deltas.speed + delta.speed
physics_overrides[player_name].deltas.jump = physics_overrides[player_name].deltas.jump + delta.jump
physics_overrides[player_name].deltas.gravity = physics_overrides[player_name].deltas.gravity + delta.gravity
-- Apply new delta on top of current physics
player:set_physics_override({
speed = def.speed + delta.speed,
jump = def.jump + delta.jump,
gravity = def.gravity + delta.gravity,
})
player:set_physics_override(overrides)
end
end
local function remove_physics_overrides(player)
local player_name = player:get_player_name()
if has_player_monoids then
for _, name in pairs({"jump", "speed", "gravity"}) do
for _, name in pairs({"jump", "speed", "gravity"}) do
if has_player_monoids then
player_monoids[name]:del_change(player, "hangglider:glider")
end
elseif has_pova then
pova.del_override(player_name, "hangglider:glider")
pova.do_override(player)
else
local def = player:get_physics_override()
if physics_overrides[player_name]
and physics_overrides[player_name].physics
and physics_overrides[player_name].deltas then
-- Subtract total delta from current values
player:set_physics_override({
speed = def.speed - physics_overrides[player_name].deltas.speed,
jump = def.jump - physics_overrides[player_name].deltas.jump,
gravity = def.gravity - physics_overrides[player_name].deltas.gravity,
})
physics_overrides[player_name] = nil
else
player:set_physics_override({speed = 1, jump = 1, gravity = 1})
player:set_physics_override({[name] = 1})
end
end
end
local function can_fly(pos, name)
local function can_fly(pos, name, player_xp, player_privs)
if not enable_flak then
return true
end
local flak = false
local flak, open = false, false
local priv_excemption, xp_limit = false, false
local xp_area, priv_area
local owners = {}
for _, area in pairs(areas:getAreasAtPos(pos)) do
for id, area in pairs(areas:getAreasAtPos(pos)) do
-- open areas are friendly airspace(?)
if area.open then
open = true
end
if player_privs then
priv_area = priv_protector.get_area_priv(id)
if player_privs[priv_area] then
priv_excemption = true
end
end
if player_xp then
xp_area = xp_redo.get_area_xp_limits(id)
if xp_area then
if (xp_area.min and player_xp < xp_area.min)
or (xp_area.max and player_xp > xp_area.max)
then
xp_limit = true
end
end
end
if area.flak then
flak = true
end
owners[area.owner] = true
end
if flak and not owners[name] then
return false
-- none of the areas has FLAK set -> friendly
-- any of the overlapping areas is open -> friendly
-- owners of overlapping areas -> safe
if not flak or open or owners[name] then
return true
end
return true
-- privilaged players -> safe
if player_privs and priv_excemption then
return true
end
-- xp limits -> unfriendly
if player_xp and not xp_limit then
return true
end
return false
end
local function safe_node_below(pos)
local node = core.get_node_or_nil(vector.new(pos.x, pos.y - 0.5, pos.z))
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 = core.registered_nodes[node.name]
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
@@ -171,7 +149,7 @@ local function safe_node_below(pos)
end
local function shoot_flak_sound(pos)
core.sound_play("hangglider_flak_shot", {
minetest.sound_play("hangglider_flak_shot", {
pos = pos,
max_hear_distance = 30,
gain = 10.0,
@@ -209,11 +187,11 @@ local function hangglider_step(self, dtime)
})
end
end
if not can_fly(pos, name) then
if not can_fly(pos, name, self.xp, self.privs) then
if not self.flak_timer then
self.flak_timer = 0
shoot_flak_sound(pos)
core.chat_send_player(name, flak_warning)
minetest.chat_send_player(name, flak_warning)
else
self.flak_timer = self.flak_timer + dtime
end
@@ -227,7 +205,7 @@ local function hangglider_step(self, dtime)
if not gliding then
remove_physics_overrides(player)
hanggliding_players[name] = nil
set_hud_overlay(player, name, false)
set_hud_overlay(player, name, "blank.png")
end
end
end
@@ -244,18 +222,25 @@ local function hangglider_use(stack, player)
local pos = player:get_pos()
local name = player:get_player_name()
if not hanggliding_players[name] then
core.sound_play("hanggliger_equip", {pos = pos, max_hear_distance = 8, gain = 1.0}, true)
local entity = core.add_entity(pos, "hangglider:glider")
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 = {xcompat.textures.wool.white.."^[multiply:#"..color, xcompat.textures.wood.apple.planks}
textures = {"wool_white.png^[multiply:#"..color, "default_wood.png"}
})
end
set_hud_overlay(player, name, true)
set_hud_overlay(player, name, "hangglider_overlay.png")
set_physics_overrides(player, {jump = 0, gravity = 0.25})
local luaentity = entity:get_luaentity()
if has_xp_redo then
luaentity.xp = xp_redo.get_xp(name)
end
if has_priv_protector then
luaentity.privs = minetest.get_player_privs(name)
end
hanggliding_players[name] = true
if hangglider_uses > 0 then
stack:add_wear(65535 / hangglider_uses)
@@ -263,26 +248,26 @@ local function hangglider_use(stack, player)
return stack
end
else
set_hud_overlay(player, name, false)
set_hud_overlay(player, name, "blank.png")
remove_physics_overrides(player)
hanggliding_players[name] = nil
end
end
core.register_on_dieplayer(function(player)
minetest.register_on_dieplayer(function(player)
local name = player:get_player_name()
hanggliding_players[name] = nil
remove_physics_overrides(player)
end)
core.register_on_leaveplayer(function(player)
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)
core.register_on_player_hpchange(function(player, hp_change, reason)
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
@@ -291,24 +276,22 @@ core.register_on_player_hpchange(function(player, hp_change, reason)
return hp_change
end, true)
core.register_entity("hangglider:glider", {
initial_properties = {
visual = "mesh",
visual_size = {x = 12, y = 12},
collisionbox = {0,0,0,0,0,0},
mesh = "hangglider.obj",
textures = {xcompat.textures.wool.white, xcompat.textures.wood.apple.planks},
immortal = true,
static_save = false,
},
minetest.register_entity("hangglider:glider", {
visual = "mesh",
visual_size = {x = 12, y = 12},
collisionbox = {0,0,0,0,0,0},
mesh = "hangglider.obj",
immortal = true,
static_save = false,
textures = {"wool_white.png", "default_wood.png"},
on_step = hangglider_step,
})
core.register_tool("hangglider:hangglider", {
description = S("Glider"),
minetest.register_tool("hangglider:hangglider", {
description = "Glider",
inventory_image = "hangglider_item.png",
sound = {breaks = "default_tool_breaks"},
on_use = hangglider_use,
})
dofile(core.get_modpath("hangglider").."/crafts.lua")
dofile(minetest.get_modpath("hangglider").."/crafts.lua")

View File

@@ -1,28 +0,0 @@
# textdomain: hangglider
White=blanc
Grey=gris
Dark_grey=gris fosc
Black=negre
Violet=violeta
Blue=blau
Cyan=cian
Dark_green=verd fosc
Green=verd
Yellow=groc
Brown=marró
Orange=taronja
Red=vermell
Magenta=magenta
Pink=rosa
Colored Glider=Planador de color
@1 Glider=Planador @1
You have entered restricted airspace!@@You will be shot down in @1 seconds by anti-aircraft guns!=Has entrat a l'espai aeri restringit!@@Seràs abatut en @1 segons per canons antiaeris!
<ID>=
Toggle airspace restrictions for area <ID>.=Activa o desactiva les restriccions d'espai aeri per a l'àrea <ID>.
Invalid usage, see /help area_flak.=Ús no vàlid, consulta /help area_flak.
Area @1 does not exist or is not owned by you.=L'àrea @1 no existeix o no és propietat teva.
Area @1 airspace is @2.=L'àrea @1 espai aeri és @2.
closed=tancat
opened=obert
Glider=Planador

View File

@@ -1,28 +0,0 @@
# textdomain: hangglider
White=Weisser
Grey=Grauer
Dark_grey=Dunkelgrauer
Black=Schwarzer
Violet=Violeter
Blue=Blauer
Cyan=Cyan farbener
Dark_green=Dunkelgrüner
Green=Grüner
Yellow=Gelber
Brown=Brauner
Orange=Orange farbener
Red=Roter
Magenta=Magenta farbener
Pink=Rosa
Colored Glider=Farbiger Gleitschirm
@1 Glider=@1 Gleitschirm
You have entered restricted airspace!@@You will be shot down in @1 seconds by anti-aircraft guns!=Du bist in eingeschränktem Luftraum eingedrungen!@nDu wirst in @1 Sekunden von FLAK abgeschossen.
<ID>=
Toggle airspace restrictions for area <ID>.=Luftraumbeschränkungen für Gebiet <ID> umschalten.
Invalid usage, see /help area_flak.=Ungültige Verwendung, siehe /help area_flak.=
Area @1 does not exist or is not owned by you.=Gebiet @1 existiert nicht oder es gehört dir nicht.
Area @1 airspace is @2.=Gebiet @1 Luftraum ist @2.
closed=eingeschränkt
opened=geöffnet
Glider=Gleitschirm

View File

@@ -1,28 +0,0 @@
# textdomain: hangglider
White=blanco
Grey=gris
Dark_grey=gris oscuro
Black=negro
Violet=violeta
Blue=azul
Cyan=cian
Dark_green=verde oscuro
Green=verde
Yellow=amarillo
Brown=marrón
Orange=naranja
Red=rojo
Magenta=magenta
Pink=rosa
Colored Glider=Planeador Colorizado
@1 Glider=Planeador @1
You have entered restricted airspace!@@You will be shot down in @1 seconds by anti-aircraft guns!=¡Has entrado en espacio aéreo restringido!@n¡Serás derribado en @1 segundos por cañones antiaéreos!
<ID>=
Toggle airspace restrictions for area <ID>.=Alternar restricciones de espacio aéreo para el área <ID>.
Invalid usage, see /help area_flak.=Uso no válido, consulta /help area_flak.
Area @1 does not exist or is not owned by you.=El área @1 no existe o no es tuya.
Area @1 airspace is @2.=Área @1 espacio aéreo está @2.
closed=cerrado
opened=abierto
Glider=Planeador

View File

@@ -1,28 +0,0 @@
# textdomain: hangglider
White=
Grey=
Dark_grey=
Black=
Violet=
Blue=
Cyan=
Dark_green=
Green=
Yellow=
Brown=
Orange=
Red=
Magenta=
Pink=
Colored Glider=
@1 Glider=
You have entered restricted airspace!@@You will be shot down in @1 seconds by anti-aircraft guns!=
<ID>=
Toggle airspace restrictions for area <ID>.=
Invalid usage, see /help area_flak.=
Area @1 does not exist or is not owned by you.=
Area @1 airspace is @2.=
closed=
opened=
Glider=

View File

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