This commit is contained in:
verdishubert 2022-12-27 13:32:51 +00:00
parent 83a29cf86e
commit d302856c75
5 changed files with 778 additions and 0 deletions

64
API.md Normal file
View File

@ -0,0 +1,64 @@
# `generic_flags` API
Programmers can use the following Lua functions to add custom flags,
get a list of all flags, and set and get the flag of flag pole nodes.
## Functions
### `generic_flags.add_flag(name)`
Add a new flag to the game. `name` is the flag identifier.
There *must* exist a texture with the name `flag_<name>.png`.
The texture *should* have an aspect ratio of 1.3.
The recommended size is 78×60, but other sizes are OK
as long the aspect ratio is respected.
The flag name *must not* already exist. This will be checked.
On success, the flag will be appended to the list of flags at the end.
If a flag with the given name already exists, no flag will be
added.
Returns `true` on success and `false` on failure.
### `generic_flags.get_flags()`
Returns a list of all available flag identifiers. The flags
are sorted by selection order.
### `generic_flags.set_flag_at(pos, flag_name)`
Sets the flag at an upper mast node at position `pos` to the flag `flag_name`.
The node at `pos` *must* be `generic_flags:upper_mast`.
Returns `true` on success and `false` otherwise.
### `generic_flags.get_flag_at(pos)`
Returns the currently used flag at the upper mast node at position `pos`.
The node at `pos` *must* be `generic_flags:upper_mast`.
Returns a string on success and `nil` otherwise.
### `generic_flags.get_wind(pos)`
Returns the current wind strength at pos. The wind strength determines how
fast a flag at pos would be waving at the time this function was called.
This function will be called from time to time by the mod to update
the flag waving speed of flags. It is called for every flag once about
every 115 seconds (plusminus 10 seconds).
This function is predefined in this mod by pseudorandomly changing the wind
strength over time using a Perlin noise. By default, the wind strength is
only controlled by the current time; the position is ignored.
This function can be overwritten by mods to define your own wind algorithm.
You can do whatever in this function, you only need to return a number in
the end. The number should be roughly in the range between 0 and 50.
This is how the wind strength affects the flag waving speed:
* wind < 10: slow
* 10 < wind < 20: medium
* 20 < wind < 40: fast
* wind > 40: very fast

79
LICENSE.txt Normal file
View File

@ -0,0 +1,79 @@
# Licensing and credits for Generic Inoffensive Flags Mod
Source Code License
----------------------------------------------------------
GNU Lesser General Public License v3 (LGPL-3.0)
Copyright (c) 2022, AwesomeDragon97 and the creators of the original mod: Leslie E. Krause <leslie@searstower.org> and Wuzzy <Wuzzy@disroot.org>
This program is free software; you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
http://www.gnu.org/licenses/lgpl-3.0.html
Multimedia License (textures, sounds, and models)
----------------------------------------------------------
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
/models/flags_mast_lower.obj
by AwesomeDragon97
/models/flags_mast_upper.obj
by AwesomeDragon97
/models/flags_wavingflag.b3d
by sorcerykid
/textures/flags_baremetal.png
by sorcerykid
/sounds/flags_flagwave1.ogg
by Chelly01
obtained from https://freesound.org/people/Chelly01/sounds/541088/
modified by sorcerykid
/sounds/flags_flagwave2.ogg
by Chelly01
obtained from https://freesound.org/people/Chelly01/sounds/541088/
modified by sorcerykid
/sounds/flags_flagwave3.ogg
by Chelly01
obtained from https://freesound.org/people/Chelly01/sounds/541088/
modified by sorcerykid
progress
by Edgy1
You are free to:
Share — copy and redistribute the material in any medium or format.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit, provide a link to the license, and
indicate if changes were made. You may do so in any reasonable manner, but not in any way
that suggests the licensor endorses you or your use.
No additional restrictions — You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.
Notices:
You do not have to comply with the license for elements of the material in the public
domain or where your use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary
for your intended use. For example, other rights such as publicity, privacy, or moral
rights may limit how you use the material.
For more details:
http://creativecommons.org/licenses/by-sa/3.0/

3
README.txt Normal file
View File

@ -0,0 +1,3 @@
~~Made by AwesomeDragon97, Anonymous~~
Credit: Wuzzy for making the original mod.

626
init.lua Normal file
View File

