Update to 0.4.16
|
@ -101,56 +101,6 @@ function hunger.handle_node_actions(pos, oldnode, player, ext)
|
|||
hunger.players[name].exhaus = exhaus
|
||||
end
|
||||
|
||||
-- sprint settings
|
||||
local enable_sprint = minetest.setting_getbool("sprint") ~= false
|
||||
local enable_sprint_particles = minetest.setting_getbool("sprint_particles") ~= false
|
||||
|
||||
-- 3d armor support
|
||||
local armor_mod = minetest.get_modpath("3d_armor")
|
||||
|
||||
-- Sets the sprint state of a player (false = stopped, true = sprinting)
|
||||
function set_sprinting(name, sprinting)
|
||||
|
||||
if not hunger.players[name] then
|
||||
return false
|
||||
end
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
-- is 3d_armor active, then set to armor defaults
|
||||
local def = {}
|
||||
if armor_mod and armor and armor.def[name] then
|
||||
def = armor.def[name]
|
||||
end
|
||||
|
||||
def.speed = def.speed or 1
|
||||
def.jump = def.jump or 1
|
||||
def.gravity = def.gravity or 1
|
||||
|
||||
if sprinting == true then
|
||||
|
||||
player:set_physics_override({
|
||||
speed = def.speed + SPRINT_SPEED,
|
||||
jump = def.jump + SPRINT_JUMP,
|
||||
gravity = def.gravity
|
||||
})
|
||||
|
||||
--print ("Speed:", def.speed + SPRINT_SPEED, "Jump:", def.jump + SPRINT_JUMP, "Gravity:", def.gravity)
|
||||
|
||||
else
|
||||
|
||||
player:set_physics_override({
|
||||
speed = def.speed,
|
||||
jump = def.jump,
|
||||
gravity = def.gravity
|
||||
})
|
||||
|
||||
--print ("Speed:", def.speed, "Jump:", def.jump, "Gravity:", def.gravity)
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Time based hunger functions
|
||||
local hunger_timer = 0
|
||||
|
@ -169,61 +119,6 @@ local function hunger_globaltimer(dtime)
|
|||
if controls.up or controls.down or controls.left or controls.right then
|
||||
hunger.handle_node_actions(nil, nil, player)
|
||||
end
|
||||
|
||||
if enable_sprint then
|
||||
|
||||
local name = player:get_player_name()
|
||||
|
||||
-- check if player should be sprinting (hunger must be over 6 points)
|
||||
if player
|
||||
and controls.aux1
|
||||
and controls.up
|
||||
and not minetest.check_player_privs(name, {fast = true})
|
||||
and hunger.players[name].lvl > 6 then
|
||||
|
||||
set_sprinting(name, true)
|
||||
|
||||
-- create particles behind player when sprinting
|
||||
if enable_sprint_particles then
|
||||
|
||||
local pos = player:getpos()
|
||||
local node = minetest.get_node({
|
||||
x = pos.x,
|
||||
y = pos.y - 1,
|
||||
z = pos.z
|
||||
})
|
||||
|
||||
if node.name ~= "air" then
|
||||
|
||||
minetest.add_particlespawner({
|
||||
time = 0.01,
|
||||
amount = 5,
|
||||
minpos = {x = pos.x - 0.25, y = pos.y + 0.1, z = pos.z - 0.25},
|
||||
maxpos = {x = pos.x + 0.25, y = pos.y + 0.1, z = pos.z + 0.25},
|
||||
minvel = {x = -0.5, y = 1, z = -0.5},
|
||||
maxvel = {x = 0.5, y = 2, z = 0.5},
|
||||
minacc = {x = 0, y = -5, z = 0},
|
||||
maxacc = {x = 0, y = -12, z = 0},
|
||||
minexptime = 0.25,
|
||||
maxexptime = 0.5,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.0,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
texture = "default_dirt.png",
|
||||
})
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Lower the player's hunger
|
||||
update_hunger(player,
|
||||
hunger.players[name].lvl - (SPRINT_DRAIN * HUNGER_MOVE_TICK))
|
||||
else
|
||||
set_sprinting(name, false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
action_timer = 0
|
||||
end
|
||||
|
@ -268,7 +163,6 @@ local function hunger_globaltimer(dtime)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
if minetest.setting_getbool("enable_damage") then
|
||||
minetest.register_globalstep(hunger_globaltimer)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,386 @@
|
|||
|
||||
-- read/write
|
||||
function hunger.read(player)
|
||||
local inv = player:get_inventory()
|
||||
if not inv then
|
||||
return nil
|
||||
end
|
||||
local hgp = inv:get_stack("hunger", 1):get_count()
|
||||
if hgp == 0 then
|
||||
hgp = 21
|
||||
inv:set_stack("hunger", 1, ItemStack({name = ":", count = hgp}))
|
||||
else
|
||||
hgp = hgp
|
||||
end
|
||||
if tonumber(hgp) > HUNGER_MAX + 1 then
|
||||
hgp = HUNGER_MAX + 1
|
||||
end
|
||||
return hgp - 1
|
||||
end
|
||||
|
||||
function hunger.save(player)
|
||||
local inv = player:get_inventory()
|
||||
local name = player:get_player_name()
|
||||
local value = hunger.players[name].lvl
|
||||
if not inv or not value then
|
||||
return nil
|
||||
end
|
||||
if value > HUNGER_MAX then
|
||||
value = HUNGER_MAX
|
||||
end
|
||||
if value < 0 then
|
||||
value = 0
|
||||
end
|
||||
inv:set_stack("hunger", 1, ItemStack({name = ":", count = value + 1}))
|
||||
return true
|
||||
end
|
||||
|
||||
function hunger.update_hunger(player, new_lvl)
|
||||
local name = player:get_player_name() or nil
|
||||
if not name then
|
||||
return false
|
||||
end
|
||||
if minetest.setting_getbool("enable_damage") == false then
|
||||
hunger.players[name] = 20
|
||||
return
|
||||
end
|
||||
local lvl = hunger.players[name].lvl
|
||||
if new_lvl then
|
||||
lvl = new_lvl
|
||||
end
|
||||
if lvl > HUNGER_MAX then
|
||||
lvl = HUNGER_MAX
|
||||
end
|
||||
hunger.players[name].lvl = lvl
|
||||
if lvl > 20 then
|
||||
lvl = 20
|
||||
end
|
||||
hud.change_item(player, "hunger", {number = lvl})
|
||||
hunger.save(player)
|
||||
end
|
||||
local update_hunger = hunger.update_hunger
|
||||
|
||||
-- player-action based hunger changes
|
||||
function hunger.handle_node_actions(pos, oldnode, player, ext)
|
||||
if not player or not player:is_player() then
|
||||
return
|
||||
end
|
||||
local name = player:get_player_name()
|
||||
if not name or not hunger.players[name] then
|
||||
return
|
||||
end
|
||||
|
||||
local exhaus = hunger.players[name].exhaus
|
||||
if not exhaus then
|
||||
hunger.players[name].exhaus = 0
|
||||
--return
|
||||
end
|
||||
|
||||
local new = HUNGER_EXHAUST_PLACE
|
||||
|
||||
-- placenode event
|
||||
if not ext then
|
||||
new = HUNGER_EXHAUST_DIG
|
||||
end
|
||||
|
||||
-- assume its send by action_timer(globalstep)
|
||||
if not pos and not oldnode then
|
||||
new = HUNGER_EXHAUST_MOVE
|
||||
end
|
||||
|
||||
exhaus = exhaus + new
|
||||
|
||||
if exhaus > HUNGER_EXHAUST_LVL then
|
||||
exhaus = 0
|
||||
local h = tonumber(hunger.players[name].lvl)
|
||||
if h > 0 then
|
||||
update_hunger(player, h - 1)
|
||||
end
|
||||
end
|
||||
|
||||
hunger.players[name].exhaus = exhaus
|
||||
end
|
||||
|
||||
-- sprint settings
|
||||
local enable_sprint = minetest.setting_getbool("sprint") ~= false
|
||||
local enable_sprint_particles = minetest.setting_getbool("sprint_particles") ~= false
|
||||
|
||||
-- 3d armor support
|
||||
local armor_mod = minetest.get_modpath("3d_armor")
|
||||
|
||||
-- Sets the sprint state of a player (false = stopped, true = sprinting)
|
||||
function set_sprinting(name, sprinting)
|
||||
|
||||
if not hunger.players[name] then
|
||||
return false
|
||||
end
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
-- is 3d_armor active, then set to armor defaults
|
||||
local def = {}
|
||||
if armor_mod and armor and armor.def[name] then
|
||||
def = armor.def[name]
|
||||
end
|
||||
|
||||
def.speed = def.speed or 1
|
||||
def.jump = def.jump or 1
|
||||
def.gravity = def.gravity or 1
|
||||
|
||||
if sprinting == true then
|
||||
|
||||
player:set_physics_override({
|
||||
speed = def.speed + SPRINT_SPEED,
|
||||
jump = def.jump + SPRINT_JUMP,
|
||||
gravity = def.gravity
|
||||
})
|
||||
|
||||
--print ("Speed:", def.speed + SPRINT_SPEED, "Jump:", def.jump + SPRINT_JUMP, "Gravity:", def.gravity)
|
||||
|
||||
else
|
||||
|
||||
player:set_physics_override({
|
||||
speed = def.speed,
|
||||
jump = def.jump,
|
||||
gravity = def.gravity
|
||||
})
|
||||
|
||||
--print ("Speed:", def.speed, "Jump:", def.jump, "Gravity:", def.gravity)
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Time based hunger functions
|
||||
local hunger_timer = 0
|
||||
local health_timer = 0
|
||||
local action_timer = 0
|
||||
|
||||
local function hunger_globaltimer(dtime)
|
||||
hunger_timer = hunger_timer + dtime
|
||||
health_timer = health_timer + dtime
|
||||
action_timer = action_timer + dtime
|
||||
|
||||
if action_timer > HUNGER_MOVE_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local controls = player:get_player_control()
|
||||
-- Determine if the player is walking
|
||||
if controls.up or controls.down or controls.left or controls.right then
|
||||
hunger.handle_node_actions(nil, nil, player)
|
||||
end
|
||||
|
||||
if enable_sprint then
|
||||
|
||||
local name = player:get_player_name()
|
||||
|
||||
-- check if player should be sprinting (hunger must be over 6 points)
|
||||
if player
|
||||
and controls.aux1
|
||||
and controls.up
|
||||
and not minetest.check_player_privs(name, {fast = true})
|
||||
and hunger.players[name].lvl > 6 then
|
||||
|
||||
set_sprinting(name, true)
|
||||
|
||||
-- create particles behind player when sprinting
|
||||
if enable_sprint_particles then
|
||||
|
||||
local pos = player:getpos()
|
||||
local node = minetest.get_node({
|
||||
x = pos.x,
|
||||
y = pos.y - 1,
|
||||
z = pos.z
|
||||
})
|
||||
|
||||
if node.name ~= "air" then
|
||||
|
||||
minetest.add_particlespawner({
|
||||
time = 0.01,
|
||||
amount = 5,
|
||||
minpos = {x = pos.x - 0.25, y = pos.y + 0.1, z = pos.z - 0.25},
|
||||
maxpos = {x = pos.x + 0.25, y = pos.y + 0.1, z = pos.z + 0.25},
|
||||
minvel = {x = -0.5, y = 1, z = -0.5},
|
||||
maxvel = {x = 0.5, y = 2, z = 0.5},
|
||||
minacc = {x = 0, y = -5, z = 0},
|
||||
maxacc = {x = 0, y = -12, z = 0},
|
||||
minexptime = 0.25,
|
||||
maxexptime = 0.5,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.0,
|
||||
vertical = false,
|
||||
collisiondetection = false,
|
||||
texture = "default_dirt.png",
|
||||
})
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Lower the player's hunger
|
||||
update_hunger(player,
|
||||
hunger.players[name].lvl - (SPRINT_DRAIN * HUNGER_MOVE_TICK))
|
||||
else
|
||||
set_sprinting(name, false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
action_timer = 0
|
||||
end
|
||||
|
||||
-- lower saturation by 1 point after <HUNGER_TICK> second(s)
|
||||
if hunger_timer > HUNGER_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local tab = hunger.players[name]
|
||||
if tab then
|
||||
local hunger = tab.lvl
|
||||
if hunger > 0 then
|
||||
update_hunger(player, hunger - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
hunger_timer = 0
|
||||
end
|
||||
|
||||
-- heal or damage player, depending on saturation
|
||||
if health_timer > HUNGER_HEALTH_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local tab = hunger.players[name]
|
||||
if tab then
|
||||
local air = player:get_breath() or 0
|
||||
local hp = player:get_hp()
|
||||
|
||||
-- heal player by 1 hp if not dead and saturation is > 15 (of 30) player is not drowning
|
||||
if tonumber(tab.lvl) > HUNGER_HEAL_LVL and hp > 0 and air > 0 then
|
||||
player:set_hp(hp + HUNGER_HEAL)
|
||||
end
|
||||
|
||||
-- or damage player by 1 hp if saturation is < 2 (of 30)
|
||||
if tonumber(tab.lvl) < HUNGER_STARVE_LVL then
|
||||
player:set_hp(hp - HUNGER_STARVE)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
health_timer = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if minetest.setting_getbool("enable_damage") then
|
||||
minetest.register_globalstep(hunger_globaltimer)
|
||||
end
|
||||
|
||||
|
||||
-- food functions
|
||||
local food = hunger.food
|
||||
|
||||
function hunger.register_food(name, hunger_change, replace_with_item, poisen, heal, sound)
|
||||
food[name] = {}
|
||||
food[name].saturation = hunger_change -- hunger points added
|
||||
food[name].replace = replace_with_item -- what item is given back after eating
|
||||
food[name].poisen = poisen -- time its poisening
|
||||
food[name].healing = heal -- amount of HP
|
||||
food[name].sound = sound -- special sound that is played when eating
|
||||
end
|
||||
|
||||
-- Poison player
|
||||
local function poisenp(tick, time, time_left, player)
|
||||
time_left = time_left + tick
|
||||
if time_left < time then
|
||||
minetest.after(tick, poisenp, tick, time, time_left, player)
|
||||
else
|
||||
hud.change_item(player, "hunger", {text = "hud_hunger_fg.png"})
|
||||
end
|
||||
local hp = player:get_hp() -1 or 0
|
||||
if hp > 0 then
|
||||
player:set_hp(hp)
|
||||
end
|
||||
end
|
||||
|
||||
-- wrapper for minetest.item_eat (this way we make sure other mods can't break this one)
|
||||
local org_eat = core.do_item_eat
|
||||
core.do_item_eat = function(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
local old_itemstack = itemstack
|
||||
itemstack = hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
for _, callback in pairs(core.registered_on_item_eats) do
|
||||
local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing, old_itemstack)
|
||||
if result then
|
||||
return result
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
local item = itemstack:get_name()
|
||||
local def = food[item]
|
||||
if not def then
|
||||
def = {}
|
||||
if type(hp_change) ~= "number" then
|
||||
hp_change = 1
|
||||
core.log("error", "Wrong on_use() definition for item '" .. item .. "'")
|
||||
end
|
||||
def.saturation = hp_change * 1.3
|
||||
def.replace = replace_with_item
|
||||
end
|
||||
local func = hunger.item_eat(def.saturation, def.replace, def.poisen, def.healing, def.sound)
|
||||
return func(itemstack, user, pointed_thing)
|
||||
end
|
||||
|
||||
function hunger.item_eat(hunger_change, replace_with_item, poisen, heal, sound)
|
||||
return function(itemstack, user, pointed_thing)
|
||||
|
||||
if itemstack:take_item() == nil and user == nil then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local name = user:get_player_name()
|
||||
if not hunger.players[name] then
|
||||
return itemstack
|
||||
end
|
||||
local sat = tonumber(hunger.players[name].lvl or 0)
|
||||
local hp = user:get_hp()
|
||||
-- Saturation
|
||||
if sat < HUNGER_MAX and hunger_change then
|
||||
sat = sat + hunger_change
|
||||
hunger.update_hunger(user, sat)
|
||||
end
|
||||
-- Healing
|
||||
if hp < 20 and heal then
|
||||
hp = hp + heal
|
||||
if hp > 20 then
|
||||
hp = 20
|
||||
end
|
||||
user:set_hp(hp)
|
||||
end
|
||||
-- Poison
|
||||
if poisen then
|
||||
hud.change_item(user, "hunger", {text = "hunger_statbar_poisen.png"})
|
||||
poisenp(1.0, poisen, 0, user)
|
||||
end
|
||||
|
||||
-- eating sound
|
||||
sound = sound or "hunger_eat"
|
||||
minetest.sound_play(sound, {to_player = name, gain = 0.7})
|
||||
|
||||
if replace_with_item then
|
||||
if itemstack:is_empty() then
|
||||
itemstack:add_item(replace_with_item)
|
||||
else
|
||||
local inv = user:get_inventory()
|
||||
if inv:room_for_item("main", {name = replace_with_item}) then
|
||||
inv:add_item("main", replace_with_item)
|
||||
else
|
||||
local pos = user:getpos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
Simple Fast Inventory
|
||||
====================
|
||||
|
||||
![SFINV Screeny](https://cdn.pbrd.co/images/1yQhd1TI.png)
|
||||
|
||||
A cleaner, simpler, solution to having an advanced inventory in Minetest.
|
||||
|
||||
Written by rubenwardy.
|
||||
License: MIT
|
||||
|
||||
See game_api.txt for this mod's API
|
||||
|
||||
License of source code and media files:
|
||||
---------------------------------------
|
||||
Copyright (C) 2016 rubenwardy <rubenwardy@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,170 @@
|
|||
sfinv = {
|
||||
pages = {},
|
||||
pages_unordered = {},
|
||||
contexts = {},
|
||||
enabled = true
|
||||
}
|
||||
|
||||
function sfinv.register_page(name, def)
|
||||
assert(name, "Invalid sfinv page. Requires a name")
|
||||
assert(def, "Invalid sfinv page. Requires a def[inition] table")
|
||||
assert(def.get, "Invalid sfinv page. Def requires a get function.")
|
||||
assert(not sfinv.pages[name], "Attempt to register already registered sfinv page " .. dump(name))
|
||||
|
||||
sfinv.pages[name] = def
|
||||
def.name = name
|
||||
table.insert(sfinv.pages_unordered, def)
|
||||
end
|
||||
|
||||
function sfinv.override_page(name, def)
|
||||
assert(name, "Invalid sfinv page override. Requires a name")
|
||||
assert(def, "Invalid sfinv page override. Requires a def[inition] table")
|
||||
local page = sfinv.pages[name]
|
||||
assert(page, "Attempt to override sfinv page " .. dump(name) .. " which does not exist.")
|
||||
for key, value in pairs(def) do
|
||||
page[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
function sfinv.get_nav_fs(player, context, nav, current_idx)
|
||||
-- Only show tabs if there is more than one page
|
||||
if #nav > 1 then
|
||||
return "tabheader[0,0;tabs;" .. table.concat(nav, ",") .. ";" .. current_idx .. ";true;false]"
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local theme_main = "bgcolor[#080808BB;true]" .. default.gui_bg ..
|
||||
default.gui_bg_img
|
||||
|
||||
local theme_inv = default.gui_slots .. [[
|
||||
list[current_player;main;0,4.7;8,1;]
|
||||
list[current_player;main;0,5.85;8,3;8]
|
||||
]]
|
||||
|
||||
function sfinv.make_formspec(player, context, content, show_inv, size)
|
||||
local tmp = {
|
||||
size or "size[8,8.6]",
|
||||
theme_main,
|
||||
sfinv.get_nav_fs(player, context, context.nav_titles, context.nav_idx),
|
||||
content
|
||||
}
|
||||
if show_inv then
|
||||
tmp[#tmp + 1] = theme_inv
|
||||
end
|
||||
return table.concat(tmp, "")
|
||||
end
|
||||
|
||||
function sfinv.get_homepage_name(player)
|
||||
return "sfinv:crafting"
|
||||
end
|
||||
|
||||
function sfinv.get_formspec(player, context)
|
||||
-- Generate navigation tabs
|
||||
local nav = {}
|
||||
local nav_ids = {}
|
||||
local current_idx = 1
|
||||
for i, pdef in pairs(sfinv.pages_unordered) do
|
||||
if not pdef.is_in_nav or pdef:is_in_nav(player, context) then
|
||||
nav[#nav + 1] = pdef.title
|
||||
nav_ids[#nav_ids + 1] = pdef.name
|
||||
if pdef.name == context.page then
|
||||
current_idx = #nav_ids
|
||||
end
|
||||
end
|
||||
end
|
||||
context.nav = nav_ids
|
||||
context.nav_titles = nav
|
||||
context.nav_idx = current_idx
|
||||
|
||||
-- Generate formspec
|
||||
local page = sfinv.pages[context.page] or sfinv.pages["404"]
|
||||
if page then
|
||||
return page:get(player, context)
|
||||
else
|
||||
local old_page = context.page
|
||||
context.page = sfinv.get_homepage_name(player)
|
||||
assert(sfinv.pages[context.page], "[sfinv] Invalid homepage")
|
||||
minetest.log("warning", "[sfinv] Couldn't find " .. dump(old_page) .. " so using switching to homepage")
|
||||
return sfinv.get_formspec(player, context)
|
||||
end
|
||||
end
|
||||
|
||||
function sfinv.get_or_create_context(player)
|
||||
local name = player:get_player_name()
|
||||
local context = sfinv.contexts[name]
|
||||
if not context then
|
||||
context = {
|
||||
page = sfinv.get_homepage_name(player)
|
||||
}
|
||||
sfinv.contexts[name] = context
|
||||
end
|
||||
return context
|
||||
end
|
||||
|
||||
function sfinv.set_context(player, context)
|
||||
sfinv.contexts[player:get_player_name()] = context
|
||||
end
|
||||
|
||||
function sfinv.set_player_inventory_formspec(player, context)
|
||||
local fs = sfinv.get_formspec(player,
|
||||
context or sfinv.get_or_create_context(player))
|
||||
player:set_inventory_formspec(fs)
|
||||
end
|
||||
|
||||
function sfinv.set_page(player, pagename)
|
||||
local context = sfinv.get_or_create_context(player)
|
||||
local oldpage = sfinv.pages[context.page]
|
||||
if oldpage and oldpage.on_leave then
|
||||
oldpage:on_leave(player, context)
|
||||
end
|
||||
context.page = pagename
|
||||
local page = sfinv.pages[pagename]
|
||||
if page.on_enter then
|
||||
page:on_enter(player, context)
|
||||
end
|
||||
sfinv.set_player_inventory_formspec(player, context)
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if sfinv.enabled then
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
sfinv.contexts[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "" or not sfinv.enabled then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get Context
|
||||
local name = player:get_player_name()
|
||||
local context = sfinv.contexts[name]
|
||||
if not context then
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Was a tab selected?
|
||||
if fields.tabs and context.nav then
|
||||
local tid = tonumber(fields.tabs)
|
||||
if tid and tid > 0 then
|
||||
local id = context.nav[tid]
|
||||
local page = sfinv.pages[id]
|
||||
if id and page then
|
||||
sfinv.set_page(player, id)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Pass event to page
|
||||
local page = sfinv.pages[context.page]
|
||||
if page and page.on_player_receive_fields then
|
||||
return page:on_player_receive_fields(player, context, fields)
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,2 +1 @@
|
|||
default
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
dofile(minetest.get_modpath("sfinv") .. "/api.lua")
|
||||
|
||||
sfinv.register_page("sfinv:crafting", {
|
||||
title = "Crafting",
|
||||
get = function(self, player, context)
|
||||
return sfinv.make_formspec(player, context, [[
|
||||
list[current_player;craft;1.75,0.5;3,3;]
|
||||
list[current_player;craftpreview;5.75,1.5;1,1;]
|
||||
image[4.75,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]
|
||||
listring[current_player;main]
|
||||
listring[current_player;craft]
|
||||
image[0,4.75;1,1;gui_hb_bg.png]
|
||||
image[1,4.75;1,1;gui_hb_bg.png]
|
||||
image[2,4.75;1,1;gui_hb_bg.png]
|
||||
image[3,4.75;1,1;gui_hb_bg.png]
|
||||
image[4,4.75;1,1;gui_hb_bg.png]
|
||||
image[5,4.75;1,1;gui_hb_bg.png]
|
||||
image[6,4.75;1,1;gui_hb_bg.png]
|
||||
image[7,4.75;1,1;gui_hb_bg.png]
|
||||
]], true)
|
||||
end
|
||||
})
|
|
@ -1,121 +0,0 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
|
@ -1,23 +0,0 @@
|
|||
Afk Kick mod for Minetest by GunshipPenguin
|
||||
|
||||
Kicks players after they are Afk for an amount of time. By default,
|
||||
players are kicked after five minutes, although this can be configured.
|
||||
|
||||
Licence: CC0 (see COPYING file)
|
||||
|
||||
This mod can be configured by changing the variables declared in the
|
||||
start of init.lua. The following is a brief explanation of each one.
|
||||
|
||||
MAX_INACTIVE_TIME (default 300)
|
||||
|
||||
Maximum amount of time that a player may remain AFK (in seconds)
|
||||
before being kicked.
|
||||
|
||||
CHECK_INTERVAL (default 1)
|
||||
|
||||
Time between checks for inactivity (In seconds)
|
||||
|
||||
WARN_TIME (default 20)
|
||||
|
||||
Number of seconds remaining before being kicked that a player will
|
||||
start to be warned via chat message.
|
|
@ -1,65 +0,0 @@
|
|||
--[[
|
||||
Afk Kick mod for Minetest by GunshipPenguin
|
||||
|
||||
To the extent possible under law, the author(s)
|
||||
have dedicated all copyright and related and neighboring rights
|
||||
to this software to the public domain worldwide. This software is
|
||||
distributed without any warranty.
|
||||
]]
|
||||
|
||||
local MAX_INACTIVE_TIME = 11200
|
||||
local CHECK_INTERVAL = 1
|
||||
local WARN_TIME = 20
|
||||
|
||||
local players = {}
|
||||
local checkTimer = 0
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local playerName = player:get_player_name()
|
||||
players[playerName] = {
|
||||
lastAction = minetest.get_gametime()
|
||||
}
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local playerName = player:get_player_name()
|
||||
players[playerName] = nil
|
||||
end)
|
||||
|
||||
minetest.register_on_chat_message(function(playerName, message)
|
||||
players[playerName]["lastAction"] = minetest.get_gametime()
|
||||
end)
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
local currGameTime = minetest.get_gametime()
|
||||
|
||||
--Loop through each player in players
|
||||
for playerName,_ in pairs(players) do
|
||||
local player = minetest.get_player_by_name(playerName)
|
||||
if player then
|
||||
|
||||
--Check for inactivity once every CHECK_INTERVAL seconds
|
||||
checkTimer = checkTimer + dtime
|
||||
if checkTimer > CHECK_INTERVAL then
|
||||
checkTimer = 0
|
||||
|
||||
--Kick player if he/she has been inactive for longer than MAX_INACTIVE_TIME seconds
|
||||
if players[playerName]["lastAction"] + MAX_INACTIVE_TIME < currGameTime then
|
||||
minetest.kick_player(playerName, "Kicked for inactivity")
|
||||
end
|
||||
|
||||
--Warn player if he/she has less than WARN_TIME seconds to move or be kicked
|
||||
if players[playerName]["lastAction"] + MAX_INACTIVE_TIME - WARN_TIME < currGameTime then
|
||||
minetest.chat_send_player(playerName, "Warning, you have " .. tostring(players[playerName]["lastAction"] + MAX_INACTIVE_TIME - currGameTime) .. " seconds to move or be kicked")
|
||||
end
|
||||
end
|
||||
|
||||
--Check if this player is doing an action
|
||||
for _,keyPressed in pairs(player:get_player_control()) do
|
||||
if keyPressed then
|
||||
players[playerName]["lastAction"] = currGameTime
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,5 +0,0 @@
|
|||
basic_vote - simple voting for minetest
|
||||
|
||||
Start vote simply with chatcommand "vote". for vote to suceed at least 50% of connected players must vote and there must be more yes votes than no votes. Player with kick privs has a lot of vote pover.
|
||||
|
||||
- add custom votes easily by defining vote types and what voting does
|
|
@ -1 +0,0 @@
|
|||
anticheat?
|
|
@ -1,348 +0,0 @@
|
|||
-- basic vote by rnd, 2015
|
||||
|
||||
local basic_vote = {};
|
||||
|
||||
-- SETTINGS ----------------------------------------------------------------------
|
||||
|
||||
-- DEFINE VOTE TYPES
|
||||
|
||||
basic_vote.types = { -- [type] = { description , votes_needed , timeout, command, help_description}
|
||||
[1] = {"ban %s for 2 minutes" , -3 , 30, "ban", "Ban player for 2 minutes"}, -- -3 means strictly more than 3 players need to vote ( so 4 or more)
|
||||
[2] = {"remove interact of %s" , 0.5, 120, "remove_interact", "Remove 'interact' privilege from player"}, -- 0.5 means at least 50% need to vote
|
||||
[3] = {"give interact to %s" , 0.5 , 120, "give_interact", "Give 'interact' privilege to player"},
|
||||
[4] = {"kill %s" , -3 , 30, "kill", "Kill player"},
|
||||
[5] = {"poison %s" , -3 , 30, "poison", "Poison player"},
|
||||
[6] = {"teleport %s to vote starter" , -3 , 30, "teleport", "Teleport player to you"},
|
||||
[7] = {"change name color of %s",-2,30,"name color","Change name of player"},
|
||||
[8] = {"mutelate %s",-2,30,"mutelate", "Mute and kill player when talking"},
|
||||
[9] = {"unmutelate",-2,30,"unmutelate","Undo effects of mutelate"},
|
||||
[10] = {"ask",1.0,30,"ask","put a question up for voting"}
|
||||
};
|
||||
basic_vote.modreq = 2; -- more that this number of moderators from "anticheat" mod must vote for mod to succeed
|
||||
|
||||
-- needed for poison vote
|
||||
local vote_poison_state = {};
|
||||
basic_vote_poison = function(name)
|
||||
|
||||
local player = minetest.get_player_by_name(name);
|
||||
|
||||
if not vote_poison_state[name] then
|
||||
vote_poison_state[name] = 60;
|
||||
end
|
||||
|
||||
vote_poison_state[name] = vote_poison_state[name] - 1;
|
||||
if vote_poison_state[name]<=0 then
|
||||
vote_poison_state[name] = nil; return;
|
||||
end
|
||||
|
||||
if player then
|
||||
if player:get_hp()>0 then
|
||||
player:set_hp(player:get_hp()-4);
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after(2, function() basic_vote_poison(name) end)
|
||||
|
||||
end
|
||||
|
||||
basic_vote.kicklist = {};
|
||||
basic_vote.talklist = {};
|
||||
basic_vote.huds = {};
|
||||
|
||||
-- for hud votes
|
||||
|
||||
local hud_definition =
|
||||
{
|
||||
hud_elem_type = "image",
|
||||
scale = {x=-50,y=-50},
|
||||
text = "default_stone.png",
|
||||
size = { x=50, y=50 },
|
||||
offset = { x=0, y=0},
|
||||
}
|
||||
|
||||
|
||||
-- DEFINE WHAT HAPPENS WHEN VOTE SUCCEEDS
|
||||
basic_vote.execute = function(type, name, reason)
|
||||
|
||||
if type == 1 then
|
||||
local ip = tostring(minetest.get_player_ip(name));
|
||||
basic_vote.kicklist[ip] = minetest.get_gametime(); -- remembers start time
|
||||
minetest.kick_player(name, reason)
|
||||
|
||||
elseif type == 2 then
|
||||
|
||||
local privs = core.get_player_privs(name);privs.interact = false
|
||||
core.set_player_privs(name, privs); minetest.auth_reload()
|
||||
|
||||
elseif type == 3 then
|
||||
|
||||
local privs = core.get_player_privs(name);privs.interact = true;
|
||||
core.set_player_privs(name, privs); minetest.auth_reload()
|
||||
|
||||
elseif type == 4 then
|
||||
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
player:set_hp(0);
|
||||
|
||||
elseif type == 5 then
|
||||
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
if not vote_poison_state[name] then
|
||||
basic_vote_poison(name);
|
||||
end
|
||||
|
||||
elseif type == 6 then
|
||||
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
local vname = basic_vote.vote.voter; local vplayer = minetest.get_player_by_name(vname);
|
||||
if not vplayer then return end
|
||||
player:setpos(vplayer:getpos());
|
||||
|
||||
elseif type == 7 then
|
||||
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
player:set_nametag_attributes({color = basic_vote.vote.reason});
|
||||
|
||||
elseif type == 8 then
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
basic_vote.talklist[name]=1;
|
||||
|
||||
elseif type == 9 then
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
basic_vote.talklist[name]=nil;
|
||||
|
||||
elseif type == 10 then
|
||||
--basic_vote.huds[name]=player:hud_add(hud_definition);
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- for ban vote
|
||||
minetest.register_on_prejoinplayer(
|
||||
function(name, ip)
|
||||
local name;
|
||||
if basic_vote.kicklist[ip] then
|
||||
|
||||
local t = minetest.get_gametime();
|
||||
t=t-basic_vote.kicklist[ip];
|
||||
if t>120 then
|
||||
basic_vote.kicklist[ip] = nil;
|
||||
else
|
||||
return "You have been temporarily banned from the server."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
)
|
||||
|
||||
-- for talking votes
|
||||
|
||||
minetest.register_on_chat_message(
|
||||
function(name, message)
|
||||
|
||||
|
||||
if basic_vote.talklist[name] then
|
||||
if basic_vote.talklist[name] == 1 then -- kill
|
||||
local player = minetest.get_player_by_name(name);
|
||||
if not player then return end
|
||||
if not player:get_inventory():is_empty("main") then
|
||||
local p = player:getpos();
|
||||
p.x=math.floor(p.x);p.y=math.floor(p.y);p.z=math.floor(p.z);
|
||||
minetest.chat_send_all("<" .. name .. "> please come get my bones at " .. minetest.pos_to_string(p))
|
||||
end
|
||||
player:set_hp(0);
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- END OF SETTINGS ---------------------------------------------------------------
|
||||
|
||||
basic_vote.votes = 0; -- vote count
|
||||
basic_vote.modscore = 0; -- how many moderators voted - need 3 for vote to succeed
|
||||
basic_vote.voters = {}; -- who voted already
|
||||
basic_vote.state = 0; -- 0 no vote, 1 vote in progress,2 timeout
|
||||
basic_vote.vote = {time = 0,type = 0, name = "", reason = "", votes_needed = 0, timeout = 0, }; -- description of current vote
|
||||
|
||||
|
||||
basic_vote.requirements = {[0]=0}
|
||||
basic_vote.vote_desc=""
|
||||
for i=1,#basic_vote.types do
|
||||
basic_vote.vote_desc = basic_vote.vote_desc .. "Type " .. i .. " (" ..basic_vote.types[i][4].. "): ".. basic_vote.types[i][5].."\n"
|
||||
end
|
||||
|
||||
local function get_description(vote)
|
||||
local type_str = string.format(basic_vote.types[basic_vote.vote.type][1], basic_vote.vote.name)
|
||||
local timeout = math.max(0, vote.timeout - os.difftime(os.time(), vote.time_start))
|
||||
if vote.reason == nil or vote.reason == "" then
|
||||
return string.format("## VOTE by %s to %s. Timeout in %ds.", vote.voter, type_str, timeout)
|
||||
else
|
||||
return string.format("## VOTE by %s to %s with reason: '%s'. Timeout in %ds.", vote.voter, type_str, vote.reason, timeout)
|
||||
end
|
||||
end
|
||||
|
||||
-- starts a new vote
|
||||
minetest.register_chatcommand("vote", {
|
||||
privs = {
|
||||
interact = true
|
||||
},
|
||||
params = "[[<type> <name> [<reason>]] | types]",
|
||||
description = "Start a vote. '/vote types' for a list of types, '/vote' without arguments to see current voting progress",
|
||||
func = function(name, param)
|
||||
|
||||
if basic_vote.state~=0 then
|
||||
minetest.chat_send_player(name,"Vote already in progress:")
|
||||
minetest.chat_send_player(name,get_description(basic_vote.vote));
|
||||
return
|
||||
elseif param == "" then
|
||||
minetest.chat_send_player(name,"No vote in progress.")
|
||||
return
|
||||
end
|
||||
local player = minetest.get_player_by_name(name);
|
||||
|
||||
-- split string param into parameters
|
||||
local paramt = string.split(param, " ")
|
||||
for i = #paramt+1,3 do paramt[i]="" end
|
||||
|
||||
|
||||
if paramt[1] == "types" then minetest.chat_send_player(name, basic_vote.vote_desc) return end
|
||||
|
||||
basic_vote.vote.time = minetest.get_gametime();
|
||||
basic_vote.vote.type = tonumber(paramt[1]);
|
||||
-- check for text-based types
|
||||
-- if basic_vote.vote.type == nil then
|
||||
-- for i=1,#basic_vote.types do
|
||||
-- if paramt[1] == basic_vote.types[i][4] then
|
||||
-- basic_vote.vote.type = i
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
if not basic_vote.types[basic_vote.vote.type] then
|
||||
minetest.chat_send_player(name,"Error: Invalid syntax or type. Use '/help vote' for help.")
|
||||
return
|
||||
end
|
||||
|
||||
-- if not basic_vote.vote.type then minetest.chat_send_player(name,"Error: Invalid syntax or type. Use '/help vote' for help.") return end
|
||||
|
||||
basic_vote.vote.name=paramt[2] or "an unknown player";
|
||||
basic_vote.vote.voter = name;
|
||||
basic_vote.vote.reason = string.match(param, "%w+ [%w_-]+ (.+)")
|
||||
basic_vote.vote.votes_needed = basic_vote.types[ basic_vote.vote.type ][2];
|
||||
basic_vote.vote.timeout = basic_vote.types[ basic_vote.vote.type ][3];
|
||||
basic_vote.vote.time_start = os.time();
|
||||
|
||||
|
||||
--check if target valid player
|
||||
if basic_vote.vote.name == "" then
|
||||
minetest.chat_send_player(name,"Error: No player specified.")
|
||||
return
|
||||
elseif not minetest.get_player_by_name(basic_vote.vote.name) and basic_vote.vote.type~= 1 then
|
||||
minetest.chat_send_player(name,"Error: The specified player is currently not connected.")
|
||||
return
|
||||
end
|
||||
|
||||
-- check anticheat db
|
||||
local ip = tostring(minetest.get_player_ip(basic_vote.vote.name));
|
||||
if anticheatdb and anticheatdb[ip] then -- #anticheat mod: makes detected cheater more succeptible to voting
|
||||
if anticheatsettings.moderators[name] then -- moderator must call vote
|
||||
basic_vote.vote.votes_needed=0; -- just need 1 vote
|
||||
name = "an anonymous player"; -- so cheater does not see who voted - anonymous vote
|
||||
end
|
||||
end
|
||||
|
||||
basic_vote.votes = 0; basic_vote.modscore = 0; basic_vote.voters = {};
|
||||
|
||||
local type_str = string.format(basic_vote.types[basic_vote.vote.type][1], basic_vote.vote.name)
|
||||
|
||||
if basic_vote.vote.reason == nil or basic_vote.vote.reason == "" then
|
||||
minetest.chat_send_all(string.format("## VOTE started (by %s to %s).\nSay '/y' to vote 'yes'. Timeout in %ds.", name, type_str, basic_vote.vote.timeout))
|
||||
else
|
||||
minetest.chat_send_all(string.format("## VOTE started (by %s to %s) with reason: '%s'.\nSay '/y' to vote 'yes'. Timeout in %ds.", name, type_str, basic_vote.vote.reason, basic_vote.vote.timeout))
|
||||
end
|
||||
|
||||
basic_vote.state = 1; minetest.after(basic_vote.vote.timeout, function()
|
||||
if basic_vote.state == 1 then basic_vote.state = 2;basic_vote.update(); end
|
||||
end)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
-- check if enough votes for vote to succeed or fail vote if timeout
|
||||
basic_vote.update = function()
|
||||
local players=minetest.get_connected_players();
|
||||
local count = #players;
|
||||
|
||||
local votes_needed;
|
||||
|
||||
if basic_vote.vote.votes_needed>0 then
|
||||
votes_needed = basic_vote.vote.votes_needed*count; -- percent of all players
|
||||
if basic_vote.vote.votes_needed>=0.5 then -- more serious vote, to prevent ppl voting serious stuff with few players on server, at least 6 votes needed
|
||||
if votes_needed<6 then votes_needed = 6 end
|
||||
end
|
||||
|
||||
else
|
||||
votes_needed = -basic_vote.vote.votes_needed; -- number instead
|
||||
end
|
||||
|
||||
if basic_vote.state == 2 then -- timeout
|
||||
minetest.chat_send_all("## VOTE failed. ".. basic_vote.votes .." voted (needed more than ".. votes_needed ..").");
|
||||
basic_vote.state = 0;basic_vote.vote = {time = 0,type = 0, name = "", reason = ""}; return
|
||||
end
|
||||
if basic_vote.state~=1 then return end -- no vote in progress
|
||||
|
||||
-- check if enough votes
|
||||
|
||||
if basic_vote.modscore> basic_vote.modreq then -- enough moderators voted for vote to succeed
|
||||
basic_vote.votes = votes_needed+1;
|
||||
end
|
||||
|
||||
if basic_vote.votes>votes_needed then -- enough voters
|
||||
minetest.chat_send_all("## VOTE succeded. "..basic_vote.votes .." voted.");
|
||||
local type = basic_vote.vote.type;
|
||||
basic_vote.execute(basic_vote.vote.type,basic_vote.vote.name, basic_vote.vote.reason)
|
||||
basic_vote.state = 0;basic_vote.vote = {time = 0,type = 0, name = "", reason = ""};
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local cast_vote = function (name,param)
|
||||
if basic_vote.state~=1 then
|
||||
-- vote not in progress
|
||||
minetest.chat_send_player(name,"Error: No vote in progress.");
|
||||
return
|
||||
end
|
||||
local ip = tostring(minetest.get_player_ip(name));
|
||||
if basic_vote.voters[ip] then
|
||||
minetest.chat_send_player(name,"Error: You already voted.");
|
||||
return
|
||||
else
|
||||
-- mark as already voted
|
||||
basic_vote.voters[ip]=true
|
||||
end
|
||||
basic_vote.votes = basic_vote.votes+1;
|
||||
if anticheatsettings and anticheatsettings.moderators[name] then -- moderator from anticheat mod
|
||||
basic_vote.modscore=basic_vote.modscore+1;
|
||||
end
|
||||
local privs = core.get_player_privs(name);if privs.kick then basic_vote.votes = 100; end
|
||||
basic_vote.update(); minetest.chat_send_player(name,"Vote received.");
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("y", {
|
||||
privs = {
|
||||
interact = true
|
||||
},
|
||||
params = "",
|
||||
description = "Vote 'Yes.' in the current vote (see vote command)",
|
||||
func = function(name, param)
|
||||
cast_vote(name,param)
|
||||
end
|
||||
}
|
||||
)
|
|
@ -65,7 +65,7 @@ messages.fire = {
|
|||
" just got roasted, hotdog style.",
|
||||
" got burned up. More light that way."
|
||||
}
|
||||
--[[
|
||||
|
||||
-- Other death messages
|
||||
messages.other = {
|
||||
" died.",
|
||||
|
@ -77,7 +77,7 @@ messages.other = {
|
|||
" is a rusher.",
|
||||
" loves maikerumine's youtube channel!"
|
||||
}
|
||||
]]
|
||||
|
||||
function get_message(mtype)
|
||||
if RANDOM_MESSAGES then
|
||||
return messages[mtype][math.random(1, #messages[mtype])]
|
||||
|
@ -107,7 +107,7 @@ minetest.register_on_dieplayer(function(player)
|
|||
else
|
||||
--minetest.chat_send_all(player_name .. get_message("other"))
|
||||
end
|
||||
player:setpos(death)
|
||||
--player:setpos(death)
|
||||
end)
|
||||
--bigfoot code
|
||||
-- bigfoot547's death messages
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
Lightning mod for minetest
|
||||
|
||||
|
||||
Copyright (C) 2016 - Auke Kok <sofar@foo-projects.org>
|
||||
|
||||
"lightning" 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 2.1
|
||||
of the license, or (at your option) any later version.
|
||||
|
||||
|
||||
Textures: CC-BY-SA-4.0 by sofar
|
||||
lightning_1.png
|
||||
lightning_2.png
|
||||
lightning_3.png
|
||||
|
||||
Sounds:
|
||||
thunder.1.ogg - CC-BY-SA - hantorio - http://www.freesound.org/people/hantorio/sounds/121945/
|
||||
thunder.2.ogg - CC-BY-SA - juskiddink - http://www.freesound.org/people/juskiddink/sounds/101948/
|
||||
thunder.3.ogg - CC-BY-SA - IllusiaProductions - http://www.freesound.org/people/IllusiaProductions/sounds/249950/
|
|
@ -0,0 +1,2 @@
|
|||
default?
|
||||
fire?
|
|
@ -0,0 +1 @@
|
|||
A mod that adds thunder and lightning effects.
|
|
@ -0,0 +1,225 @@
|
|||
|
||||
--[[
|
||||
|
||||
Copyright (C) 2016 - Auke Kok <sofar@foo-projects.org>
|
||||
|
||||
"lightning" 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 2.1
|
||||
of the license, or (at your option) any later version.
|
||||
|
||||
--]]
|
||||
|
||||
lightning = {}
|
||||
|
||||
lightning.interval_low = 17
|
||||
lightning.interval_high = 503
|
||||
lightning.range_h = 100
|
||||
lightning.range_v = 50
|
||||
lightning.size = 100
|
||||
-- disable this to stop lightning mod from striking
|
||||
lightning.auto = true
|
||||
|
||||
local rng = PcgRandom(32321123312123)
|
||||
|
||||
local ps = {}
|
||||
local ttl = 1
|
||||
|
||||
local revertsky = function()
|
||||
if ttl == 0 then
|
||||
return
|
||||
end
|
||||
ttl = ttl - 1
|
||||
if ttl > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
for key, entry in pairs(ps) do
|
||||
local sky = entry.sky
|
||||
entry.p:set_sky(sky.bgcolor, sky.type, sky.textures)
|
||||
end
|
||||
|
||||
ps = {}
|
||||
end
|
||||
|
||||
minetest.register_globalstep(revertsky)
|
||||
|
||||
-- select a random strike point, midpoint
|
||||
local function choose_pos(pos)
|
||||
if not pos then
|
||||
local playerlist = minetest.get_connected_players()
|
||||
local playercount = table.getn(playerlist)
|
||||
|
||||
-- nobody on
|
||||
if playercount == 0 then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local r = rng:next(1, playercount)
|
||||
local randomplayer = playerlist[r]
|
||||
pos = randomplayer:getpos()
|
||||
|
||||
-- avoid striking underground
|
||||
if pos.y < -20 then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
pos.x = math.floor(pos.x - (lightning.range_h / 2) + rng:next(1, lightning.range_h))
|
||||
pos.y = pos.y + (lightning.range_v / 2)
|
||||
pos.z = math.floor(pos.z - (lightning.range_h / 2) + rng:next(1, lightning.range_h))
|
||||
end
|
||||
|
||||
local b, pos2 = minetest.line_of_sight(pos, {x = pos.x, y = pos.y - lightning.range_v, z = pos.z}, 1)
|
||||
|
||||
-- nothing but air found
|
||||
if b then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local n = minetest.get_node({x = pos2.x, y = pos2.y - 1/2, z = pos2.z})
|
||||
if n.name == "air" or n.name == "ignore" then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return pos, pos2
|
||||
end
|
||||
|
||||
-- lightning strike API
|
||||
-- * pos: optional, if not given a random pos will be chosen
|
||||
-- * returns: bool - success if a strike happened
|
||||
lightning.strike = function(pos)
|
||||
if lightning.auto then
|
||||
minetest.after(rng:next(lightning.interval_low, lightning.interval_high), lightning.strike)
|
||||
end
|
||||
|
||||
local pos2
|
||||
pos, pos2 = choose_pos(pos)
|
||||
|
||||
if not pos then
|
||||
return false
|
||||
end
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 1,
|
||||
time = 0.2,
|
||||
-- make it hit the top of a block exactly with the bottom
|
||||
minpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
|
||||
maxpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
|
||||
minvel = {x = 0, y = 0, z = 0},
|
||||
maxvel = {x = 0, y = 0, z = 0},
|
||||
minacc = {x = 0, y = 0, z = 0},
|
||||
maxacc = {x = 0, y = 0, z = 0},
|
||||
minexptime = 0.2,
|
||||
maxexptime = 0.2,
|
||||
minsize = lightning.size * 10,
|
||||
maxsize = lightning.size * 10,
|
||||
collisiondetection = true,
|
||||
vertical = true,
|
||||
-- to make it appear hitting the node that will get set on fire, make sure
|
||||
-- to make the texture lightning bolt hit exactly in the middle of the
|
||||
-- texture (e.g. 127/128 on a 256x wide texture)
|
||||
texture = "lightning_lightning_" .. rng:next(1,3) .. ".png",
|
||||
-- 0.4.15+
|
||||
glow = 14,
|
||||
})
|
||||
|
||||
minetest.sound_play({ pos = pos, name = "lightning_thunder", gain = 10, max_hear_distance = 500 })
|
||||
|
||||
-- damage nearby objects, player or not
|
||||
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 5)) do
|
||||
-- nil as param#1 is supposed to work, but core can't handle it.
|
||||
obj:punch(obj, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy=8}}, nil)
|
||||
end
|
||||
|
||||
local playerlist = minetest.get_connected_players()
|
||||
for i = 1, #playerlist do
|
||||
local player = playerlist[i]
|
||||
local sky = {}
|
||||
|
||||
sky.bgcolor, sky.type, sky.textures = player:get_sky()
|
||||
|
||||
local name = player:get_player_name()
|
||||
if ps[name] == nil then
|
||||
ps[name] = {p = player, sky = sky}
|
||||
player:set_sky(0xffffff, "plain", {})
|
||||
end
|
||||
end
|
||||
|
||||
-- trigger revert of skybox
|
||||
ttl = 5
|
||||
|
||||
-- set the air node above it on fire
|
||||
pos2.y = pos2.y + 1/2
|
||||
if minetest.get_item_group(minetest.get_node({x = pos2.x, y = pos2.y - 1, z = pos2.z}).name, "liquid") < 1 then
|
||||
if minetest.get_node(pos2).name == "air" then
|
||||
-- only 1/4 of the time, something is changed
|
||||
if rng:next(1,4) > 1 then
|
||||
return
|
||||
end
|
||||
-- very rarely, potentially cause a fire
|
||||
if fire and rng:next(1,1000) == 1 then
|
||||
minetest.set_node(pos2, {name = "fire:basic_flame"})
|
||||
else
|
||||
minetest.set_node(pos2, {name = "lightning:dying_flame"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- perform block modifications
|
||||
if not default or rng:next(1,10) > 1 then
|
||||
return
|
||||
end
|
||||
pos2.y = pos2.y - 1
|
||||
local n = minetest.get_node(pos2)
|
||||
if minetest.get_item_group(n.name, "tree") > 0 then
|
||||
minetest.set_node(pos2, { name = "default:coalblock"})
|
||||
elseif minetest.get_item_group(n.name, "sand") > 0 then
|
||||
minetest.set_node(pos2, { name = "default:glass"})
|
||||
elseif minetest.get_item_group(n.name, "soil") > 0 then
|
||||
minetest.set_node(pos2, { name = "default:gravel"})
|
||||
end
|
||||
end
|
||||
|
||||
-- a special fire node that doesn't burn anything, and automatically disappears
|
||||
minetest.register_node("lightning:dying_flame", {
|
||||
description = "Dying Flame",
|
||||
drawtype = "firelike",
|
||||
tiles = {
|
||||
{
|
||||
name = "fire_basic_flame_animated.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 1
|
||||
},
|
||||
},
|
||||
},
|
||||
inventory_image = "fire_basic_flame.png",
|
||||
paramtype = "light",
|
||||
light_source = 14,
|
||||
walkable = false,
|
||||
buildable_to = true,
|
||||
sunlight_propagates = true,
|
||||
damage_per_second = 4,
|
||||
groups = {dig_immediate = 3, not_in_creative_inventory=1},
|
||||
on_timer = function(pos)
|
||||
minetest.remove_node(pos)
|
||||
end,
|
||||
drop = "",
|
||||
|
||||
on_construct = function(pos)
|
||||
minetest.get_node_timer(pos):start(rng:next(20, 40))
|
||||
if fire and fire.on_flame_add_at then
|
||||
minetest.after(0.5, fire.on_flame_add_at, pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- if other mods disable auto lightning during initialization, don't trigger the first lightning.
|
||||
minetest.after(5, function(dtime)
|
||||
if lightning.auto then
|
||||
minetest.after(rng:next(lightning.interval_low,
|
||||
lightning.interval_high), lightning.strike)
|
||||
end
|
||||
end)
|
|
@ -0,0 +1 @@
|
|||
name = lightning
|
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
|
@ -1,119 +0,0 @@
|
|||
minetest.register_privilege("whois", {
|
||||
description = "Allows player to see other player IPs"})
|
||||
|
||||
-- Created by Krock to stop mass-account-creators
|
||||
-- License: WTFPL
|
||||
|
||||
ipnames = {}
|
||||
ipnames.data = {}
|
||||
ipnames.tmp_data = {}
|
||||
ipnames.changes = false
|
||||
ipnames.save_interval = 120
|
||||
ipnames.save_time = 0
|
||||
ipnames.file = minetest.get_worldpath().."/ipnames.txt"
|
||||
|
||||
ipnames.name_per_ip_limit = tonumber(minetest.setting_get("max_names_per_ip")) or 4
|
||||
|
||||
-- Get accounts self:
|
||||
minetest.register_chatcommand("whois", {
|
||||
description = "Gets all players who have the same IP as the specified player",
|
||||
privs = {whois = true},
|
||||
func = function(name, param)
|
||||
if not ipnames.data[param] then
|
||||
minetest.chat_send_player(name, "The player \"" .. param .. "\" did not join yet.")
|
||||
return
|
||||
end
|
||||
|
||||
local ip = ipnames.data[param]
|
||||
local names = "";
|
||||
for k, v in pairs(ipnames.data) do
|
||||
if v == ip then
|
||||
if names ~= "" then
|
||||
names = names .. ", " .. k
|
||||
else
|
||||
names = names .. " " .. k
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, "Players for IP address " .. ip .. ": " .. names)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Get IP if player tries to join, ban if there are too much names per IP:
|
||||
minetest.register_on_prejoinplayer(function(name, ip)
|
||||
-- Only stop new accounts:
|
||||
ipnames.tmp_data[name] = ip
|
||||
if not ipnames.data[name] then
|
||||
local count = 1
|
||||
local names = ""
|
||||
for k, v in pairs(ipnames.data) do
|
||||
if v == ip then
|
||||
count = count + 1
|
||||
names = names .. k .. ", "
|
||||
end
|
||||
end
|
||||
|
||||
if count <= ipnames.name_per_ip_limit and count > 1 then
|
||||
minetest.log("action", name .. " now has " .. count .. " accounts. Other accounts: " .. names)
|
||||
end
|
||||
|
||||
if count > ipnames.name_per_ip_limit then
|
||||
ipnames.tmp_data[name] = nil
|
||||
if tostring(ip) ~= "127.0.0.1" then
|
||||
return ("\nYou exceeded the limit of accounts (" .. ipnames.name_per_ip_limit ..
|
||||
").\nYou already have the following accounts:\n" .. names)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Save IP if player joined:
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
ipnames.data[name] = ipnames.tmp_data[name]
|
||||
ipnames.tmp_data[name] = nil
|
||||
ipnames.changes = true
|
||||
end)
|
||||
|
||||
function ipnames.load_data()
|
||||
local file = io.open(ipnames.file, "r")
|
||||
if not file then
|
||||
return
|
||||
end
|
||||
for line in file:lines() do
|
||||
if line ~= "" then
|
||||
local data = line:split("|")
|
||||
if #data >= 2 then
|
||||
ipnames.data[data[1]] = data[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
io.close(file)
|
||||
end
|
||||
|
||||
function ipnames.save_data()
|
||||
if not ipnames.changes then
|
||||
return
|
||||
end
|
||||
ipnames.changes = false
|
||||
local file = io.open(ipnames.file, "w")
|
||||
for i, v in pairs(ipnames.data) do
|
||||
if v ~= nil then
|
||||
file:write(i .. "|" .. v .. "\n")
|
||||
end
|
||||
end
|
||||
io.close(file)
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(t)
|
||||
ipnames.save_time = ipnames.save_time + t
|
||||
if ipnames.save_time < ipnames.save_interval then
|
||||
return
|
||||
end
|
||||
ipnames.save_time = 0
|
||||
ipnames.save_data()
|
||||
end)
|
||||
|
||||
minetest.register_on_shutdown(function() ipnames.save_data() end)
|
||||
|
||||
minetest.after(3, function() ipnames.load_data() end)
|
|
@ -0,0 +1,4 @@
|
|||
*~
|
||||
.settings/*
|
||||
.project
|
||||
.buildpath
|
|
@ -0,0 +1,47 @@
|
|||
weather-pack
|
||||
=======================
|
||||
Weather mod for Minetest (http://minetest.net/)
|
||||
|
||||
Weathers included
|
||||
-----------------------
|
||||
* rain
|
||||
* snow
|
||||
* thunder
|
||||
|
||||
Commands
|
||||
-----------------------
|
||||
`set_weather <weather>` requires `weather_manager` privilege.
|
||||
|
||||
Dependencies
|
||||
-----------------------
|
||||
Thunder weather requres [lightning](https://github.com/minetest-mods/lightning) mod.
|
||||
|
||||
Configuration properties
|
||||
-----------------------
|
||||
Weather mod for indoor check depends on sunlight propogation check. Some nodes (e.g. glass block) propogates sunlight and thus weather particles will go through it. To change that set `weather_allow_override_nodes=true` in `minetest.conf` file. Be aware that just few nodes will be override and these blocks needs to be re-builded to take effect. Maybe in future other 'cheap' way to check indoor will be available.
|
||||
|
||||
Weather mod mostly relies on particles generation however for some small things ABM may be used. Users which do not want it can disable ABM with property `weather_allow_abm=false`.
|
||||
|
||||
License of source code:
|
||||
-----------------------
|
||||
LGPL 2.1+
|
||||
|
||||
Authors of media files:
|
||||
-----------------------
|
||||
|
||||
TeddyDesTodes:
|
||||
Snowflakes licensed under CC-BY-SA 3.0 by from weather branch at https://github.com/TeddyDesTodes/minetest/tree/weather
|
||||
|
||||
* `weather_pack_snow_snowflake1.png` - CC-BY-SA 3.0
|
||||
* `weather_pack_snow_snowflake2.png` - CC-BY-SA 3.0
|
||||
|
||||
xeranas:
|
||||
|
||||
* `weather_pack_rain_raindrop_1.png` - CC-0
|
||||
* `weather_pack_rain_raindrop_2.png` - CC-0
|
||||
* `weather_pack_rain_raindrop_3.png` - CC-0
|
||||
|
||||
inchadney (http://freesound.org/people/inchadney/):
|
||||
|
||||
* `weather_rain.ogg` - CC-BY-SA 3.0 (cut from http://freesound.org/people/inchadney/sounds/58835/)
|
||||
|
|
@ -0,0 +1 @@
|
|||
lightning?
|
|
@ -0,0 +1 @@
|
|||
Set of weathers for minetest.
|
|
@ -0,0 +1,13 @@
|
|||
local modpath = minetest.get_modpath("weather_pack");
|
||||
dofile(modpath.."/weather_core.lua")
|
||||
dofile(modpath.."/snow.lua")
|
||||
dofile(modpath.."/rain.lua")
|
||||
|
||||
if minetest.get_modpath("lightning") ~= nil then
|
||||
dofile(modpath.."/thunder.lua")
|
||||
end
|
||||
|
||||
-- If not located then embeded skycolor mod version will be loaded.
|
||||
if minetest.get_modpath("skycolor") == nil then
|
||||
dofile(modpath.."/skycolor.lua")
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
name = weather_pack
|
|
@ -0,0 +1,186 @@
|
|||
rain = {
|
||||
-- max rain particles created at time
|
||||
particles_count = 35,
|
||||
|
||||
-- flag to turn on/off extinguish fire for rain
|
||||
extinguish_fire = true,
|
||||
|
||||
-- flag useful when mixing weathers
|
||||
raining = false,
|
||||
|
||||
-- keeping last timeofday value (rounded).
|
||||
-- Defaulted to non-existing value for initial comparing.
|
||||
sky_last_update = -1,
|
||||
|
||||
init_done = false,
|
||||
}
|
||||
|
||||
rain.sound_handler = function(player)
|
||||
return minetest.sound_play("weather_rain", {
|
||||
object = player,
|
||||
max_hear_distance = 2,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
|
||||
-- set skybox based on time (uses skycolor api)
|
||||
rain.set_sky_box = function()
|
||||
skycolor.add_layer(
|
||||
"weather-pack-rain-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=98},
|
||||
{r=152, g=150, b=159},
|
||||
{r=85, g=86, b=98},
|
||||
{r=0, g=0, b=0}})
|
||||
skycolor.active = true
|
||||
end
|
||||
|
||||
-- creating manually parctiles instead of particles spawner because of easier to control
|
||||
-- spawn position.
|
||||
rain.add_rain_particles = function(player)
|
||||
|
||||
rain.last_rp_count = 0
|
||||
for i=rain.particles_count, 1,-1 do
|
||||
local random_pos_x, random_pos_y, random_pos_z = weather.get_random_pos_by_player_look_dir(player)
|
||||
if minetest.get_node_light({x=random_pos_x, y=random_pos_y, z=random_pos_z}, 0.5) == 15 then
|
||||
rain.last_rp_count = rain.last_rp_count + 1
|
||||
minetest.add_particle({
|
||||
pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z},
|
||||
velocity = {x=0, y=-10, z=0},
|
||||
acceleration = {x=0, y=-30, z=0},
|
||||
expirationtime = 0.2,
|
||||
size = math.random(0.5, 3),
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = rain.get_texture(),
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Simple random texture getter
|
||||
rain.get_texture = function()
|
||||
local texture_name
|
||||
local random_number = math.random()
|
||||
if random_number > 0.33 then
|
||||
texture_name = "weather_pack_rain_raindrop_1.png"
|
||||
elseif random_number > 0.66 then
|
||||
texture_name = "weather_pack_rain_raindrop_2.png"
|
||||
else
|
||||
texture_name = "weather_pack_rain_raindrop_3.png"
|
||||
end
|
||||
return texture_name;
|
||||
end
|
||||
|
||||
-- register player for rain weather.
|
||||
-- basically needs for origin sky reference and rain sound controls.
|
||||
rain.add_player = function(player)
|
||||
if weather.players[player:get_player_name()] == nil then
|
||||
local player_meta = {}
|
||||
player_meta.origin_sky = {player:get_sky()}
|
||||
weather.players[player:get_player_name()] = player_meta
|
||||
end
|
||||
end
|
||||
|
||||
-- remove player from player list effected by rain.
|
||||
-- be sure to remove sound before removing player otherwise soundhandler reference will be lost.
|
||||
rain.remove_player = function(player)
|
||||
local player_meta = weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil and player_meta.origin_sky ~= nil then
|
||||
player:set_sky(player_meta.origin_sky[1], player_meta.origin_sky[2], player_meta.origin_sky[3])
|
||||
weather.players[player:get_player_name()] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- adds and removes rain sound depending how much rain particles around player currently exist.
|
||||
-- have few seconds delay before each check to avoid on/off sound too often
|
||||
-- when player stay on 'edge' where sound should play and stop depending from random raindrop appearance.
|
||||
rain.update_sound = function(player)
|
||||
local player_meta = weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil then
|
||||
if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > os.time() then
|
||||
return false
|
||||
end
|
||||
|
||||
if player_meta.sound_handler ~= nil then
|
||||
if rain.last_rp_count == 0 then
|
||||
minetest.sound_stop(player_meta.sound_handler)
|
||||
player_meta.sound_handler = nil
|
||||
end
|
||||
elseif rain.last_rp_count > 0 then
|
||||
player_meta.sound_handler = rain.sound_handler(player)
|
||||
end
|
||||
|
||||
player_meta.sound_updated = os.time()
|
||||
end
|
||||
end
|
||||
|
||||
-- rain sound removed from player.
|
||||
rain.remove_sound = function(player)
|
||||
local player_meta = weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil and player_meta.sound_handler ~= nil then
|
||||
minetest.sound_stop(player_meta.sound_handler)
|
||||
player_meta.sound_handler = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- callback function for removing rain
|
||||
rain.clear = function()
|
||||
rain.raining = false
|
||||
rain.sky_last_update = -1
|
||||
rain.init_done = false
|
||||
skycolor.remove_layer("weather-pack-rain-sky")
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
rain.remove_sound(player)
|
||||
rain.remove_player(player)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if weather.state ~= "rain" then
|
||||
return false
|
||||
end
|
||||
|
||||
rain.make_weather()
|
||||
end)
|
||||
|
||||
rain.make_weather = function()
|
||||
if rain.init_done == false then
|
||||
rain.raining = true
|
||||
rain.set_sky_box()
|
||||
end
|
||||
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
if (weather.is_underwater(player)) then
|
||||
return false
|
||||
end
|
||||
rain.add_player(player)
|
||||
rain.add_rain_particles(player)
|
||||
rain.update_sound(player)
|
||||
end
|
||||
end
|
||||
|
||||
if weather.reg_weathers.rain == nil then
|
||||
weather.reg_weathers.rain = {
|
||||
chance = 15,
|
||||
clear = rain.clear
|
||||
}
|
||||
end
|
||||
|
||||
-- ABM for extinguish fire
|
||||
if weather.allow_abm then
|
||||
minetest.register_abm({
|
||||
nodenames = {"fire:basic_flame"},
|
||||
interval = 4.0,
|
||||
chance = 2,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
if rain.raining and rain.extinguish_fire then
|
||||
if weather.is_outdoor(pos) then
|
||||
minetest.remove_node(pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,215 @@
|
|||
skycolor = {
|
||||
-- Should be activated before do any effect.
|
||||
active = false,
|
||||
|
||||
-- To skip update interval
|
||||
force_update = true,
|
||||
|
||||
-- Update interval.
|
||||
update_interval = 15,
|
||||
|
||||
-- Main sky colors: starts from midnight to midnight.
|
||||
-- Please do not set directly. Use add_layer instead.
|
||||
colors = {},
|
||||
|
||||
-- min value which will be used in color gradient, usualy its first user given color in 'pure' color.
|
||||
min_val = 0,
|
||||
|
||||
-- number of colors while constructing gradient of user given colors
|
||||
max_val = 1000,
|
||||
|
||||
-- Enables smooth transition between existing sky color and target.
|
||||
smooth_transitions = true,
|
||||
|
||||
-- Transition between current sky color and new user given.
|
||||
transition_in_progress = false,
|
||||
|
||||
-- Transition colors are generated automaticly during initialization.
|
||||
transition_colors = {},
|
||||
|
||||
-- Time where transition between current color and user given will be done
|
||||
transition_time = 15,
|
||||
|
||||
-- Tracks how much time passed during transition
|
||||
transition_timer = 0,
|
||||
|
||||
-- Table for tracking layer order
|
||||
layer_names = {},
|
||||
|
||||
-- To layer to colors table
|
||||
add_layer = function(layer_name, layer_color, instant_update)
|
||||
skycolor.colors[layer_name] = layer_color
|
||||
table.insert(skycolor.layer_names, layer_name)
|
||||
if (instant_update ~= true) then
|
||||
skycolor.init_transition()
|
||||
end
|
||||
skycolor.force_update = true
|
||||
end,
|
||||
|
||||
-- Retrieve layer from colors table
|
||||
retrieve_layer = function()
|
||||
local last_layer = skycolor.layer_names[#skycolor.layer_names]
|
||||
return skycolor.colors[last_layer]
|
||||
end,
|
||||
|
||||
-- Remove layer from colors table
|
||||
remove_layer = function(layer_name)
|
||||
for k, name in ipairs(skycolor.layer_names) do
|
||||
if name == layer_name then
|
||||
table.remove(skycolor.layer_names, k)
|
||||
skycolor.force_update = true
|
||||
return
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
-- Update sky color. If players not specified update sky for all players.
|
||||
update_sky_color = function(players)
|
||||
local color = skycolor.current_sky_layer_color()
|
||||
if (color == nil) then
|
||||
skycolor.active = false
|
||||
skycolor.set_default_sky()
|
||||
return
|
||||
end
|
||||
|
||||
players = skycolor.utils.get_players(players)
|
||||
for _, player in ipairs(players) do
|
||||
player:set_sky(color, "plain", nil)
|
||||
end
|
||||
end,
|
||||
|
||||
-- Returns current layer color in {r, g, b} format
|
||||
current_sky_layer_color = function()
|
||||
if #skycolor.layer_names == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- min timeofday value 0; max timeofday value 1. So sky color gradient range will be between 0 and 1 * skycolor.max_value.
|
||||
local timeofday = minetest.get_timeofday()
|
||||
local rounded_time = math.floor(timeofday * skycolor.max_val)
|
||||
local color = skycolor.utils.convert_to_rgb(skycolor.min_val, skycolor.max_val, rounded_time, skycolor.retrieve_layer())
|
||||
return color
|
||||
end,
|
||||
|
||||
-- Initialy used only on
|
||||
update_transition_sky_color = function()
|
||||
if #skycolor.layer_names == 0 then
|
||||
skycolor.active = false
|
||||
skycolor.set_default_sky()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local multiplier = 100
|
||||
local rounded_time = math.floor(skycolor.transition_timer * multiplier)
|
||||
if rounded_time >= skycolor.transition_time * multiplier then
|
||||
skycolor.stop_transition()
|
||||
return
|
||||
end
|
||||
|
||||
local color = skycolor.utils.convert_to_rgb(0, skycolor.transition_time * multiplier, rounded_time, skycolor.transition_colors)
|
||||
|
||||
local players = skycolor.utils.get_players(nil)
|
||||
for _, player in ipairs(players) do
|
||||
player:set_sky(color, "plain", nil)
|
||||
end
|
||||
end,
|
||||
|
||||
-- Reset sky color to game default. If players not specified update sky for all players.
|
||||
-- Could be sometimes useful but not recomended to use in general case as there may be other color layers
|
||||
-- which needs to preserve.
|
||||
set_default_sky = function(players)
|
||||
local players = skycolor.utils.get_players(players)
|
||||
for _, player in ipairs(players) do
|
||||
player:set_sky(nil, "regular", nil)
|
||||
end
|
||||
end,
|
||||
|
||||
init_transition = function()
|
||||
-- sadly default sky returns unpredictible colors so transition mode becomes usable only for user defined color layers
|
||||
-- Here '2' means that one color layer existed before new added and transition is posible.
|
||||
if #skycolor.layer_names < 2 then
|
||||
return
|
||||
end
|
||||
|
||||
local transition_start_color = skycolor.utils.get_current_bg_color()
|
||||
if (transition_start_color == nil) then
|
||||
return
|
||||
end
|
||||
local transition_end_color = skycolor.current_sky_layer_color()
|
||||
skycolor.transition_colors = {transition_start_color, transition_end_color}
|
||||
skycolor.transition_in_progress = true
|
||||
end,
|
||||
|
||||
stop_transition = function()
|
||||
skycolor.transition_in_progress = false
|
||||
skycolor.transition_colors = {}
|
||||
skycolor.transition_timer = 0
|
||||
end,
|
||||
|
||||
utils = {
|
||||
convert_to_rgb = function(minval, maxval, current_val, colors)
|
||||
local max_index = #colors - 1
|
||||
local val = (current_val-minval) / (maxval-minval) * max_index + 1.0
|
||||
local index1 = math.floor(val)
|
||||
local index2 = math.min(math.floor(val)+1, max_index + 1)
|
||||
local f = val - index1
|
||||
local c1 = colors[index1]
|
||||
local c2 = colors[index2]
|
||||
return {r=math.floor(c1.r + f*(c2.r - c1.r)), g=math.floor(c1.g + f*(c2.g-c1.g)), b=math.floor(c1.b + f*(c2.b - c1.b))}
|
||||
end,
|
||||
|
||||
-- Simply getter. Ether returns user given players list or get all connected players if none provided
|
||||
get_players = function(players)
|
||||
if players == nil or #players == 0 then
|
||||
players = minetest.get_connected_players()
|
||||
end
|
||||
return players
|
||||
end,
|
||||
|
||||
-- Returns first player sky color. I assume that all players are in same color layout.
|
||||
get_current_bg_color = function()
|
||||
local players = skycolor.utils.get_players(nil)
|
||||
for _, player in ipairs(players) do
|
||||
return player:get_sky()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
},
|
||||
|
||||
}
|
||||
--maikerumine turned this off to use skybox instead
|
||||
--[[
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if skycolor.active ~= true or #minetest.get_connected_players() == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if skycolor.smooth_transitions and skycolor.transition_in_progress then
|
||||
skycolor.transition_timer = skycolor.transition_timer + dtime
|
||||
skycolor.update_transition_sky_color()
|
||||
return
|
||||
end
|
||||
|
||||
if skycolor.force_update then
|
||||
skycolor.update_sky_color()
|
||||
skycolor.force_update = false
|
||||
return
|
||||
end
|
||||
|
||||
-- regular updates based on iterval
|
||||
timer = timer + dtime;
|
||||
if timer >= skycolor.update_interval then
|
||||
skycolor.update_sky_color()
|
||||
timer = 0
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if (skycolor.active) then
|
||||
skycolor.update_sky_color({player})
|
||||
end
|
||||
end)
|
||||
]]
|
|
@ -0,0 +1,90 @@
|
|||
snow = {}
|
||||
|
||||
snow.particles_count = 15
|
||||
snow.init_done = false
|
||||
|
||||
-- calculates coordinates and draw particles for snow weather
|
||||
snow.add_rain_particles = function(player)
|
||||
rain.last_rp_count = 0
|
||||
for i=snow.particles_count, 1,-1 do
|
||||
local random_pos_x, random_pos_y, random_pos_z = weather.get_random_pos_by_player_look_dir(player)
|
||||
random_pos_y = math.random() + math.random(player:getpos().y - 1, player:getpos().y + 7)
|
||||
if minetest.get_node_light({x=random_pos_x, y=random_pos_y, z=random_pos_z}, 0.5) == 15 then
|
||||
rain.last_rp_count = rain.last_rp_count + 1
|
||||
minetest.add_particle({
|
||||
pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z},
|
||||
velocity = {x = math.random(-1,-0.5), y = math.random(-2,-1), z = math.random(-1,-0.5)},
|
||||
acceleration = {x = math.random(-1,-0.5), y=-0.5, z = math.random(-1,-0.5)},
|
||||
expirationtime = 2.0,
|
||||
size = math.random(0.5, 2),
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = snow.get_texture(),
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
snow.set_sky_box = function()
|
||||
skycolor.add_layer(
|
||||
"weather-pack-snow-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=241, g=244, b=249},
|
||||
{r=0, g=0, b=0}}
|
||||
)
|
||||
skycolor.active = true
|
||||
end
|
||||
|
||||
snow.clear = function()
|
||||
skycolor.remove_layer("weather-pack-snow-sky")
|
||||
snow.init_done = false
|
||||
end
|
||||
|
||||
-- Simple random texture getter
|
||||
snow.get_texture = function()
|
||||
local texture_name
|
||||
local random_number = math.random()
|
||||
if random_number > 0.5 then
|
||||
texture_name = "weather_pack_snow_snowflake1.png"
|
||||
else
|
||||
texture_name = "weather_pack_snow_snowflake2.png"
|
||||
end
|
||||
return texture_name;
|
||||
end
|
||||
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if weather.state ~= "snow" then
|
||||
return false
|
||||
end
|
||||
|
||||
timer = timer + dtime;
|
||||
if timer >= 0.5 then
|
||||
timer = 0
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
if snow.init_done == false then
|
||||
snow.set_sky_box()
|
||||
snow.init_done = true
|
||||
end
|
||||
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
if (weather.is_underwater(player)) then
|
||||
return false
|
||||
end
|
||||
snow.add_rain_particles(player)
|
||||
end
|
||||
end)
|
||||
|
||||
-- register snow weather
|
||||
if weather.reg_weathers.snow == nil then
|
||||
weather.reg_weathers.snow = {
|
||||
chance = 10,
|
||||
clear = snow.clear
|
||||
}
|
||||
end
|
||||
|
After Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 209 B |
After Width: | Height: | Size: 220 B |
After Width: | Height: | Size: 192 B |
After Width: | Height: | Size: 195 B |
|
@ -0,0 +1,37 @@
|
|||
-- turn off lightning mod 'auto mode'
|
||||
lightning.auto = false
|
||||
|
||||
thunder = {
|
||||
next_strike = 0,
|
||||
min_delay = 3,
|
||||
max_delay = 12,
|
||||
}
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if weather.state ~= "thunder" then
|
||||
return false
|
||||
end
|
||||
|
||||
rain.make_weather()
|
||||
|
||||
if (thunder.next_strike <= os.time()) then
|
||||
lightning.strike()
|
||||
local delay = math.random(thunder.min_delay, thunder.max_delay)
|
||||
thunder.next_strike = os.time() + delay
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
thunder.clear = function()
|
||||
rain.clear()
|
||||
end
|
||||
|
||||
-- register thunderstorm weather
|
||||
if weather.reg_weathers.thunder == nil then
|
||||
weather.reg_weathers.thunder = {
|
||||
chance = 5,
|
||||
clear = thunder.clear,
|
||||
min_duration = 120,
|
||||
max_duration = 600,
|
||||
}
|
||||
end
|
|
@ -0,0 +1,175 @@
|
|||
weather = {
|
||||
-- weather states, 'none' is default, other states depends from active mods
|
||||
state = "none",
|
||||
|
||||
-- player list for saving player meta info
|
||||
players = {},
|
||||
|
||||
-- time when weather should be re-calculated
|
||||
next_check = 0,
|
||||
|
||||
-- default weather recalculation interval
|
||||
check_interval = 300,
|
||||
|
||||
-- weather min duration
|
||||
min_duration = 240,
|
||||
|
||||
-- weather max duration
|
||||
max_duration = 3600,
|
||||
|
||||
-- weather calculated end time
|
||||
end_time = nil,
|
||||
|
||||
-- registered weathers
|
||||
reg_weathers = {},
|
||||
|
||||
-- automaticly calculates intervals and swap weathers
|
||||
auto_mode = true,
|
||||
|
||||
-- global flag to disable/enable ABM logic.
|
||||
allow_abm = true,
|
||||
}
|
||||
|
||||
weather.get_rand_end_time = function(min_duration, max_duration)
|
||||
if min_duration ~= nil and max_duration ~= nil then
|
||||
return os.time() + math.random(min_duration, max_duration);
|
||||
else
|
||||
return os.time() + math.random(weather.min_duration, weather.max_duration);
|
||||
end
|
||||
end
|
||||
|
||||
weather.is_outdoor = function(pos)
|
||||
if minetest.get_node_light({x=pos.x, y=pos.y + 1, z=pos.z}, 0.5) == 15 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- checks if player is undewater. This is needed in order to
|
||||
-- turn off weather particles generation.
|
||||
weather.is_underwater = function(player)
|
||||
local ppos = player:getpos()
|
||||
local offset = player:get_eye_offset()
|
||||
local player_eye_pos = {x = ppos.x + offset.x,
|
||||
y = ppos.y + offset.y + 1.5,
|
||||
z = ppos.z + offset.z}
|
||||
local node_level = minetest.get_node_level(player_eye_pos)
|
||||
if node_level == 8 or node_level == 7 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- trying to locate position for particles by player look direction for performance reason.
|
||||
-- it is costly to generate many particles around player so goal is focus mainly on front view.
|
||||
weather.get_random_pos_by_player_look_dir = function(player)
|
||||
local look_dir = player:get_look_dir()
|
||||
local player_pos = player:getpos()
|
||||
|
||||
local random_pos_x = 0
|
||||
local random_pos_y = 0
|
||||
local random_pos_z = 0
|
||||
|
||||
if look_dir.x > 0 then
|
||||
if look_dir.z > 0 then
|
||||
random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5)
|
||||
random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5)
|
||||
else
|
||||
random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5)
|
||||
random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5)
|
||||
end
|
||||
else
|
||||
if look_dir.z > 0 then
|
||||
random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5)
|
||||
random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5)
|
||||
else
|
||||
random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5)
|
||||
random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5)
|
||||
end
|
||||
end
|
||||
|
||||
random_pos_y = math.random() + math.random(player_pos.y + 1, player_pos.y + 3)
|
||||
return random_pos_x, random_pos_y, random_pos_z
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if weather.auto_mode == false then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- recalculate weather only when there aren't currently any
|
||||
if (weather.state ~= "none") then
|
||||
if (weather.end_time ~= nil and weather.end_time <= os.time()) then
|
||||
weather.reg_weathers[weather.state].clear()
|
||||
weather.state = "none"
|
||||
end
|
||||
elseif (weather.next_check <= os.time()) then
|
||||
for weather_name, weather_meta in pairs(weather.reg_weathers) do
|
||||
weather.set_random_weather(weather_name, weather_meta)
|
||||
end
|
||||
-- fallback next_check set, weather 'none' will be.
|
||||
weather.next_check = os.time() + weather.check_interval
|
||||
end
|
||||
end)
|
||||
|
||||
-- sets random weather (which could be 'regular' (no weather)).
|
||||
weather.set_random_weather = function(weather_name, weather_meta)
|
||||
if weather.next_check > os.time() then return 0 end
|
||||
|
||||
if (weather_meta ~= nil and weather_meta.chance ~= nil) then
|
||||
local random_roll = math.random(0,100)
|
||||
if (random_roll <= weather_meta.chance) then
|
||||
weather.state = weather_name
|
||||
weather.end_time = weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration)
|
||||
weather.next_check = os.time() + weather.check_interval
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_privilege("weather_manager", {
|
||||
description = "Gives ability to control weather",
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
-- Weather command definition. Set
|
||||
minetest.register_chatcommand("set_weather", {
|
||||
params = "<weather>",
|
||||
description = "Changes weather by given param, parameter none will remove weather.",
|
||||
privs = {weather_manager = true},
|
||||
func = function(name, param)
|
||||
if (param == "none") then
|
||||
if (weather.state ~= nil and weather.reg_weathers[weather.state] ~= nil) then
|
||||
weather.reg_weathers[weather.state].clear()
|
||||
weather.state = param
|
||||
end
|
||||
weather.state = "none"
|
||||
end
|
||||
|
||||
if (weather.reg_weathers ~= nil and weather.reg_weathers[param] ~= nil) then
|
||||
if (weather.state ~= nil and weather.state ~= "none" and weather.reg_weathers[weather.state] ~= nil) then
|
||||
weather.reg_weathers[weather.state].clear()
|
||||
end
|
||||
weather.state = param
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- Configuration setting which allows user to disable ABM for weathers (if they use it).
|
||||
-- Weather mods expected to be use this flag before registering ABM.
|
||||
local weather_allow_abm = minetest.setting_getbool("weather_allow_abm")
|
||||
if weather_allow_abm ~= nil and weather_allow_abm == false then
|
||||
weather.allow_abm = false
|
||||
end
|
||||
|
||||
-- Overrides nodes 'sunlight_propagates' attribute for efficient indoor check (e.g. for glass roof).
|
||||
-- Controlled from minetest.conf setting and by default it is disabled.
|
||||
-- To enable set weather_allow_override_nodes to true.
|
||||
-- Only new nodes will be effected (glass roof needs to be rebuilded).
|
||||
if minetest.setting_getbool("weather_allow_override_nodes") then
|
||||
if minetest.registered_nodes["default:glass"] then
|
||||
minetest.override_item("default:glass", {sunlight_propagates = false})
|
||||
end
|
||||
if minetest.registered_nodes["default:meselamp"] then
|
||||
minetest.override_item("default:meselamp", {sunlight_propagates = false})
|
||||
end
|
||||
end
|
|
@ -24,6 +24,8 @@ minetest.register_alias("esmobs:nametag", "mobs:nametag")
|
|||
minetest.register_alias("esmobs:leather", "mobs:leather")
|
||||
minetest.register_alias("esmobs:meat_raw", "mobs:meat_raw")
|
||||
minetest.register_alias("esmobs:meat", "mobs:meat")
|
||||
minetest.register_alias("badplayer:meat", "mobs:meat")
|
||||
minetest.register_alias("badplayer:meat_raw", "mobs:meat")
|
||||
minetest.register_alias("esmobs:pork_raw", "mobs:pork_raw")
|
||||
minetest.register_alias("esmobs:pork_cooked", "mobs:pork_cooked")
|
||||
minetest.register_alias("esmobs:rat_cooked", "mobs:rat_cooked")
|
||||
|
@ -45,6 +47,7 @@ minetest.register_alias("esmobs:cobweb", "mobs:cobweb")
|
|||
|
||||
|
||||
minetest.register_alias("esmobs:spawner", "mobs:spawner")
|
||||
minetest.register_alias("mobs:spawner", "es:boneblock")
|
||||
minetest.register_alias("esmobs:bones", "mobs:bones")
|
||||
minetest.register_alias("esmobs:fireball", "mobs:fireball")
|
||||
minetest.register_alias("esmobs:arrow", "mobs:arrow")
|
||||
|
|
|
@ -13,8 +13,18 @@
|
|||
--(c) Copyright (2014-2015) maikerumine; CC-BY-SA 3.0
|
||||
|
||||
minetest.register_alias("stairs:slab_Dirt", "stairs:slab_dirt")
|
||||
minetest.register_alias("stairs:stair_Dirt", "stairs:stair_dirt")
|
||||
|
||||
minetest.register_alias("stairs:slab_Ruby", "stairs:slab_ruby")
|
||||
minetest.register_alias("stairs:stair_Ruby", "stairs:stair_ruby")
|
||||
|
||||
|
||||
minetest.register_alias("es:stair_dirt_with_grass", "stairs:stair_dirt")
|
||||
|
||||
minetest.register_alias("stairs:stair_granite", "stairs:stair_granite")
|
||||
minetest.register_alias("stairs:stair_granitebricks", "stairs:stair_granite_bricks")
|
||||
minetest.register_alias("stairs:stair_marble", "stairs:stair_marble")
|
||||
minetest.register_alias("stairs:stair_marblebricks", "stairs:stair_marble_bricks")
|
||||
|
||||
es = {}
|
||||
--need micro""_1
|
||||
|
|
|
@ -274,9 +274,10 @@ minetest.register_alias("technic:solar_panel", "es:aikerumblock")
|
|||
minetest.register_alias("technic:marble", "es:marble")
|
||||
minetest.register_alias("technic:granite", "es:granite")
|
||||
minetest.register_alias("technic:marble_bricks", "es:marble_bricks")
|
||||
--minetest.register_alias("technic:mineral_uranium", "es:depleted_uranium")
|
||||
--minetest.register_alias("technic:mineral_chromium", "es:stone_with_aikerum")
|
||||
--minetest.register_alias("technic:mineral_zinc", "es:stone_with_emerald")
|
||||
minetest.register_alias("technic:granite_bricks", "es:granite_bricks")
|
||||
minetest.register_alias("technic:mineral_uranium", "es:depleted_uraniums")
|
||||
minetest.register_alias("technic:mineral_chromium", "es:stone_with_aikerums")
|
||||
minetest.register_alias("technic:mineral_zinc", "es:stone_with_emeralds")
|
||||
minetest.register_alias("technic:mineral_uranium", "es:depleted_coal")
|
||||
minetest.register_alias("technic:mineral_chromium", "es:stone_with_coal")
|
||||
minetest.register_alias("technic:mineral_zinc", "es:stone_with_coal")
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Carts (formerly boost_cart)
|
||||
==========================
|
||||
|
||||
Carts, based almost entirely on the mod boost_cart [1], which
|
||||
itself is based on (and fully compatible with) the carts mod [2].
|
||||
|
||||
The model was originally designed by stujones11 [3] (CC-0).
|
||||
|
||||
Cart textures are based on original work from PixelBOX (WTFPL).
|
||||
|
||||
|
||||
[1] https://github.com/SmallJoker/boost_cart/
|
||||
[2] https://github.com/PilzAdam/carts/
|
||||
[3] https://github.com/stujones11/railcart/
|
||||
|
||||
|
||||
Features
|
||||
----------
|
||||
- A fast cart for your railway or roller coaster (up to 7 m/s!)
|
||||
- Boost and brake rails
|
||||
- Rail junction switching with the 'right-left' walking keys
|
||||
- Handbrake with the 'back' key
|
|
@ -0,0 +1,403 @@
|
|||
local cart_entity = {
|
||||
physical = false, -- otherwise going uphill breaks
|
||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "carts_cart.b3d",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {"carts_cart.png"},
|
||||
|
||||
driver = nil,
|
||||
punched = false, -- used to re-send velocity and position
|
||||
velocity = {x=0, y=0, z=0}, -- only used on punch
|
||||
old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch
|
||||
old_pos = nil,
|
||||
old_switch = 0,
|
||||
railtype = nil,
|
||||
attached_items = {}
|
||||
}
|
||||
|
||||
function cart_entity:on_rightclick(clicker)
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
end
|
||||
local player_name = clicker:get_player_name()
|
||||
if self.driver and player_name == self.driver then
|
||||
self.driver = nil
|
||||
carts:manage_attachment(clicker, nil)
|
||||
elseif not self.driver then
|
||||
self.driver = player_name
|
||||
carts:manage_attachment(clicker, self.object)
|
||||
end
|
||||
end
|
||||
|
||||
function cart_entity:on_activate(staticdata, dtime_s)
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
if string.sub(staticdata, 1, string.len("return")) ~= "return" then
|
||||
return
|
||||
end
|
||||
local data = minetest.deserialize(staticdata)
|
||||
if not data or type(data) ~= "table" then
|
||||
return
|
||||
end
|
||||
self.railtype = data.railtype
|
||||
if data.old_dir then
|
||||
self.old_dir = data.old_dir
|
||||
end
|
||||
if data.old_vel then
|
||||
self.old_vel = data.old_vel
|
||||
end
|
||||
end
|
||||
|
||||
function cart_entity:get_staticdata()
|
||||
return minetest.serialize({
|
||||
railtype = self.railtype,
|
||||
old_dir = self.old_dir,
|
||||
old_vel = self.old_vel
|
||||
})
|
||||
end
|
||||
|
||||
function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||
local pos = self.object:getpos()
|
||||
if not self.railtype then
|
||||
local node = minetest.get_node(pos).name
|
||||
self.railtype = minetest.get_item_group(node, "connect_to_raillike")
|
||||
end
|
||||
-- Punched by non-player
|
||||
if not puncher or not puncher:is_player() then
|
||||
local cart_dir = carts:get_rail_direction(pos, self.old_dir, nil, nil, self.railtype)
|
||||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
|
||||
return
|
||||
end
|
||||
self.velocity = vector.multiply(cart_dir, 2)
|
||||
self.punched = true
|
||||
return
|
||||
end
|
||||
-- Player digs cart by sneak-punch
|
||||
if puncher:get_player_control().sneak then
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
end
|
||||
-- Detach driver and items
|
||||
if self.driver then
|
||||
if self.old_pos then
|
||||
self.object:setpos(self.old_pos)
|
||||
end
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
carts:manage_attachment(player, nil)
|
||||
end
|
||||
for _,obj_ in ipairs(self.attached_items) do
|
||||
if obj_ then
|
||||
obj_:set_detach()
|
||||
end
|
||||
end
|
||||
-- Pick up cart
|
||||
local inv = puncher:get_inventory()
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(puncher:get_player_name()))
|
||||
or not inv:contains_item("main", "carts:cart") then
|
||||
local leftover = inv:add_item("main", "carts:cart")
|
||||
-- If no room in inventory add a replacement cart to the world
|
||||
if not leftover:is_empty() then
|
||||
minetest.add_item(self.object:getpos(), leftover)
|
||||
end
|
||||
end
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
-- Player punches cart to alter velocity
|
||||
local vel = self.object:getvelocity()
|
||||
if puncher:get_player_name() == self.driver then
|
||||
if math.abs(vel.x + vel.z) > carts.punch_speed_max then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local punch_dir = carts:velocity_to_dir(puncher:get_look_dir())
|
||||
punch_dir.y = 0
|
||||
local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype)
|
||||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
|
||||
return
|
||||
end
|
||||
|
||||
local punch_interval = 1
|
||||
if tool_capabilities and tool_capabilities.full_punch_interval then
|
||||
punch_interval = tool_capabilities.full_punch_interval
|
||||
end
|
||||
time_from_last_punch = math.min(time_from_last_punch or punch_interval, punch_interval)
|
||||
local f = 2 * (time_from_last_punch / punch_interval)
|
||||
|
||||
self.velocity = vector.multiply(cart_dir, f)
|
||||
self.old_dir = cart_dir
|
||||
self.punched = true
|
||||
end
|
||||
|
||||
local function rail_on_step_event(handler, obj, dtime)
|
||||
if handler then
|
||||
handler(obj, dtime)
|
||||
end
|
||||
end
|
||||
|
||||
-- sound refresh interval = 1.0sec
|
||||
local function rail_sound(self, dtime)
|
||||
if not self.sound_ttl then
|
||||
self.sound_ttl = 1.0
|
||||
return
|
||||
elseif self.sound_ttl > 0 then
|
||||
self.sound_ttl = self.sound_ttl - dtime
|
||||
return
|
||||
end
|
||||
self.sound_ttl = 1.0
|
||||
if self.sound_handle then
|
||||
local handle = self.sound_handle
|
||||
self.sound_handle = nil
|
||||
minetest.after(0.2, minetest.sound_stop, handle)
|
||||
end
|
||||
local vel = self.object:getvelocity()
|
||||
local speed = vector.length(vel)
|
||||
if speed > 0 then
|
||||
self.sound_handle = minetest.sound_play(
|
||||
"carts_cart_moving", {
|
||||
object = self.object,
|
||||
gain = (speed / carts.speed_max) / 2,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function get_railparams(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
return carts.railparams[node.name] or {}
|
||||
end
|
||||
|
||||
local function rail_on_step(self, dtime)
|
||||
local vel = self.object:getvelocity()
|
||||
if self.punched then
|
||||
vel = vector.add(vel, self.velocity)
|
||||
self.object:setvelocity(vel)
|
||||
self.old_dir.y = 0
|
||||
elseif vector.equals(vel, {x=0, y=0, z=0}) then
|
||||
return
|
||||
end
|
||||
|
||||
local pos = self.object:getpos()
|
||||
local update = {}
|
||||
|
||||
-- stop cart if velocity vector flips
|
||||
if self.old_vel and self.old_vel.y == 0 and
|
||||
(self.old_vel.x * vel.x < 0 or self.old_vel.z * vel.z < 0) then
|
||||
self.old_vel = {x = 0, y = 0, z = 0}
|
||||
self.old_pos = pos
|
||||
self.object:setvelocity(vector.new())
|
||||
self.object:setacceleration(vector.new())
|
||||
rail_on_step_event(get_railparams(pos).on_step, self, dtime)
|
||||
return
|
||||
end
|
||||
self.old_vel = vector.new(vel)
|
||||
|
||||
if self.old_pos and not self.punched then
|
||||
local flo_pos = vector.round(pos)
|
||||
local flo_old = vector.round(self.old_pos)
|
||||
if vector.equals(flo_pos, flo_old) then
|
||||
-- Do not check one node multiple times
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local ctrl, player
|
||||
|
||||
-- Get player controls
|
||||
if self.driver then
|
||||
player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
ctrl = player:get_player_control()
|
||||
end
|
||||
end
|
||||
|
||||
if self.old_pos then
|
||||
-- Detection for "skipping" nodes
|
||||
local found_path = carts:pathfinder(
|
||||
pos, self.old_pos, self.old_dir, ctrl, self.old_switch, self.railtype
|
||||
)
|
||||
|
||||
if not found_path then
|
||||
-- No rail found: reset back to the expected position
|
||||
pos = vector.new(self.old_pos)
|
||||
update.pos = true
|
||||
end
|
||||
end
|
||||
|
||||
local cart_dir = carts:velocity_to_dir(vel)
|
||||
local railparams
|
||||
|
||||
-- dir: New moving direction of the cart
|
||||
-- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node
|
||||
local dir, switch_keys = carts:get_rail_direction(
|
||||
pos, cart_dir, ctrl, self.old_switch, self.railtype
|
||||
)
|
||||
|
||||
local new_acc = {x=0, y=0, z=0}
|
||||
if vector.equals(dir, {x=0, y=0, z=0}) then
|
||||
vel = {x = 0, y = 0, z = 0}
|
||||
pos = vector.round(pos)
|
||||
update.pos = true
|
||||
update.vel = true
|
||||
else
|
||||
-- Direction change detected
|
||||
if not vector.equals(dir, self.old_dir) then
|
||||
vel = vector.multiply(dir, math.abs(vel.x + vel.z))
|
||||
update.vel = true
|
||||
if dir.y ~= self.old_dir.y then
|
||||
pos = vector.round(pos)
|
||||
update.pos = true
|
||||
end
|
||||
end
|
||||
-- Center on the rail
|
||||
if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then
|
||||
pos.x = math.floor(pos.x + 0.5)
|
||||
update.pos = true
|
||||
end
|
||||
if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then
|
||||
pos.z = math.floor(pos.z + 0.5)
|
||||
update.pos = true
|
||||
end
|
||||
|
||||
-- Slow down or speed up..
|
||||
local acc = dir.y * -4.0
|
||||
|
||||
-- Get rail for corrected position
|
||||
railparams = get_railparams(pos)
|
||||
|
||||
-- no need to check for railparams == nil since we always make it exist.
|
||||
local speed_mod = railparams.acceleration
|
||||
if speed_mod and speed_mod ~= 0 then
|
||||
-- Try to make it similar to the original carts mod
|
||||
acc = acc + speed_mod
|
||||
else
|
||||
-- Handbrake or coast
|
||||
if ctrl and ctrl.down then
|
||||
acc = acc - 3
|
||||
else
|
||||
acc = acc - 0.4
|
||||
end
|
||||
end
|
||||
|
||||
new_acc = vector.multiply(dir, acc)
|
||||
end
|
||||
|
||||
-- Limits
|
||||
local max_vel = carts.speed_max
|
||||
for _, v in pairs({"x","y","z"}) do
|
||||
if math.abs(vel[v]) > max_vel then
|
||||
vel[v] = carts:get_sign(vel[v]) * max_vel
|
||||
new_acc[v] = 0
|
||||
update.vel = true
|
||||
end
|
||||
end
|
||||
|
||||
self.object:setacceleration(new_acc)
|
||||
self.old_pos = vector.new(pos)
|
||||
if not vector.equals(dir, {x=0, y=0, z=0}) then
|
||||
self.old_dir = vector.new(dir)
|
||||
end
|
||||
self.old_switch = switch_keys
|
||||
|
||||
if self.punched then
|
||||
-- Collect dropped items
|
||||
for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
if not obj_:is_player() and
|
||||
obj_:get_luaentity() and
|
||||
not obj_:get_luaentity().physical_state and
|
||||
obj_:get_luaentity().name == "__builtin:item" then
|
||||
|
||||
obj_:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
|
||||
self.attached_items[#self.attached_items + 1] = obj_
|
||||
end
|
||||
end
|
||||
self.punched = false
|
||||
update.vel = true
|
||||
end
|
||||
|
||||
railparams = railparams or get_railparams(pos)
|
||||
|
||||
if not (update.vel or update.pos) then
|
||||
rail_on_step_event(railparams.on_step, self, dtime)
|
||||
return
|
||||
end
|
||||
|
||||
local yaw = 0
|
||||
if self.old_dir.x < 0 then
|
||||
yaw = 0.5
|
||||
elseif self.old_dir.x > 0 then
|
||||
yaw = 1.5
|
||||
elseif self.old_dir.z < 0 then
|
||||
yaw = 1
|
||||
end
|
||||
self.object:setyaw(yaw * math.pi)
|
||||
|
||||
local anim = {x=0, y=0}
|
||||
if dir.y == -1 then
|
||||
anim = {x=1, y=1}
|
||||
elseif dir.y == 1 then
|
||||
anim = {x=2, y=2}
|
||||
end
|
||||
self.object:set_animation(anim, 1, 0)
|
||||
|
||||
self.object:setvelocity(vel)
|
||||
if update.pos then
|
||||
self.object:setpos(pos)
|
||||
end
|
||||
|
||||
-- call event handler
|
||||
rail_on_step_event(railparams.on_step, self, dtime)
|
||||
end
|
||||
|
||||
function cart_entity:on_step(dtime)
|
||||
rail_on_step(self, dtime)
|
||||
rail_sound(self, dtime)
|
||||
end
|
||||
|
||||
minetest.register_entity("carts:cart", cart_entity)
|
||||
|
||||
minetest.register_craftitem("carts:cart", {
|
||||
description = "Cart (Sneak+Click to pick up)",
|
||||
inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png", "carts_cart_side.png"),
|
||||
wield_image = "carts_cart_side.png",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local under = pointed_thing.under
|
||||
local node = minetest.get_node(under)
|
||||
local udef = minetest.registered_nodes[node.name]
|
||||
if udef and udef.on_rightclick and
|
||||
not (placer and placer:get_player_control().sneak) then
|
||||
return udef.on_rightclick(under, node, placer, itemstack,
|
||||
pointed_thing) or itemstack
|
||||
end
|
||||
|
||||
if not pointed_thing.type == "node" then
|
||||
return
|
||||
end
|
||||
if carts:is_rail(pointed_thing.under) then
|
||||
minetest.add_entity(pointed_thing.under, "carts:cart")
|
||||
elseif carts:is_rail(pointed_thing.above) then
|
||||
minetest.add_entity(pointed_thing.above, "carts:cart")
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
minetest.sound_play({name = "default_place_node_metal", gain = 0.5},
|
||||
{pos = pointed_thing.above})
|
||||
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(placer:get_player_name())) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "carts:cart",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
function boost_cart:get_sign(z)
|
||||
|
||||
function carts:get_sign(z)
|
||||
if z == 0 then
|
||||
return 0
|
||||
else
|
||||
|
@ -8,44 +6,37 @@ function boost_cart:get_sign(z)
|
|||
end
|
||||
end
|
||||
|
||||
function boost_cart:manage_attachment(player, status, obj)
|
||||
|
||||
function carts:manage_attachment(player, obj)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
local status = obj ~= nil
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
if default.player_attached[player_name] == status then
|
||||
return
|
||||
end
|
||||
|
||||
default.player_attached[player_name] = status
|
||||
|
||||
if status then
|
||||
player:set_attach(obj, "", {x=0, y=6, z=0}, {x=0, y=0, z=0})
|
||||
player:set_eye_offset({x=0, y=-2, z=0},{x=0, y=-2, z=0})
|
||||
player:set_eye_offset({x=0, y=-4, z=0},{x=0, y=-4, z=0})
|
||||
else
|
||||
player:set_detach()
|
||||
player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
|
||||
end
|
||||
end
|
||||
|
||||
function boost_cart:velocity_to_dir(v)
|
||||
|
||||
function carts:velocity_to_dir(v)
|
||||
if math.abs(v.x) > math.abs(v.z) then
|
||||
return {x=boost_cart:get_sign(v.x), y=boost_cart:get_sign(v.y), z=0}
|
||||
return {x=carts:get_sign(v.x), y=carts:get_sign(v.y), z=0}
|
||||
else
|
||||
return {x=0, y=boost_cart:get_sign(v.y), z=boost_cart:get_sign(v.z)}
|
||||
return {x=0, y=carts:get_sign(v.y), z=carts:get_sign(v.z)}
|
||||
end
|
||||
end
|
||||
|
||||
function boost_cart:is_rail(pos, railtype)
|
||||
|
||||
function carts:is_rail(pos, railtype)
|
||||
local node = minetest.get_node(pos).name
|
||||
|
||||
if node == "ignore" then
|
||||
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos, pos)
|
||||
local area = VoxelArea:new{
|
||||
|
@ -56,63 +47,50 @@ function boost_cart:is_rail(pos, railtype)
|
|||
local vi = area:indexp(pos)
|
||||
node = minetest.get_name_from_content_id(data[vi])
|
||||
end
|
||||
|
||||
if minetest.get_item_group(node, "rail") == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
if not railtype then
|
||||
return true
|
||||
end
|
||||
|
||||
return minetest.get_item_group(node, "connect_to_raillike") == railtype
|
||||
end
|
||||
|
||||
function boost_cart:check_front_up_down(pos, dir_, check_down, railtype)
|
||||
|
||||
function carts:check_front_up_down(pos, dir_, check_up, railtype)
|
||||
local dir = vector.new(dir_)
|
||||
local cur = nil
|
||||
local cur
|
||||
|
||||
-- Front
|
||||
dir.y = 0
|
||||
cur = vector.add(pos, dir)
|
||||
|
||||
if boost_cart:is_rail(cur, railtype) then
|
||||
if carts:is_rail(cur, railtype) then
|
||||
return dir
|
||||
end
|
||||
|
||||
-- Up
|
||||
if check_down then
|
||||
|
||||
if check_up then
|
||||
dir.y = 1
|
||||
cur = vector.add(pos, dir)
|
||||
|
||||
if boost_cart:is_rail(cur, railtype) then
|
||||
if carts:is_rail(cur, railtype) then
|
||||
return dir
|
||||
end
|
||||
end
|
||||
|
||||
-- Down
|
||||
dir.y = -1
|
||||
cur = vector.add(pos, dir)
|
||||
|
||||
if boost_cart:is_rail(cur, railtype) then
|
||||
if carts:is_rail(cur, railtype) then
|
||||
return dir
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function boost_cart:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||
|
||||
function carts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||
local pos = vector.round(pos_)
|
||||
local cur = nil
|
||||
local cur
|
||||
local left_check, right_check = true, true
|
||||
|
||||
|
||||
-- Check left and right
|
||||
local left = {x=0, y=0, z=0}
|
||||
local right = {x=0, y=0, z=0}
|
||||
|
||||
if dir.z ~= 0 and dir.x == 0 then
|
||||
left.x = -dir.z
|
||||
right.x = dir.z
|
||||
|
@ -120,146 +98,124 @@ function boost_cart:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
|||
left.z = dir.x
|
||||
right.z = -dir.x
|
||||
end
|
||||
|
||||
if ctrl then
|
||||
|
||||
if ctrl then
|
||||
if old_switch == 1 then
|
||||
left_check = false
|
||||
elseif old_switch == 2 then
|
||||
right_check = false
|
||||
end
|
||||
|
||||
if ctrl.left and left_check then
|
||||
|
||||
cur = boost_cart:check_front_up_down(pos, left, false, railtype)
|
||||
|
||||
cur = carts:check_front_up_down(pos, left, false, railtype)
|
||||
if cur then
|
||||
return cur, 1
|
||||
end
|
||||
|
||||
left_check = false
|
||||
end
|
||||
|
||||
if ctrl.right and right_check then
|
||||
|
||||
cur = boost_cart:check_front_up_down(pos, right, false, railtype)
|
||||
|
||||
cur = carts:check_front_up_down(pos, right, false, railtype)
|
||||
if cur then
|
||||
return cur, 2
|
||||
end
|
||||
|
||||
right_check = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Normal
|
||||
cur = boost_cart:check_front_up_down(pos, dir, true, railtype)
|
||||
|
||||
-- Normal
|
||||
cur = carts:check_front_up_down(pos, dir, true, railtype)
|
||||
if cur then
|
||||
return cur
|
||||
end
|
||||
|
||||
|
||||
-- Left, if not already checked
|
||||
if left_check then
|
||||
|
||||
cur = boost_cart:check_front_up_down(pos, left, false, railtype)
|
||||
|
||||
cur = carts:check_front_up_down(pos, left, false, railtype)
|
||||
if cur then
|
||||
return cur
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Right, if not already checked
|
||||
if right_check then
|
||||
|
||||
cur = boost_cart:check_front_up_down(pos, right, false, railtype)
|
||||
|
||||
cur = carts:check_front_up_down(pos, right, false, railtype)
|
||||
if cur then
|
||||
return cur
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Backwards
|
||||
if not old_switch then
|
||||
|
||||
cur = boost_cart:check_front_up_down(pos, {
|
||||
x = -dir.x,
|
||||
y = dir.y,
|
||||
z = -dir.z
|
||||
}, true, railtype)
|
||||
|
||||
cur = carts:check_front_up_down(pos, {
|
||||
x = -dir.x,
|
||||
y = dir.y,
|
||||
z = -dir.z
|
||||
}, true, railtype)
|
||||
if cur then
|
||||
return cur
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
function boost_cart:pathfinder(pos_, expected_pos, old_dir, ctrl, pf_switch, railtype)
|
||||
|
||||
function carts:pathfinder(pos_, old_pos, old_dir, ctrl, pf_switch, railtype)
|
||||
local pos = vector.round(pos_)
|
||||
local pf_pos = vector.round(expected_pos)
|
||||
local pf_pos = vector.round(old_pos)
|
||||
local pf_dir = vector.new(old_dir)
|
||||
|
||||
for i = 1, 3 do
|
||||
|
||||
if vector.equals(pf_pos, pos) then
|
||||
|
||||
return true -- success! cart moved on correctly
|
||||
-- Success! Cart moved on correctly
|
||||
return true
|
||||
end
|
||||
|
||||
pf_dir, pf_switch = boost_cart:get_rail_direction(pf_pos, pf_dir,
|
||||
ctrl, pf_switch, railtype)
|
||||
|
||||
if vector.equals(pf_dir, {x = 0, y = 0, z = 0}) then
|
||||
|
||||
return false -- no way forwards
|
||||
pf_dir, pf_switch = carts:get_rail_direction(pf_pos, pf_dir, ctrl, pf_switch, railtype)
|
||||
if vector.equals(pf_dir, {x=0, y=0, z=0}) then
|
||||
-- No way forwards
|
||||
return false
|
||||
end
|
||||
|
||||
pf_pos = vector.add(pf_pos, pf_dir)
|
||||
|
||||
end
|
||||
|
||||
return false -- cart not found
|
||||
-- Cart not found
|
||||
return false
|
||||
end
|
||||
|
||||
function boost_cart:boost_rail(pos, amount)
|
||||
|
||||
minetest.get_meta(pos):set_string("cart_acceleration", tostring(amount))
|
||||
|
||||
for _,obj_ in pairs(minetest.get_objects_inside_radius(pos, 0.5)) do
|
||||
|
||||
if not obj_:is_player()
|
||||
and obj_:get_luaentity()
|
||||
and obj_:get_luaentity().name == "carts:cart" then
|
||||
obj_:get_luaentity():on_punch()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function boost_cart:register_rail(name, def)
|
||||
|
||||
local def_default = {
|
||||
function carts:register_rail(name, def_overwrite, railparams)
|
||||
local def = {
|
||||
drawtype = "raillike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = true,
|
||||
is_ground_content = false,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
||||
}
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults()
|
||||
}
|
||||
|
||||
for k, v in pairs(def_default) do
|
||||
for k, v in pairs(def_overwrite) do
|
||||
def[k] = v
|
||||
end
|
||||
|
||||
if not def.inventory_image then
|
||||
def.wield_image = def.tiles[1]
|
||||
def.inventory_image = def.tiles[1]
|
||||
end
|
||||
|
||||
if railparams then
|
||||
carts.railparams[name] = table.copy(railparams)
|
||||
end
|
||||
|
||||
minetest.register_node(name, def)
|
||||
end
|
||||
|
||||
function carts:get_rail_groups(additional_groups)
|
||||
-- Get the default rail groups and add more when a table is given
|
||||
local groups = {dig_immediate = 2, attached_node = 1, rail = 1, connect_to_raillike = 1}
|
||||
if type(additional_groups) == "table" then
|
||||
for k, v in pairs(additional_groups) do
|
||||
groups[k] = v
|
||||
end
|
||||
end
|
||||
return groups
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
carts = {}
|
||||
carts.modpath = minetest.get_modpath("carts")
|
||||
carts.railparams = {}
|
||||
|
||||
-- Maximal speed of the cart in m/s (min = -1)
|
||||
carts.speed_max = 7
|
||||
-- Set to -1 to disable punching the cart from inside (min = -1)
|
||||
carts.punch_speed_max = 5
|
||||
|
||||
|
||||
dofile(carts.modpath.."/functions.lua")
|
||||
dofile(carts.modpath.."/rails.lua")
|
||||
|
||||
-- Support for non-default games
|
||||
if not default.player_attached then
|
||||
default.player_attached = {}
|
||||
end
|
||||
|
||||
dofile(carts.modpath.."/cart_entity.lua")
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
License of source code
|
||||
----------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (C) 2012-2016 PilzAdam
|
||||
Copyright (C) 2014-2016 SmallJoker
|
||||
Copyright (C) 2012-2016 Various Minetest developers and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
For more details:
|
||||
https://opensource.org/licenses/MIT
|
||||
|
||||
|
||||
Licenses of media
|
||||
-----------------
|
||||
|
||||
CC-0, see: https://creativecommons.org/share-your-work/public-domain/cc0/, except
|
||||
if other license is mentioned.
|
||||
|
||||
|
||||
Authors
|
||||
---------
|
||||
Originally from PixelBOX (Gambit):
|
||||
carts_cart_side.png
|
||||
carts_cart_top.png
|
||||
carts_cart_front.png*
|
||||
carts_cart.png*
|
||||
|
||||
sofar + stujones11:
|
||||
carts_cart.b3d and carts_cart.blend
|
||||
|
||||
hexafraction, modified by sofar
|
||||
carts_rail_*.png
|
||||
|
||||
http://www.freesound.org/people/YleArkisto/sounds/253159/ - YleArkisto - CC-BY-3.0
|
||||
carts_cart_moving.*.ogg
|
|
@ -0,0 +1,59 @@
|
|||
carts:register_rail("carts:rail", {
|
||||
description = "Rail",
|
||||
tiles = {
|
||||
"carts_rail_straight.png", "carts_rail_curved.png",
|
||||
"carts_rail_t_junction.png", "carts_rail_crossing.png"
|
||||
},
|
||||
inventory_image = "carts_rail_straight.png",
|
||||
wield_image = "carts_rail_straight.png",
|
||||
groups = carts:get_rail_groups(),
|
||||
}, {})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "carts:rail 18",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_alias("default:rail", "carts:rail")
|
||||
|
||||
|
||||
carts:register_rail("carts:powerrail", {
|
||||
description = "Powered rail",
|
||||
tiles = {
|
||||
"carts_rail_straight_pwr.png", "carts_rail_curved_pwr.png",
|
||||
"carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"
|
||||
},
|
||||
groups = carts:get_rail_groups(),
|
||||
}, {acceleration = 5})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "carts:powerrail 18",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
carts:register_rail("carts:brakerail", {
|
||||
description = "Brake rail",
|
||||
tiles = {
|
||||
"carts_rail_straight_brk.png", "carts_rail_curved_brk.png",
|
||||
"carts_rail_t_junction_brk.png", "carts_rail_crossing_brk.png"
|
||||
},
|
||||
groups = carts:get_rail_groups(),
|
||||
}, {acceleration = -3})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "carts:brakerail 18",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "group:wood", "default:steel_ingot"},
|
||||
}
|
||||
})
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 486 B |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 612 B |
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 618 B |
After Width: | Height: | Size: 614 B |
After Width: | Height: | Size: 602 B |
After Width: | Height: | Size: 660 B |
After Width: | Height: | Size: 661 B |
After Width: | Height: | Size: 707 B |
After Width: | Height: | Size: 698 B |
After Width: | Height: | Size: 697 B |
After Width: | Height: | Size: 275 B |
After Width: | Height: | Size: 587 B |
After Width: | Height: | Size: 630 B |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 211 B |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 198 B |
After Width: | Height: | Size: 166 B |
|
@ -27,41 +27,41 @@
|
|||
"black.png",
|
||||
}
|
||||
|
||||
local time = 0
|
||||
local time = 0
|
||||
--minetest.after(1, function()
|
||||
minetest.register_globalstep(function(dtime)
|
||||
time = time + dtime
|
||||
if time > 0 then for _, player in ipairs(minetest.get_connected_players()) do
|
||||
time = 0
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
time = time + dtime
|
||||
if time > 5 then for _, player in ipairs(minetest.get_connected_players()) do
|
||||
time = 0
|
||||
local name = player:get_player_name()
|
||||
local pos = player:getpos()
|
||||
|
||||
--If the player has reached Space
|
||||
if minetest.get_player_by_name(name) and pos.y >= space then
|
||||
player:set_physics_override(1, 0.6, 0.2,false,false) -- speed, jump, gravity
|
||||
player:set_sky({}, "skybox", spaceskybox) -- Sets skybox
|
||||
|
||||
local name = player:get_player_name()
|
||||
local pos = player:getpos()
|
||||
--If the player is on Earth
|
||||
elseif minetest.get_player_by_name(name) and pos.y < space then
|
||||
player:set_physics_override(1, 1, 1,true,true) -- speed, jump, gravity [default]
|
||||
player:set_sky({}, "regular", {}) -- Sets skybox, in this case it sets the skybox to it's default setting if and only if the player's Y value is less than the value of space.
|
||||
|
||||
--If the player has reached Space
|
||||
if minetest.get_player_by_name(name) and pos.y >= space then
|
||||
player:set_physics_override(1, 0.6, 0.2,false,false) -- speed, jump, gravity
|
||||
player:set_sky({}, "skybox", spaceskybox) -- Sets skybox
|
||||
--If the player has reached Cave
|
||||
if minetest.get_player_by_name(name) and pos.y <=cave then
|
||||
player:set_physics_override(1, 1, 1.2,true,true) -- speed, jump, gravity
|
||||
player:set_sky({}, "cavebox", caveskybox) -- Sets skybox
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
--end)
|
||||
|
||||
--If the player is on Earth
|
||||
elseif minetest.get_player_by_name(name) and pos.y < space then
|
||||
player:set_physics_override(1, 1, 1,true,true) -- speed, jump, gravity [default]
|
||||
player:set_sky({}, "regular", {}) -- Sets skybox, in this case it sets the skybox to it's default setting if and only if the player's Y value is less than the value of space.
|
||||
|
||||
--If the player has reached Cave
|
||||
if minetest.get_player_by_name(name) and pos.y <=cave then
|
||||
player:set_physics_override(1, 1, 1.2,true,true) -- speed, jump, gravity
|
||||
player:set_sky({}, "cavebox", caveskybox) -- Sets skybox
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
|
||||
if name then
|
||||
player:set_sky({}, "regular", {})
|
||||
|
||||
end
|
||||
end)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
if name then
|
||||
player:set_sky({}, "regular", {})
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -24,6 +24,7 @@ es.modpath = modpath
|
|||
dofile(modpath.."/crafting.lua")
|
||||
dofile(modpath.."/antigrief.lua")
|
||||
dofile(modpath.."/armor.lua")
|
||||
dofile(modpath.."/kill.lua")
|
||||
dofile(modpath.."/shields.lua")
|
||||
dofile(modpath.."/shutdown.lua")
|
||||
dofile(modpath.."/spawn.lua")
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
minetest.register_chatcommand("killme", {
|
||||
description = "Kill yourself to respawn",
|
||||
func = function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
if minetest.settings:get_bool("enable_damage") then
|
||||
player:set_hp(0)
|
||||
return true
|
||||
else
|
||||
for _, callback in pairs(core.registered_on_respawnplayers) do
|
||||
if callback(player) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- There doesn't seem to be a way to get a default spawn pos from the lua API
|
||||
return false, "No static_spawnpoint defined"
|
||||
end
|
||||
else
|
||||
-- Show error message if used when not logged in, eg: from IRC mod
|
||||
return false, "You need to be online to be killed!"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
|
@ -20,10 +20,10 @@ es = {}
|
|||
--Modified by maikerumine
|
||||
-- Time to shut down server.
|
||||
-- Default is twice a day: at 06:05 and 18:05
|
||||
local H = 18
|
||||
local X = 18
|
||||
local Y = 19
|
||||
local Z = 19
|
||||
local H = 19
|
||||
local X = 19
|
||||
local Y = 20
|
||||
local Z = 20
|
||||
|
||||
local M = 55
|
||||
local N = 00
|
||||
|
@ -41,7 +41,7 @@ minetest.register_globalstep(function(dtime)
|
|||
local t = os.date("*t")
|
||||
if ((t.hour == H or t.hour == X) and (t.min == M) and (t.sec <= 2)
|
||||
and ((D == nil) or (t.wday == D))) then
|
||||
minetest.chat_send_all("Scheduled shutdown. 1900 Eastern Time Zone"
|
||||
minetest.chat_send_all("Scheduled shutdown. 2000 Eastern Time Zone DST 0000 ZULU "
|
||||
.."Shutting down in FIVE minutes. Enjoy the break")
|
||||
minetest.chat_send_all("STORE YOUR ITEMS WITHIN 4 MINUTES. "
|
||||
.."Shutting down in FIVE minutes.")
|
||||
|
@ -49,18 +49,18 @@ minetest.register_globalstep(function(dtime)
|
|||
end
|
||||
if ((t.hour == Y or t.hour == Z) and (t.min ==N) and (t.sec <= 2)
|
||||
and ((D == nil) or (t.wday == D))) then
|
||||
minetest.chat_send_all("SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed-up.")
|
||||
minetest.chat_send_all("5 SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed--up.")
|
||||
minetest.chat_send_all("4 SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed---up.")
|
||||
minetest.chat_send_all("3 SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed----up.")
|
||||
minetest.chat_send_all("2 SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed-----up.")
|
||||
minetest.chat_send_all("1 SHUTTING SERVER DOWN NOW!"
|
||||
.." Please come back in a few while map is backed------up.")
|
||||
minetest.chat_send_all("SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed-up. ")
|
||||
minetest.chat_send_all("E SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed--up. ")
|
||||
minetest.chat_send_all("S SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed---up. ")
|
||||
minetest.chat_send_all("M SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed----up. ")
|
||||
minetest.chat_send_all("! SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed-----up. ")
|
||||
minetest.chat_send_all("! SHUTTING SERVER DOWN NOW! "
|
||||
.." Please come back in a few while map is backed------up. ")
|
||||
minetest.after(2, minetest.request_shutdown)
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -41,7 +41,7 @@ local crop_def = {
|
|||
snappy = 3, flammable = 2, plant = 1, attached_node = 1,
|
||||
not_in_creative_inventory = 1, growing = 1
|
||||
},
|
||||
sounds = default.node_sound_leaves_defaults()
|
||||
sounds = default.node_sound_wood_defaults()
|
||||
}
|
||||
|
||||
-- stage 1
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
minetest.register_on_newplayer(function(player)
|
||||
--print("on_newplayer")
|
||||
minetest.log("action", "Giving initial stuff to player "..player:get_player_name())
|
||||
player:get_inventory():add_item('main', 'default:torch 3')
|
||||
player:get_inventory():add_item('main', 'default:apple 3')
|
||||
player:get_inventory():add_item('main', 'default:pick_wood 1')
|
||||
player:get_inventory():add_item('main', 'mt_seasons:jackolantern 1')
|
||||
player:get_inventory():add_item('main', 'default:goldblock 1')
|
||||
|
||||
|
||||
|
||||
end)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
maikerumine changed to add es nodes and added metal sound compatibility.
|
||||
|
||||
Stairs Redo 0.1 based on Minetest 0.4 mod: stairs
|
||||
|
||||
License of source code:
|
||||
-----------------------
|
||||
Copyright (C) 2011-2012 Kahrl <kahrl@gmx.net>
|
||||
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
|
||||
License of media (textures and sounds)
|
||||
--------------------------------------
|
||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
Authors of media files
|
||||
-----------------------
|
||||
Everything not listed in here:
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
|
||||
Note: This mod has been changed by TenPlus1 to include other blocks from different mods as well as corner stairs...
|
|
@ -0,0 +1,16 @@
|
|||
Minetest Game mod: stairs
|
||||
=========================
|
||||
See license.txt for license information.
|
||||
|
||||
Authors of source code
|
||||
----------------------
|
||||
Originally by Kahrl <kahrl@gmx.net> (LGPL 2.1) and
|
||||
celeron55, Perttu Ahola <celeron55@gmail.com> (LGPL 2.1)
|
||||
Various Minetest developers and contributors (LGPL 2.1)
|
||||
|
||||
Authors of media (models)
|
||||
-------------------------
|
||||
Jean-Patrick G. (kilbith) <jeanpatrick.guerrero@gmail.com> (CC BY-SA 3.0):
|
||||
stairs_stair.obj
|
||||
|
||||
|
|
@ -1 +1,2 @@
|
|||
default
|
||||
farming
|
||||
|
|