@ -0,0 +1,626 @@
--------------------------------------------------------
-- Minetest :: Generic Flags Mod (flags)
--
-- See README.txt for licensing and other information.
-- Made by AwesomeDragon97, Anonymous with code from Wuzzy.
--------------------------------------------------------
flags = {}
local wind_noise = PerlinNoise( 204, 1, 0, 500 )
-- Check whether the new `get_2d` Perlin function is available,
-- otherwise use `get2d`. Needed to suppress deprecation
-- warning in newer Minetest versions.
local old_get2d = true
if wind_noise.get_2d then
old_get2d = false
end
local active_flags = { }
local pi = math.pi
local rad_180 = pi
local rad_90 = pi / 2
-- This flag is used as the default or fallback in case of error/missing data
local DEFAULT_FLAG = minetest.settings:get('default_flag') or 'white'
local flag_list = {}
if minetest.settings:get_bool('name_flags', true) == true then
flag_list = { -- generic flags
"white",
-- "red",
-- "green",
-- "brown",
-- "blue",
"black",
-- "orange",
-- "yellow",
-- "purple",
-- "pink",
-- "gray",
-- "cyan",
-- "magenta",
"dark_green",
"dark_gray",
"pirate",
"anarcho",
"makhno",
"anonymous",
"guyfox",
"jollyroger",
"anarchy" }
end
local next_flag, prev_flag
local update_next_prev_flag_lists = function()
next_flag = {}
prev_flag = {}
for f=1, #flag_list do
local name1 = flag_list[f]
local name0, name2
if f < #flag_list then
name2 = flag_list[f+1]
else
name2 = flag_list[1]
end
if f == 1 then
name0 = flag_list[#flag_list]
else
name0 = flag_list[f-1]
end
next_flag[name1] = name2
prev_flag[name1] = name0
end
end
update_next_prev_flag_lists()
local flag_exists = function(flag_name)
return next_flag[ flag_name] ~= nil
end
local get_next_flag = function(current_flag_name)
if not current_flag_name or not flag_exists( current_flag_name ) then
return DEFAULT_FLAG
else
return next_flag[current_flag_name]
end
end
local get_prev_flag = function(current_flag_name)
if not current_flag_name or not flag_exists( current_flag_name ) then
return DEFAULT_FLAG
else
return prev_flag[current_flag_name]
end
end
-- Delete entity if there is no flag mast node
local delete_if_orphan = function( self )
local pos = self.object:get_pos( )
local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
if node.name ~= "pirate_flag:upper_mast" and node.name ~= "ignore" then
minetest.log("action", "[flags] Orphan flag entity removed at "..minetest.pos_to_string(pos, 1))
self.object:remove( )
return true
end
return false
end
minetest.register_entity( "pirate_flag:wavingflag", {
initial_properties = {
physical = false,
visual = "mesh",
visual_size = { x = 8.5, y = 8.5 },
collisionbox = { 0, 0, 0, 0, 0, 0 },
backface_culling = false,
pointable = false,
mesh = "flags_wavingflag.b3d",
textures = { "flag_" .. DEFAULT_FLAG .. ".png" },
use_texture_alpha = false,
},
on_activate = function ( self, staticdata, dtime )
if delete_if_orphan( self) then
return
end
-- Init stuff
self:reset_animation( true )
self.object:set_armor_groups( { immortal = 1 } )
if staticdata ~= "" then
local data = minetest.deserialize( staticdata )
if data.flag_name then
self.flag_name = data.flag_name
else
self.flag_name = DEFAULT_FLAG
end
self.node_idx = data.node_idx
if not self.flag_name or not self.node_idx then
self.object:remove( )
return
end
self:reset_texture( self.flag_name )
active_flags[ self.node_idx ] = self.object
else
self.flag_name = DEFAULT_FLAG
end
-- Delete entity if there is already one for this pos
local objs = minetest.get_objects_inside_radius( self.object:get_pos(), 0.5 )
for o=1, #objs do
local obj = objs[o]
local lua = obj:get_luaentity( )
if lua and self ~= lua and lua.name == "pirate_flag:wavingflag" then
if lua.node_idx == self.node_idx then
self.object:remove( )
return
end
end
end
end,
on_deactivate = function ( self )
if self.sound_id then
minetest.sound_stop( self.sound_id )
end
end,
on_step = function ( self, dtime )
self.anim_timer = self.anim_timer - dtime
if self.anim_timer <= 0 then
if delete_if_orphan( self) then
return
end
self:reset_animation( )
end
end,
reset_animation = function ( self, initial )
local pos = self.object:get_pos( )
local cur_wind = flags.get_wind( pos )
minetest.log("verbose", "[flags] Current wind at "..minetest.pos_to_string(pos, 1)..": " .. cur_wind)
local anim_speed
local wave_sound
if cur_wind < 10 then
anim_speed = 10 -- 2 cycle
wave_sound = "flags_flagwave1"
elseif cur_wind < 20 then
anim_speed = 20 -- 4 cycles
wave_sound = "flags_flagwave1"
elseif cur_wind < 40 then
anim_speed = 40 -- 8 cycles
wave_sound = "flags_flagwave2"
else
anim_speed = 80 -- 16 cycles
wave_sound = "flags_flagwave3"
end
-- slightly anim_speed change to desyncronize flag waving
anim_speed = anim_speed + math.random(0, 200) * 0.01
if self.object then
if initial or (not self.object.set_animation_frame_speed) then
self.object:set_animation( { x = 1, y = 575 }, anim_speed, 0, true )
else
self.object:set_animation_frame_speed(anim_speed)
end
end
if not self.sound_id then
self.sound_id = minetest.sound_play( wave_sound, { object = self.object, gain = 1.0, loop = true } )
end
self.anim_timer = 115 + math.random(-10, 10) -- time to reset animation
end,
reset_texture = function ( self, flag_name, nextprev )
if nextprev == 1 then
self.flag_name = get_next_flag(self.flag_name)
elseif nextprev == -1 then
self.flag_name = get_prev_flag(self.flag_name)
else
self.flag_name = flag_name
end
local texture = string.format( "flag_%s.png", self.flag_name )
self.object:set_properties( { textures = { texture } } )
return self.flag_name
end,
get_staticdata = function ( self )
return minetest.serialize( {
node_idx = self.node_idx,
flag_name = self.flag_name,
} )
end,
} )
local metal_sounds
if minetest.get_modpath("default") ~= nil then
if default.node_sound_metal_defaults then
metal_sounds = default.node_sound_metal_defaults()
end
end
minetest.register_node("pirate_flag:lower_mast", {
description = ("Flag Pole"),
drawtype = "mesh",
paramtype = "light",
mesh = "flags_mast_lower.obj",
paramtype2 = "facedir",
tiles = { "flags_baremetal.png", "flags_baremetal.png" },
wield_image = "flags_pole_bottom_inv.png",
inventory_image = "flags_pole_bottom_inv.png",
groups = { cracky = 2 },
sounds = metal_sounds,
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = { { -2/16, -1/2, -2/16, 2/16, 1/2, 2/16 } },
},
collision_box = {
type = "fixed",
fixed = { { -2/16, -1/2, -2/16, 2/16, 1/2, 2/16 } },
},
on_rotate = function(pos, node, user, mode, new_param2)
if mode == screwdriver.ROTATE_AXIS then
return false
end
end,
} )
local function get_flag_pos( pos, param2 )
local facedir_to_pos = {
[0] = { x = 0, y = 0.5, z = -0.125 },
[1] = { x = -0.125, y = 0.5, z = 0 },
[2] = { x = 0, y = 0.5, z = 0.125 },
[3] = { x = 0.125, y = 0.5, z = 0 },
}
return vector.add( pos, vector.multiply( facedir_to_pos[ param2 ], 1 ) )
end
local function rotate_flag_by_param2( flag, param2 )
local facedir_to_yaw = {
[0] = rad_90,
[1] = 0,
[2] = -rad_90,
[3] = rad_180,
}
local baseyaw = facedir_to_yaw[ param2 ]
if not baseyaw then
minetest.log("warning", "[flags] Unsupported flag pole node param2: "..tostring(param2))
return
end
flag:set_yaw( baseyaw - rad_180 )
end
local function spawn_flag( pos )
local node_idx = minetest.hash_node_position( pos )
local param2 = minetest.get_node( pos ).param2
local flag_pos = get_flag_pos( pos, param2 )
local obj = minetest.add_entity( flag_pos, "pirate_flag:wavingflag" )
if not obj or not obj:get_luaentity( ) then
return
end
obj:get_luaentity( ).node_idx = node_idx
rotate_flag_by_param2( obj, param2 )
active_flags[ node_idx ] = obj
return obj
end
local function spawn_flag_and_set_texture( pos )
local flag = spawn_flag( pos )
if flag and flag:get_luaentity() then
local meta = minetest.get_meta( pos )
local flag_name = meta:get_string("flag_name")
if flag_name == "" then
flag_name = DEFAULT_FLAG
meta:set_string("flag_idx", "")
meta:set_string("flag_name", flag_name)
end
flag:get_luaentity():reset_texture( flag_name )
end
return flag
end
local function cycle_flag( pos, player, cycle_backwards )
local pname = player:get_player_name( )
if minetest.is_protected( pos, pname ) and not
minetest.check_player_privs( pname, "protection_bypass") then
minetest.record_protection_violation( pos, pname )
return
end
local node_idx = minetest.hash_node_position( pos )
local aflag = active_flags[ node_idx ]
local flag
if aflag then
flag = aflag:get_luaentity( )
end
if flag then
local flag_name
if cycle_backwards then
flag_name = flag:reset_texture( nil, -1 )
else
flag_name = flag:reset_texture( nil, 1 )
end
local meta = minetest.get_meta( pos )
meta:set_string("flag_name", flag_name)
else
spawn_flag_and_set_texture( pos )
end
end
minetest.register_node( "pirate_flag:upper_mast", {
description = ("Flag Pole with Flag"),
drawtype = "mesh",
paramtype = "light",
mesh = "flags_mast_upper.obj",
paramtype2 = "facedir",
tiles = { "flags_baremetal.png", "flags_baremetal.png" },
wield_image = "flags_pole_top_inv.png",
inventory_image = "flags_pole_top_inv.png",
groups = { cracky = 2 },
sounds = metal_sounds,
is_ground_content = false,
drop = {"pirate_flag:lower_mast 2", "pirate_flag:upper_mast"},
selection_box = {
type = "fixed",
fixed = { { -2/16, -1/2, -2/16, 2/16, 3/2, 2/16 } },
},
collision_box = {
type = "fixed",
fixed = { { -2/16, -1/2, -2/16, 2/16, 3/2, 2/16 } },
},
on_rightclick = function ( pos, node, player )
cycle_flag( pos, player )
end,
on_punch = function ( pos, node, player )
cycle_flag( pos, player, -1 )
end,
node_placement_prediction = "",
on_place = function( itemstack, placer, pointed_thing )
if not pointed_thing.type == "node" then
return itemstack
end
local node = minetest.get_node(pointed_thing.under)
local node2 = minetest.get_node(pointed_thing.under - vector.new(0, 1, 0))
if (node.name ~= "pirate_flag:lower_mast" or node2.name ~= "pirate_flag:lower_mast") then
return itemstack
end
local pn = placer:get_player_name()
if minetest.is_protected(pointed_thing.under, pn) then
minetest.record_protection_violation(pointed_thing.under, pn)
return itemstack
elseif minetest.is_protected(pointed_thing.under - vector.new(0, 1, 0), pn) then
minetest.record_protection_violation(pointed_thing.under - vector.new(0, 1, 0), pn)
return itemstack
end
local yaw = placer:get_look_horizontal()
local dir = minetest.yaw_to_dir(yaw)
local param2 = (minetest.dir_to_facedir(dir) + 3) % 4
minetest.set_node(pointed_thing.under - vector.new(0, 1, 0), {name = "pirate_flag:upper_mast", param2 = param2 })
minetest.set_node(pointed_thing.under, {name = "pirate_flag:upper_mast_hidden_1"})
if not (minetest.is_creative_enabled(pn)) then
itemstack:take_item()
end
local def = minetest.registered_nodes["pirate_flag:upper_mast"]
if def and def.sounds then
minetest.sound_play(def.sounds.place, {pos = pos}, true)
end
return itemstack
end,
on_construct = function ( pos )
local flag = spawn_flag ( pos )
if flag and flag:get_luaentity() then
local meta = minetest.get_meta( pos )
meta:set_string("flag_name", flag:get_luaentity().flag_name)
end
end,
on_destruct = function ( pos )
local node_idx = minetest.hash_node_position( pos )
if active_flags[ node_idx ] then
active_flags[ node_idx ]:remove( )
end
end,
after_destruct = function( pos )
local above1 = {x=pos.x, y=pos.y+1, z=pos.z}
if minetest.get_node( above1 ).name == "pirate_flag:upper_mast_hidden_1" then
minetest.remove_node( above1 )
end
end,
on_rotate = function(pos, node, user, mode, new_param2)
if mode == screwdriver.ROTATE_AXIS then
return false
end
local node_idx = minetest.hash_node_position( pos )
local aflag = active_flags[ node_idx ]
if aflag then
local lua = aflag:get_luaentity( )
if not lua then
aflag = spawn_flag_and_set_texture( pos )
if aflag then
lua = aflag:get_luaentity()
if not lua then
return
end
end
end
local flag_pos_idx = lua.node_idx
local flag_pos = minetest.get_position_from_hash( flag_pos_idx )
flag_pos = get_flag_pos( flag_pos, new_param2 )
rotate_flag_by_param2( aflag, new_param2 )
aflag:set_pos( flag_pos )
end
end,
} )
minetest.register_lbm({
name = "pirate_flag:respawn_flags",
label = "Respawn flags",
nodenames = {"pirate_flag:upper_mast"},
run_at_every_load = true,
action = function(pos, node)
local node_idx = minetest.hash_node_position( pos )
local aflag = active_flags[ node_idx ]
if aflag then
return
end
spawn_flag_and_set_texture( pos )
end
})
minetest.register_lbm({
name = "pirate_flag:update_node_meta",
label = "Update mast node meta",
nodenames = {"pirate_flag:upper_mast"},
run_at_every_load = false,
action = function(pos, node)
local meta = minetest.get_meta( pos )
local flag_idx = meta:get_int("flag_idx")
if flag_idx ~= 0 then
flag_name = DEFAULT_FLAG
meta:set_string("flag_idx", "")
meta:set_string("flag_name", flag_name)
end
end
})
local flagpole_material = minetest.settings:get('flagpole_material') or "default:steel_ingot"
if minetest.registered_items[flagpole_material] then
minetest.register_craft({
output = "pirate_flag:lower_mast 3",
recipe = {
{flagpole_material},
{flagpole_material},
{flagpole_material},
},
})
end
minetest.register_craft({
output = "pirate_flag:upper_mast",
recipe = {
{"group:wool", "group:wool", "group:wool"},
{"group:wool", "group:wool", "group:wool"},
},
})
-- Add a hidden upper mast segments to block the node
-- the upper mast occupies. This is also needed to prevent
-- collision issues.
-- This node will be automatically
-- added/removed when the upper mast is constructed
-- or destructed.
minetest.register_node( "pirate_flag:upper_mast_hidden_1", {
drawtype = "airlike",
pointable = false,
paramtype = "light",
sunlight_propagates = true,
wield_image = "flags_pole_hidden1_inv.png",
inventory_image = "flags_pole_hidden1_inv.png",
groups = { not_in_creative_inventory = 1 },
sounds = metal_sounds,
collision_box = {
type = "fixed",
fixed = { { -3/32, -1/2, -3/32, 3/32, 1/5, 3/32 } },
},
is_ground_content = false,
on_blast = function() end,
can_dig = function() return false end,
drop = "",
diggable = false,
floodable = false,
})
-- API
flags.add_flag = function( name )
if flag_exists( name ) then
return false
end
table.insert( flag_list, name )
update_next_prev_flag_lists()
return true
end
flags.get_flags = function( )
return table.copy( flag_list )
end
flags.set_flag_at = function( pos, flag_name )
local node = minetest.get_node( pos )
if node.name ~= "pirate_flag:upper_mast" then
return false
end
if not flag_exists( flag_name ) then
return false
end
local node_idx = minetest.hash_node_position( pos )
local aflag = active_flags[ node_idx ]
local flag
if aflag then
flag = aflag:get_luaentity( )
end
if flag then
local set_flag_name = flag:reset_texture( flag_name )
if set_flag_name == flag_name then
local meta = minetest.get_meta( pos )
meta:set_string("flag_name", flag_name)
return true
end
end
return false
end
flags.get_flag_at = function( pos )
local node = minetest.get_node( pos )
if node.name ~= "pirate_flag:upper_mast" then
return nil
end
local meta = minetest.get_meta( pos )
local flag_name = meta:get_string("flag_name")
if flag_name ~= "" then
return flag_name
end
end
-- Returns the wind strength at pos.
-- Can be overwritten by mods.
flags.get_wind = function( pos )
-- The default wind function ignores pos.
-- Returns a wind between ca. 0 and 55
local coords = { x = os.time( ) % 65535, y = 0 }
local cur_wind
if old_get2d then
cur_wind = wind_noise:get2d(coords)
else
cur_wind = wind_noise:get_2d(coords)
end
cur_wind = cur_wind * 30 + 30
return cur_wind
end

6
mod.conf Normal file
View File

@ -0,0 +1,6 @@
name = pirate_flag
title = Pirate Flags
author = Anonymous
optional_depends = default, screwdriver
description = Seven flags based on Generic flags