Compare commits
42 Commits
rewrite-we
...
master
Author | SHA1 | Date |
---|---|---|
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 23f1c51912 | |
Nils Dagsson Moskopp | 4d02af8c94 | |
Li0n_2 | 7ede0ca79a | |
JosiahWI | e2e7e15b39 | |
cora | 93a0879b40 | |
Nils Dagsson Moskopp | 0b5fa14041 | |
cora | 4f33f626f5 | |
Nils Dagsson Moskopp | df7bd78af5 | |
Nils Dagsson Moskopp | fe3e837e1b | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 6f811b3cee | |
Lizzy Fleckenstein | dfa56f229f | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | ee77f33ea8 | |
cora | 5deaabdb47 | |
cora | d77f31eab8 | |
cora | 10670d5c10 | |
Nils Dagsson Moskopp | 609105e091 | |
cora | f9e3c4fd6d | |
cora | d70c05e92c | |
Lizzy Fleckenstein | cacddd3fb4 | |
cora | dd347c50c5 | |
Nils Dagsson Moskopp | 54b36c42a9 | |
cora | 5252952555 | |
Li0n_2 | c08f9850f6 | |
Nils Dagsson Moskopp | f8b5066c61 | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 4b9094ddc2 | |
cora | 5087e8194a | |
cora | 71befcff10 | |
Nils Dagsson Moskopp | 66dd87af25 | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | fc47f0a87e | |
Nils Dagsson Moskopp | 6bd4b0e62c | |
AFCMS | 3473f55136 | |
Lizzy Fleckenstein | af7f7c3574 | |
cora | 434e545745 | |
Nils Dagsson Moskopp | 76d2846c59 | |
Nils Dagsson Moskopp | 526c25aa57 | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 249cfb8118 | |
cora | 84194b71e6 | |
cora | 0b7097cb28 | |
cora | bfbc953b92 | |
Nils Dagsson Moskopp | 253100380c | |
cora | df98db1d8c | |
Nils Dagsson Moskopp | f410de485a |
|
@ -21,7 +21,7 @@ The basic digging time groups determine by which tools a node can be dug.
|
|||
* `swordy=1`: Diggable by sword (any material), and this node is *not* a cobweb
|
||||
* `swordy_cobweb=1`: Diggable by sword (any material), and this node is a cobweb
|
||||
* `shearsy=1`: Diggable by shears, and this node is *not* wool
|
||||
* `shearsy=wool=1`: Diggable by shears, and this node is wool
|
||||
* `shearsy_wool=1`: Diggable by shears, and this node is wool
|
||||
* `handy=1`: Breakable by hand and this node gives it useful drop when dug by hand. All nodes which are breakable by pickaxe, axe, shovel, sword or shears are also automatically breakable by hand, but not neccess
|
||||
* `creative_breakable=1`: Block is breakable by hand in creative mode. This group is implied if the node belongs to any other digging group
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# mcl_colors
|
||||
Mod providing global table containing legacy minecraft colors to be used in mods.
|
||||
|
||||
## mcl_colors.*
|
||||
Colors by upper name, in hex value.
|
||||
|
||||
## mcl_colors.background.*
|
||||
Background colors by upper name, in hex value.
|
|
@ -0,0 +1,36 @@
|
|||
mcl_colors = {
|
||||
BLACK = "#000000",
|
||||
DARK_BLUE = "#0000AA",
|
||||
DARK_GREEN = "#00AA00",
|
||||
DARK_AQUA = "#00AAAA",
|
||||
DARK_RED = "#AA0000",
|
||||
DARK_PURPLE = "#AA00AA",
|
||||
GOLD = "#FFAA00",
|
||||
GRAY = "#AAAAAA",
|
||||
DARK_GRAY = "#555555",
|
||||
BLUE = "#5555FF",
|
||||
GREEN = "#55FF55",
|
||||
AQUA = "#55FFFF",
|
||||
RED = "#FF5555",
|
||||
LIGHT_PURPLE = "#FF55FF",
|
||||
YELLOW = "#FFFF55",
|
||||
WHITE = "#FFFFFF",
|
||||
background = {
|
||||
BLACK = "#000000",
|
||||
DARK_BLUE = "#00002A",
|
||||
DARK_GREEN = "#002A00",
|
||||
DARK_AQUA = "#002A2A",
|
||||
DARK_RED = "#2A0000",
|
||||
DARK_PURPLE = "#2A002A",
|
||||
GOLD = "#2A2A00",
|
||||
GRAY = "#2A2A2A",
|
||||
DARK_GRAY = "#151515",
|
||||
BLUE = "#15153F",
|
||||
GREEN = "#153F15",
|
||||
AQUA = "#153F3F",
|
||||
RED = "#3F1515",
|
||||
LIGHT_PURPLE = "#3F153F",
|
||||
YELLOW = "#3F3F15",
|
||||
WHITE = "#373501",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_colors
|
||||
author = Fleckenstein
|
||||
description = The HTML sequences for the minecraft colors
|
|
@ -155,6 +155,16 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
}) + 1
|
||||
end
|
||||
|
||||
local already_burning = mcl_burning.is_burning(obj)
|
||||
|
||||
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
mcl_burning.set(obj, "string", "reason", reason)
|
||||
|
||||
if already_burning then
|
||||
return
|
||||
end
|
||||
|
||||
local hud_id
|
||||
if obj:is_player() then
|
||||
hud_id = mcl_burning.get(obj, "int", "hud_id")
|
||||
|
@ -168,8 +178,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
}) + 1
|
||||
end
|
||||
end
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
mcl_burning.set(obj, "string", "reason", reason)
|
||||
|
||||
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
||||
mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||
|
||||
|
@ -285,7 +294,7 @@ function mcl_burning.fire_entity_step(self, dtime)
|
|||
end
|
||||
|
||||
local animation_timer = self.animation_timer + dtime
|
||||
if animation_timer >= 0.015 then
|
||||
if animation_timer >= ( 1 / mcl_burning.animation_fps ) then
|
||||
animation_timer = 0
|
||||
local animation_frame = self.animation_frame + 1
|
||||
if animation_frame > mcl_burning.animation_frames - 1 then
|
||||
|
@ -296,3 +305,48 @@ function mcl_burning.fire_entity_step(self, dtime)
|
|||
end
|
||||
self.animation_timer = animation_timer
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("burn", {
|
||||
params = S("<playername> <duration> <reason>"),
|
||||
description = S("Sets a player on fire for the given amount of seconds with the given reason."),
|
||||
privs = { debug = true },
|
||||
func = function(name, params)
|
||||
local playername, duration, reason = params:match("^(.+) (.+) (.+)$")
|
||||
if not (playername and duration and reason) then
|
||||
return false, S("Error: Parameter missing.")
|
||||
end
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
if not player then
|
||||
return false, S(
|
||||
"Error: Player “@1” not found.",
|
||||
playername
|
||||
)
|
||||
end
|
||||
local duration_number = tonumber(duration)
|
||||
-- Lua numbers are truthy
|
||||
-- NaN is not equal to NaN
|
||||
if not duration_number or (duration_number ~= duration_number) then
|
||||
return false, S(
|
||||
"Error: Duration “@1” is not a number.",
|
||||
duration
|
||||
)
|
||||
end
|
||||
if duration_number < 0 then
|
||||
return false, S(
|
||||
"Error: Duration “@1” is negative.",
|
||||
duration
|
||||
)
|
||||
end
|
||||
mcl_burning.set_on_fire(
|
||||
player,
|
||||
duration_number,
|
||||
reason
|
||||
)
|
||||
return true, S(
|
||||
"Set @1 on fire for @2s for the following reason: @3",
|
||||
playername,
|
||||
duration,
|
||||
reason
|
||||
)
|
||||
end,
|
||||
})
|
||||
|
|
|
@ -2,7 +2,8 @@ local S = minetest.get_translator("mcl_burning")
|
|||
local modpath = minetest.get_modpath("mcl_burning")
|
||||
|
||||
mcl_burning = {
|
||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8,
|
||||
animation_fps = tonumber(minetest.settings:get("fire_animation_fps")) or 30
|
||||
}
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
|
|
|
@ -14,6 +14,8 @@ local DEFAULT_FALL_SPEED = -10
|
|||
local FLOP_HEIGHT = 5.0
|
||||
local FLOP_HOR_SPEED = 1.5
|
||||
|
||||
local LIGHT_SUN = minetest.LIGHT_MAX + 1
|
||||
|
||||
local MOB_CAP = {}
|
||||
MOB_CAP.hostile = 70
|
||||
MOB_CAP.passive = 10
|
||||
|
@ -1057,7 +1059,7 @@ local do_env_damage = function(self)
|
|||
if mod_worlds then
|
||||
_, dim = mcl_worlds.y_to_layer(pos.y)
|
||||
end
|
||||
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (minetest.get_node_light(pos) or 0) >= minetest.LIGHT_MAX and dim == "overworld" then
|
||||
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (minetest.get_node_light(pos) or 0) == LIGHT_SUN and dim == "overworld" then
|
||||
if self.ignited_by_sunlight then
|
||||
mcl_burning.set_on_fire(self.object, 10)
|
||||
else
|
||||
|
|
|
@ -1,78 +1,5 @@
|
|||
`mcl_weather`
|
||||
=======================
|
||||
This was largely rewritten to use particlespawners (what were you thinking !?) and an event based code flow.
|
||||
|
||||
|
||||
Weathers included
|
||||
-----------------------
|
||||
* rain
|
||||
* snow
|
||||
* thunder
|
||||
|
||||
Commands
|
||||
-----------------------
|
||||
`weather <weather>`, requires `weather_manager` privilege.
|
||||
|
||||
Dependencies
|
||||
-----------------------
|
||||
Lightning has been made a hard dependency since it is included in Mineclonia.
|
||||
|
||||
Adding new weathers:
|
||||
|
||||
It's as simple as calling a mcl_weather.register_weather(weatherdef):
|
||||
|
||||
Weatherdef
|
||||
-----------------------
|
||||
{
|
||||
light_factor = 1,
|
||||
-- 0-1
|
||||
|
||||
cloudcolor= ,
|
||||
--hexrgb
|
||||
|
||||
sound,
|
||||
--A sound name (loop)
|
||||
|
||||
min_duration ,
|
||||
-- In seconds
|
||||
|
||||
max_duration ,
|
||||
transitions,
|
||||
--The chances how likely this weather is to change into another weather.
|
||||
-- e.g.
|
||||
-- {
|
||||
-- [65] = "none",
|
||||
-- [80] = "rain",
|
||||
-- [100] = "thunder",
|
||||
-- }
|
||||
|
||||
skylayer= ,
|
||||
-- a skylayer defintion (see skycolor api)
|
||||
|
||||
particlespawners = {},
|
||||
-- a table of particlespawnerdefinitions
|
||||
|
||||
start ,
|
||||
--A function that is run when the weather is about to start
|
||||
|
||||
start_player,
|
||||
--A function per player that is run when the weather is about to start
|
||||
clear ,
|
||||
--A function that is run when the weather is about to clear/change
|
||||
clear_player , -- function per player that is run on change (first argument is the playername)
|
||||
|
||||
at_pos ,
|
||||
--A function that is run for each player with current position as argument. weather is disabled for the player when it returns false
|
||||
|
||||
change_at_pos,
|
||||
--function that is run for each player with current position as argument. return a weather name and the weather transitions into the supplied weather for that player
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
This was the original README.md from mcl2_weather:
|
||||
|
||||
Weather mod for MineClone 2. Forked from the `weather_pack` mod by xeranas.
|
||||
|
||||
Weathers included
|
||||
|
|
|
@ -1,385 +1,17 @@
|
|||
local S = minetest.get_translator("mcl_weather")
|
||||
local modpath = minetest.get_modpath("mcl_weather")
|
||||
|
||||
mcl_weather = {}
|
||||
|
||||
mcl_weather.allow_abm = true
|
||||
mcl_weather.current = "none"
|
||||
mcl_weather.state = "none"
|
||||
mcl_weather.current_endtime = 0
|
||||
mcl_weather.next = nil
|
||||
mcl_weather.registered_weathers = {}
|
||||
|
||||
mcl_weather.min_duration = 600
|
||||
mcl_weather.max_duration = 9000
|
||||
|
||||
local players = {}
|
||||
players.particlespawners={}
|
||||
players.soundhandler={}
|
||||
players.weatheractive = {}
|
||||
players.weather = {}
|
||||
|
||||
local interval=1
|
||||
local weathercycle_active=false
|
||||
|
||||
function mcl_weather.register_weather(name,def)
|
||||
mcl_weather.registered_weathers[name] = def
|
||||
-- If not located then embeded skycolor mod version will be loaded.
|
||||
if minetest.get_modpath("skycolor") == nil then
|
||||
dofile(modpath.."/skycolor.lua")
|
||||
end
|
||||
mcl_weather.register_weather("none",{
|
||||
min_duration = mcl_weather.min_duration,
|
||||
max_duration = mcl_weather.max_duration,
|
||||
transitions = {
|
||||
[50] = "rain",
|
||||
[100] = "snow"
|
||||
}
|
||||
})
|
||||
local modpath=minetest.get_modpath(minetest.get_current_modname())
|
||||
dofile(modpath.."/skycolor.lua")
|
||||
|
||||
dofile(modpath.."/weather_core.lua")
|
||||
dofile(modpath.."/snow.lua")
|
||||
dofile(modpath.."/rain.lua")
|
||||
dofile(modpath.."/nether_dust.lua")
|
||||
dofile(modpath.."/thunder.lua")
|
||||
|
||||
function mcl_weather.get_weatherdef(name)
|
||||
if mcl_weather.registered_weathers[name] then
|
||||
return mcl_weather.registered_weathers[name]
|
||||
end
|
||||
return mcl_weather.registered_weathers["none"]
|
||||
if minetest.get_modpath("lightning") ~= nil then
|
||||
dofile(modpath.."/thunder.lua")
|
||||
end
|
||||
|
||||
function mcl_weather.get_weather()
|
||||
return mcl_weather.current
|
||||
end
|
||||
|
||||
function mcl_weather.get_current_light_factor()
|
||||
local lf = mcl_weather.get_weatherdef(mcl_weather.current).light_factor
|
||||
if mcl_weather.current == "none" or not lf then
|
||||
return nil
|
||||
else
|
||||
return lf
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns true if pos is outdoor.
|
||||
-- Outdoor is defined as any node in the Overworld under open sky.
|
||||
-- FIXME: Nodes below glass also count as “outdoor”, this should not be the case.
|
||||
function mcl_weather.is_outdoor(pos)
|
||||
local cpos = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
local dim = mcl_worlds.pos_to_dimension(cpos)
|
||||
if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- checks if player is undewater. This is needed in order to
|
||||
-- turn off weather particles generation.
|
||||
function mcl_weather.is_underwater(player)
|
||||
local ppos=player:get_pos()
|
||||
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
|
||||
|
||||
function mcl_weather.player_has_weather(player)
|
||||
local pos=player:get_pos()
|
||||
if mcl_weather.is_outdoor(pos) and not mcl_weather.is_underwater(player) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function mcl_weather.add_sound(name,sound)
|
||||
players.soundhandler[name] = minetest.sound_play(sound, {
|
||||
to_player = name,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
function mcl_weather.remove_sound(name)
|
||||
if players.soundhandler[name] then
|
||||
minetest.sound_fade(players.soundhandler[name], -0.5, 0.0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function mcl_weather.add_particlespawners_player(name,spawners)
|
||||
if not spawners then return end
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then return end
|
||||
if players.particlespawners[name] ~= nil then return end
|
||||
players.particlespawners[name]={}
|
||||
for k,v in ipairs(spawners) do
|
||||
v.playername=name
|
||||
v.attached=player
|
||||
table.insert(players.particlespawners[name],minetest.add_particlespawner(v))
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_weather.delete_particlespawners_player(name)
|
||||
if players.particlespawners[name] == nil then
|
||||
players.particlespawners[name]={}
|
||||
end
|
||||
for k,v in ipairs(players.particlespawners[name]) do
|
||||
minetest.delete_particlespawner(v)
|
||||
end
|
||||
players.particlespawners[name]=nil
|
||||
end
|
||||
|
||||
local function doplayers(func,players)
|
||||
players = players or minetest.get_connected_players()
|
||||
for k,v in ipairs(players) do
|
||||
func(v:get_player_name(),v)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_weather.get_next_weather()
|
||||
local current=mcl_weather.get_weatherdef(mcl_weather.current)
|
||||
local random_roll = math.random(0,100)
|
||||
local new_weather
|
||||
if current and current.transitions then
|
||||
for v, weather in pairs(current.transitions) do
|
||||
if random_roll < v then
|
||||
new_weather=weather
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not new_weather then new_weather = "none" end
|
||||
return new_weather
|
||||
end
|
||||
|
||||
function mcl_weather.start_weather_player(name,def)
|
||||
local player=minetest.get_player_by_name(name)
|
||||
mcl_weather.add_particlespawners_player(name,def.particlespawners)
|
||||
if def.skycolor then
|
||||
local player=minetest.get_player_by_name(name)
|
||||
player:set_clouds({color=def.skycolor})
|
||||
end
|
||||
if def.sound then
|
||||
mcl_weather.add_sound(name,def.sound)
|
||||
end
|
||||
players.weather[name]=def
|
||||
players.weatheractive[name] = true
|
||||
end
|
||||
|
||||
function mcl_weather.stop_weather_player(name,def)
|
||||
mcl_weather.delete_particlespawners_player(name)
|
||||
if def.skycolor then
|
||||
local player=minetest.get_player_by_name(name)
|
||||
player:set_clouds({color="#FFF0F0E5"})
|
||||
end
|
||||
mcl_weather.remove_sound(name,def.sound)
|
||||
end
|
||||
|
||||
function mcl_weather.start_weather(def)
|
||||
if def.start then def.start() end
|
||||
|
||||
if def.skylayer then
|
||||
mcl_weather.skycolor.add_layer("weather",def.skylayer)
|
||||
mcl_weather.skycolor.update_sky_color()
|
||||
end
|
||||
doplayers(function(name)
|
||||
if def.start_player == nil or not def.start_player(name) then
|
||||
mcl_weather.start_weather_player(name,def)
|
||||
players.weather[name]=mcl_weather.current
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function mcl_weather.stop_weather(def)
|
||||
if def.clear then def.clear() end
|
||||
if def.clear_player then doplayers(def.clear_player) end
|
||||
doplayers(function(name)
|
||||
mcl_weather.stop_weather_player(name,def)
|
||||
end)
|
||||
if def.skylayer then
|
||||
mcl_weather.skycolor.remove_layer("weather")
|
||||
mcl_weather.skycolor.force_update=true
|
||||
mcl_weather.skycolor.update_sky_color()
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_weather.change(new_weather,force)
|
||||
if new_weather == mcl_weather.current then return end
|
||||
local def=mcl_weather.get_weatherdef(new_weather)
|
||||
local old=mcl_weather.get_weatherdef(mcl_weather.current)
|
||||
if not def then return end
|
||||
if force or minetest.settings:get_bool("mcl_doWeatherCycle") then
|
||||
mcl_weather.stop_weather(old)
|
||||
mcl_weather.start_weather(def)
|
||||
mcl_weather.current = new_weather
|
||||
mcl_weather.state = new_weather
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_weather.seasonal_change()
|
||||
local new=mcl_weather.get_next_weather()
|
||||
local def=mcl_weather.get_weatherdef(new)
|
||||
local duration = math.random(def.min_duration,def.max_duration)
|
||||
mcl_weather.current_endtime = os.time() + duration
|
||||
minetest.log("action", "[mcl_weather] Weather changed from " .. mcl_weather.current .. " to " .. new .. " Duration: "..duration)
|
||||
mcl_weather.change(new)
|
||||
minetest.after(duration,mcl_weather.seasonal_change)
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
minetest.after(5,mcl_weather.seasonal_change)
|
||||
end)
|
||||
|
||||
function mcl_weather.change_player(name,new)
|
||||
if players.weather[name] ~= new then
|
||||
local nd = mcl_weather.get_weatherdef(new)
|
||||
local od=mcl_weather.get_weatherdef(mcl_weather.current)
|
||||
if players.weather[name] ~= nil then
|
||||
minetest.log("action", "[mcl_weather] Weather for ".. name .." changed from "..players.weather[name].. " to "..new)
|
||||
od=mcl_weather.get_weatherdef(players.weather[name])
|
||||
else
|
||||
minetest.log("action", "[mcl_weather] Weather for ".. name .." changed from "..mcl_weather.current.. " to "..new)
|
||||
end
|
||||
if nd then
|
||||
mcl_weather.stop_weather_player(name,od)
|
||||
mcl_weather.start_weather_player(name,nd)
|
||||
players.weather[name] = new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_weather.tick()
|
||||
doplayers(function(name,player)
|
||||
local pos=player:get_pos()
|
||||
local cdef=mcl_weather.get_weatherdef(mcl_weather.current)
|
||||
|
||||
if type(cdef.change_at_pos) == "function" then --switch to returned weather in at_pos conditions
|
||||
local cap=cdef.change_at_pos(pos)
|
||||
if cap then
|
||||
mcl_weather.change_player(name,cap)
|
||||
else
|
||||
mcl_weather.change_player(name,mcl_weather.current)
|
||||
end
|
||||
end
|
||||
|
||||
if players.weatheractive[name] then --turn off weather indoows/underwater
|
||||
if not mcl_weather.player_has_weather(player) then
|
||||
mcl_weather.stop_weather_player(name,cdef)
|
||||
players.weatheractive[name] = false
|
||||
end
|
||||
if type(cdef.at_pos) == "function" and cdef.at_pos(pos) then
|
||||
mcl_weather.stop_weather_player(name,cdef)
|
||||
players.weatheractive[name] = false
|
||||
end
|
||||
else
|
||||
if mcl_weather.player_has_weather(player) then
|
||||
mcl_weather.start_weather_player(name,cdef)
|
||||
players.weather[name]=mcl_weather.current
|
||||
players.weatheractive[name] = true
|
||||
end
|
||||
if type(cdef.at_pos) == "function" and not cdef.at_pos(pos) then
|
||||
mcl_weather.start_weather_player(name,cdef)
|
||||
players.weatheractive[name] = false
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
minetest.after(interval,mcl_weather.tick)
|
||||
end
|
||||
mcl_weather.tick()
|
||||
|
||||
mcl_worlds.register_on_dimension_change(function(player, dimension)
|
||||
if mcl_worlds.has_weather(player:get_pos()) then
|
||||
mcl_weather.start_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
|
||||
else
|
||||
mcl_weather.stop_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
mcl_weather.start_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
cla_weather.stop_weather_player(player:get_player_name())
|
||||
end)
|
||||
|
||||
minetest.register_privilege("weather_manager", {
|
||||
description = S("Gives ability to control weather"),
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
-- Weather command definition. Set
|
||||
minetest.register_chatcommand("weather", {
|
||||
params = "(clear | rain | snow | thunder) [<duration>]",
|
||||
description = S("Changes the weather to the specified parameter."),
|
||||
privs = {weather_manager = true},
|
||||
func = function(name, param)
|
||||
if (param == "") then
|
||||
return false, S("Error: No weather specified.")
|
||||
end
|
||||
local player=minetest.get_player_by_name(name)
|
||||
local new_weather, duration
|
||||
local parse1, parse2 = string.match(param, "(%w+) ?(%d*)")
|
||||
if parse1 then
|
||||
if parse1 == "clear" then
|
||||
new_weather = "none"
|
||||
else
|
||||
new_weather = parse1
|
||||
end
|
||||
else
|
||||
return false, S("Error: Invalid parameters.")
|
||||
end
|
||||
if parse2 then
|
||||
if type(tonumber(parse2)) == "number" then
|
||||
duration = tonumber(parse2)
|
||||
if duration < 1 then
|
||||
return false, S("Error: Duration can't be less than 1 second.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local def=mcl_weather.get_weatherdef(param)
|
||||
local old_weather=mcl_weather.current
|
||||
if def then
|
||||
if duration then
|
||||
def.min_duration=duration
|
||||
def.max_duration=duration
|
||||
end
|
||||
mcl_weather.change(param,true)
|
||||
minetest.log("action", "[mcl_weather] " .. name .. " changed the weather from " .. old_weather .. " to " .. param)
|
||||
return true
|
||||
end
|
||||
return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.")
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("biome", {
|
||||
params = "",
|
||||
description = S("Gets current biome"),
|
||||
privs = {weather_manager = true},
|
||||
func = function(name)
|
||||
local player=minetest.get_player_by_name(name)
|
||||
local biome=minetest.get_biome_data(player:get_pos())
|
||||
local bname = minetest.get_biome_name(biome.biome)
|
||||
minetest.chat_send_player(name,bname.. " " ..dump(biome))
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("toggledownfall", {
|
||||
params = "",
|
||||
description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"),
|
||||
privs = {weather_manager = true},
|
||||
func = function(name, param)
|
||||
-- Currently rain/thunder/snow: Set weather to clear
|
||||
if mcl_weather.state ~= "none" then
|
||||
mcl_weather.change("none",true)
|
||||
|
||||
-- Currently clear: Set weather randomly to rain/thunder/snow
|
||||
else
|
||||
local new = { "rain", "thunder", "snow" }
|
||||
local r = math.random(1, #new)
|
||||
return mcl_weather.change(new[r],true)
|
||||
end
|
||||
end
|
||||
})
|
|
@ -1,3 +1,4 @@
|
|||
name = mcl_weather
|
||||
description = Weather and sky handling: Rain, snow, thunderstorm, End and Nether ambience
|
||||
depends = mcl_init, mcl_worlds, lightning
|
||||
depends = mcl_init, mcl_worlds
|
||||
optional_depends = lightning
|
||||
|
|
|
@ -1,70 +1,36 @@
|
|||
mcl_weather.nether_dust = {}
|
||||
mcl_weather.nether_dust.particlespawners = {}
|
||||
mcl_weather.nether_dust.particles_count = 99
|
||||
|
||||
local psdef= {
|
||||
amount = 99,
|
||||
time = 0,
|
||||
minpos = vector.new(-25,0,-25),
|
||||
maxpos =vector.new(25,4,25),
|
||||
minvel = vector.new(-0.3,-0.15,-1),
|
||||
maxvel = vector.new(0.3,0.15,0.3),
|
||||
minacc = vector.new(-1,-0.4,-1),
|
||||
maxacc = vector.new(1,0.4,1),
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 0.1,
|
||||
maxsize = 2,
|
||||
collisiondetection = false,
|
||||
collision_removal = false,
|
||||
object_collision = false,
|
||||
vertical = false,
|
||||
glow = math.random(0,minetest.LIGHT_MAX)
|
||||
}
|
||||
|
||||
local function check_player(player)
|
||||
local name=player:get_player_name(name)
|
||||
if mcl_worlds.has_dust(player:get_pos()) and not mcl_weather.nether_dust.particlespawners[name] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
mcl_weather.nether_dust.add_particlespawners = function(player)
|
||||
if not check_player(player) then return end
|
||||
local name=player:get_player_name(name)
|
||||
mcl_weather.nether_dust.particlespawners[name]={}
|
||||
psdef.playername = name
|
||||
psdef.attached = player
|
||||
for i=1,3 do
|
||||
psdef.texture="mcl_particles_nether_dust"..i..".png"
|
||||
mcl_weather.nether_dust.particlespawners[name][i]=minetest.add_particlespawner(psdef)
|
||||
-- calculates coordinates and draw particles for Nether dust
|
||||
mcl_weather.nether_dust.add_dust_particles = function(player)
|
||||
for i=mcl_weather.nether_dust.particles_count, 1,-1 do
|
||||
local rpx, rpy, rpz = mcl_weather.get_random_pos_by_player_look_dir(player)
|
||||
minetest.add_particle({
|
||||
pos = {x = rpx, y = rpy - math.random(6, 18), z = rpz},
|
||||
velocity = {x = math.random(-30,30)*0.01, y = math.random(-15,15)*0.01, z = math.random(-30,30)*0.01},
|
||||
acceleration = {x = math.random(-50,50)*0.02, y = math.random(-20,20)*0.02, z = math.random(-50,50)*0.02},
|
||||
expirationtime = 3,
|
||||
size = math.random(6,20)*0.01,
|
||||
collisiondetection = false,
|
||||
object_collision = false,
|
||||
vertical = false,
|
||||
glow = math.random(0,minetest.LIGHT_MAX),
|
||||
texture = "mcl_particles_nether_dust"..tostring(i%3+1)..".png",
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
mcl_weather.nether_dust.delete_particlespawners = function(player)
|
||||
local name=player:get_player_name(name)
|
||||
if mcl_weather.nether_dust.particlespawners[name] then
|
||||
for i=1,3 do
|
||||
minetest.delete_particlespawner(mcl_weather.nether_dust.particlespawners[name][i])
|
||||
end
|
||||
mcl_weather.nether_dust.particlespawners[name]=nil
|
||||
end
|
||||
end
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer < 0.7 then return end
|
||||
timer = 0
|
||||
|
||||
mcl_worlds.register_on_dimension_change(function(player, dimension)
|
||||
if dimension == "nether" then
|
||||
if check_player(player) then
|
||||
mcl_weather.nether_dust.add_particlespawners(player)
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
if not mcl_worlds.has_dust(player:get_pos()) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
mcl_weather.nether_dust.delete_particlespawners(player)
|
||||
mcl_weather.nether_dust.add_dust_particles(player)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if check_player(player) then
|
||||
mcl_weather.nether_dust.add_particlespawners(player)
|
||||
end
|
||||
end)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
mcl_weather.nether_dust.delete_particlespawners(player)
|
||||
end)
|
|
@ -1,105 +1,200 @@
|
|||
local PARTICLES_COUNT_RAIN = 30
|
||||
local PARTICLES_COUNT_THUNDER = 45
|
||||
|
||||
mcl_weather.rain = {
|
||||
-- max rain particles created at time
|
||||
particles_count = PARTICLES_COUNT_RAIN,
|
||||
|
||||
-- flag to turn on/off extinguish fire for rain
|
||||
extinguish_fire = true,
|
||||
|
||||
-- flag useful when mixing weathers
|
||||
raining = false
|
||||
}
|
||||
mcl_weather.raining = false
|
||||
raining = false,
|
||||
|
||||
mcl_weather.register_weather("rain",{
|
||||
light_factor = 0.6,
|
||||
cloudcolor= "#5D5D5FE8" ,
|
||||
sound = "weather_rain",
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[70] = "none",
|
||||
[100] = "thunder",
|
||||
},
|
||||
skylayer= {{r=0, g=0, b=0},
|
||||
-- keeping last timeofday value (rounded).
|
||||
-- Defaulted to non-existing value for initial comparing.
|
||||
sky_last_update = -1,
|
||||
|
||||
init_done = false,
|
||||
}
|
||||
|
||||
mcl_weather.rain.sound_handler = function(player)
|
||||
return minetest.sound_play("weather_rain", {
|
||||
to_player = player:get_player_name(),
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
|
||||
-- set skybox based on time (uses skycolor api)
|
||||
mcl_weather.rain.set_sky_box = function()
|
||||
if mcl_weather.state == "rain" then
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-rain-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=98},
|
||||
{r=135, g=135, b=151},
|
||||
{r=85, g=86, b=98},
|
||||
{r=0, g=0, b=0}},
|
||||
particlespawners = {
|
||||
{
|
||||
amount = 800,
|
||||
time = 0,
|
||||
minpos = vector.new(-10,10,-10),
|
||||
maxpos =vector.new(10,15,10),
|
||||
minvel = vector.new(0,-8,0),
|
||||
maxvel = vector.new(0,-12,0),
|
||||
minacc = vector.new(0,-20,0),
|
||||
maxacc = vector.new(0,-30,0),
|
||||
minexptime = 1,
|
||||
maxexptime = 2,
|
||||
minsize = 0.5,
|
||||
maxsize = 5,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_rain_raindrop_1.png"
|
||||
},
|
||||
{
|
||||
amount = 200,
|
||||
time = 0,
|
||||
minpos = vector.new(-15,5,-15),
|
||||
maxpos =vector.new(15,15,15),
|
||||
minvel = vector.new(0,-8,0),
|
||||
maxvel = vector.new(0,-12,0),
|
||||
minacc = vector.new(0,-20,0),
|
||||
maxacc = vector.new(0,-40,0),
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 0.5,
|
||||
maxsize = 3,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_rain_raindrop_2.png"
|
||||
},
|
||||
{
|
||||
amount = 200,
|
||||
time = 0,
|
||||
minpos = vector.new(-5,0,-5),
|
||||
maxpos = vector.new(5,0.8,5),
|
||||
minvel = vector.new(-4, 5,-4),
|
||||
maxvel = vector.new(4,10,4),
|
||||
minacc = vector.new(0,10,0),
|
||||
maxacc = vector.new(0,25,0),
|
||||
minexptime = 0.01,
|
||||
maxexptime = 0.04,
|
||||
minsize = 0.5,
|
||||
maxsize = 3,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
texture = "weather_pack_rain_raindrop_2.png",
|
||||
glow = 0
|
||||
}
|
||||
},
|
||||
start = function() mcl_weather.rain.raining = true end,
|
||||
clear = function() mcl_weather.rain.raining = false end,
|
||||
at_pos = function(pos)
|
||||
local biome=minetest.get_biome_data(pos)
|
||||
if mcl_worlds.has_weather(pos) and biome.heat > 95 then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
change_at_pos=function(pos)
|
||||
local biome=minetest.get_biome_data(pos)
|
||||
if mcl_worlds.has_weather(pos) and ( biome.heat < 15 or pos.y > 64 ) then
|
||||
return "snow"
|
||||
{r=0, g=0, b=0}})
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
player:set_clouds({color="#5D5D5FE8"})
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- creating manually parctiles instead of particles spawner because of easier to control
|
||||
-- spawn position.
|
||||
mcl_weather.rain.add_rain_particles = function(player)
|
||||
|
||||
mcl_weather.rain.last_rp_count = 0
|
||||
for i=mcl_weather.rain.particles_count, 1,-1 do
|
||||
local random_pos_x, random_pos_y, random_pos_z = mcl_weather.get_random_pos_by_player_look_dir(player)
|
||||
if mcl_weather.is_outdoor({x=random_pos_x, y=random_pos_y, z=random_pos_z}) then
|
||||
mcl_weather.rain.last_rp_count = mcl_weather.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 = 1.0,
|
||||
size = math.random(0.5, 3),
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = true,
|
||||
texture = mcl_weather.rain.get_texture(),
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Simple random texture getter
|
||||
mcl_weather.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.
|
||||
mcl_weather.rain.add_player = function(player)
|
||||
if mcl_weather.players[player:get_player_name()] == nil then
|
||||
local player_meta = {}
|
||||
player_meta.origin_sky = {player:get_sky()}
|
||||
mcl_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.
|
||||
mcl_weather.rain.remove_player = function(player)
|
||||
local player_meta = mcl_weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil and player_meta.origin_sky ~= nil then
|
||||
player:set_clouds({color="#FFF0F0E5"})
|
||||
mcl_weather.players[player:get_player_name()] = nil
|
||||
end
|
||||
end
|
||||
|
||||
mcl_worlds.register_on_dimension_change(function(player, dimension)
|
||||
if dimension ~= "overworld" and dimension ~= "void" then
|
||||
mcl_weather.rain.remove_sound(player)
|
||||
mcl_weather.rain.remove_player(player)
|
||||
elseif dimension == "overworld" then
|
||||
mcl_weather.rain.update_sound(player)
|
||||
if mcl_weather.rain.raining then
|
||||
mcl_weather.rain.add_rain_particles(player)
|
||||
mcl_weather.rain.add_player(player)
|
||||
end
|
||||
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.
|
||||
mcl_weather.rain.update_sound = function(player)
|
||||
local player_meta = mcl_weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil then
|
||||
if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then
|
||||
return false
|
||||
end
|
||||
|
||||
if player_meta.sound_handler ~= nil then
|
||||
if mcl_weather.rain.last_rp_count == 0 then
|
||||
minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
|
||||
player_meta.sound_handler = nil
|
||||
end
|
||||
elseif mcl_weather.rain.last_rp_count > 0 then
|
||||
player_meta.sound_handler = mcl_weather.rain.sound_handler(player)
|
||||
end
|
||||
|
||||
player_meta.sound_updated = minetest.get_gametime()
|
||||
end
|
||||
end
|
||||
|
||||
-- rain sound removed from player.
|
||||
mcl_weather.rain.remove_sound = function(player)
|
||||
local player_meta = mcl_weather.players[player:get_player_name()]
|
||||
if player_meta ~= nil and player_meta.sound_handler ~= nil then
|
||||
minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
|
||||
player_meta.sound_handler = nil
|
||||
player_meta.sound_updated = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- callback function for removing rain
|
||||
mcl_weather.rain.clear = function()
|
||||
mcl_weather.rain.raining = false
|
||||
mcl_weather.rain.sky_last_update = -1
|
||||
mcl_weather.rain.init_done = false
|
||||
mcl_weather.rain.set_particles_mode("rain")
|
||||
mcl_weather.skycolor.remove_layer("weather-pack-rain-sky")
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
mcl_weather.rain.remove_sound(player)
|
||||
mcl_weather.rain.remove_player(player)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if mcl_weather.state ~= "rain" then
|
||||
return false
|
||||
end
|
||||
|
||||
mcl_weather.rain.make_weather()
|
||||
end)
|
||||
|
||||
mcl_weather.rain.make_weather = function()
|
||||
if mcl_weather.rain.init_done == false then
|
||||
mcl_weather.rain.raining = true
|
||||
mcl_weather.rain.set_sky_box()
|
||||
mcl_weather.rain.set_particles_mode(mcl_weather.mode)
|
||||
mcl_weather.rain.init_done = true
|
||||
end
|
||||
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then
|
||||
mcl_weather.rain.remove_sound(player)
|
||||
return false
|
||||
end
|
||||
mcl_weather.rain.add_player(player)
|
||||
mcl_weather.rain.add_rain_particles(player)
|
||||
mcl_weather.rain.update_sound(player)
|
||||
end
|
||||
end
|
||||
|
||||
-- Switch the number of raindrops: "thunder" for many raindrops, otherwise for normal raindrops
|
||||
mcl_weather.rain.set_particles_mode = function(mode)
|
||||
if mode == "thunder" then
|
||||
mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER
|
||||
else
|
||||
mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN
|
||||
end
|
||||
end
|
||||
|
||||
if mcl_weather.allow_abm then
|
||||
-- ABM for extinguish fire
|
||||
|
@ -168,4 +263,19 @@ if mcl_weather.allow_abm then
|
|||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if mcl_weather.reg_weathers.rain == nil then
|
||||
mcl_weather.reg_weathers.rain = {
|
||||
clear = mcl_weather.rain.clear,
|
||||
light_factor = 0.6,
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[65] = "none",
|
||||
[70] = "snow",
|
||||
[100] = "thunder",
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -70,7 +70,7 @@ mcl_weather.skycolor = {
|
|||
else
|
||||
arg = ratio
|
||||
end
|
||||
if arg and arg >= 0 and arg <= 1 then player:override_day_night_ratio(arg) end
|
||||
player:override_day_night_ratio(arg)
|
||||
end,
|
||||
|
||||
-- Update sky color. If players not specified update sky for all players.
|
||||
|
|
|
@ -1,65 +1,96 @@
|
|||
mcl_weather.register_weather("snow",{
|
||||
light_factor = 0.6,
|
||||
cloudcolor= "#ADADADE8" ,
|
||||
sound = "weather_rain",
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[65] = "none",
|
||||
[80] = "rain",
|
||||
[100] = "thunder",
|
||||
},
|
||||
skylayer= {{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=86},
|
||||
{r=135, g=135, b=135},
|
||||
{r=85, g=86, b=86},
|
||||
{r=0, g=0, b=0}},
|
||||
particlespawners = {
|
||||
{
|
||||
amount = 99,
|
||||
time = 0,
|
||||
minpos = vector.new(-15,-5,-15),
|
||||
maxpos =vector.new(15,10,15),
|
||||
minvel = vector.new(0,-1,0),
|
||||
maxvel = vector.new(0,-4,0),
|
||||
minacc = vector.new(0,-1,0),
|
||||
maxacc = vector.new(0,-4,0),
|
||||
minexptime = 2,
|
||||
maxexptime = 5,
|
||||
minsize = 0.5,
|
||||
maxsize = 5,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_snow_snowflake1.png"
|
||||
},{
|
||||
amount = 30,
|
||||
time = 0,
|
||||
minpos = vector.new(-15,5,-15),
|
||||
maxpos =vector.new(15,10,15),
|
||||
minvel = vector.new(0,-1,0),
|
||||
maxvel = vector.new(0,-5,0),
|
||||
minacc = vector.new(0,-1,0),
|
||||
maxacc = vector.new(0,-4,0),
|
||||
minexptime = 0.1,
|
||||
maxexptime = 0.5,
|
||||
minsize = 0.5,
|
||||
maxsize = 5,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_snow_snowflake2.png"
|
||||
}
|
||||
},
|
||||
change_at_pos = function(pos)
|
||||
local biome=minetest.get_biome_data(pos)
|
||||
if pos.y > 50 or (mcl_worlds.has_weather(pos) and biome.heat > 15 ) then
|
||||
return "rain"
|
||||
end
|
||||
mcl_weather.snow = {}
|
||||
|
||||
mcl_weather.snow.particles_count = 15
|
||||
mcl_weather.snow.init_done = false
|
||||
|
||||
-- calculates coordinates and draw particles for snow weather
|
||||
mcl_weather.snow.add_snow_particles = function(player)
|
||||
mcl_weather.rain.last_rp_count = 0
|
||||
for i=mcl_weather.snow.particles_count, 1,-1 do
|
||||
local random_pos_x, random_pos_y, random_pos_z = mcl_weather.get_random_pos_by_player_look_dir(player)
|
||||
random_pos_y = math.random() + math.random(player:get_pos().y - 1, player:get_pos().y + 7)
|
||||
if minetest.get_node_light({x=random_pos_x, y=random_pos_y, z=random_pos_z}, 0.5) == 15 then
|
||||
mcl_weather.rain.last_rp_count = mcl_weather.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(-100,100)*0.001, y = math.random(-300,-100)*0.004, z = math.random(-100,100)*0.001},
|
||||
acceleration = {x = 0, y=0, z = 0},
|
||||
expirationtime = 8.0,
|
||||
size = 1,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = false,
|
||||
vertical = false,
|
||||
texture = mcl_weather.snow.get_texture(),
|
||||
playername = player:get_player_name()
|
||||
})
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
mcl_weather.snow.set_sky_box = function()
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-snow-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=86},
|
||||
{r=135, g=135, b=135},
|
||||
{r=85, g=86, b=86},
|
||||
{r=0, g=0, b=0}})
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
player:set_clouds({color="#ADADADE8"})
|
||||
end
|
||||
mcl_weather.skycolor.active = true
|
||||
end
|
||||
|
||||
mcl_weather.snow.clear = function()
|
||||
mcl_weather.skycolor.remove_layer("weather-pack-snow-sky")
|
||||
mcl_weather.snow.init_done = false
|
||||
end
|
||||
|
||||
-- Simple random texture getter
|
||||
mcl_weather.snow.get_texture = function()
|
||||
return "weather_pack_snow_snowflake"..math.random(1,2)..".png"
|
||||
end
|
||||
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if mcl_weather.state ~= "snow" then
|
||||
return false
|
||||
end
|
||||
|
||||
timer = timer + dtime;
|
||||
if timer >= 0.5 then
|
||||
timer = 0
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
if mcl_weather.snow.init_done == false then
|
||||
mcl_weather.snow.set_sky_box()
|
||||
mcl_weather.snow.init_done = true
|
||||
end
|
||||
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then
|
||||
return false
|
||||
end
|
||||
mcl_weather.snow.add_snow_particles(player)
|
||||
end
|
||||
end)
|
||||
|
||||
-- register snow weather
|
||||
if mcl_weather.reg_weathers.snow == nil then
|
||||
mcl_weather.reg_weathers.snow = {
|
||||
clear = mcl_weather.snow.clear,
|
||||
light_factor = 0.6,
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[65] = "none",
|
||||
[80] = "rain",
|
||||
[100] = "thunder",
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,110 +1,61 @@
|
|||
-- turn off lightning mod 'auto mode'
|
||||
lightning.auto = false
|
||||
|
||||
mcl_weather.thunder = {
|
||||
next_strike = 0,
|
||||
min_delay = 3,
|
||||
max_delay = 12
|
||||
max_delay = 12,
|
||||
init_done = false,
|
||||
}
|
||||
lightning.auto=false
|
||||
|
||||
local thunderactive=false
|
||||
|
||||
local function do_thunder(start)
|
||||
if (start and not thunderactive) or mcl_weather.current == "thunder" then
|
||||
local delay = math.random(mcl_weather.thunder.min_delay, mcl_weather.thunder.max_delay)
|
||||
minetest.after(delay,do_thunder)
|
||||
thunderactive=true
|
||||
else
|
||||
thunderactive=false
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if mcl_weather.get_weather() ~= "thunder" then
|
||||
return false
|
||||
end
|
||||
minetest.chat_send_all("thunder "..mcl_weather.current)
|
||||
lightning.strike()
|
||||
end
|
||||
|
||||
mcl_weather.rain.set_particles_mode("thunder")
|
||||
mcl_weather.rain.make_weather()
|
||||
|
||||
mcl_weather.register_weather("thunder",{
|
||||
light_factor = 0.33,
|
||||
cloudcolor= "#3D3D3FE8" ,
|
||||
sound = "weather_rain",
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[100] = "rain"
|
||||
},
|
||||
skylayer= {{r=0, g=0, b=0},
|
||||
if mcl_weather.thunder.init_done == false then
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-thunder-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=40, g=40, b=40},
|
||||
{r=85, g=86, b=86},
|
||||
{r=40, g=40, b=40},
|
||||
{r=0, g=0, b=0}},
|
||||
particlespawners = {
|
||||
{
|
||||
amount = 1200,
|
||||
time = 0,
|
||||
minpos = vector.new(-15,5,-15),
|
||||
maxpos =vector.new(15,10,15),
|
||||
minvel = vector.new(0,-8,0),
|
||||
maxvel = vector.new(0,-12,0),
|
||||
minacc = vector.new(0,-20,0),
|
||||
maxacc = vector.new(0,-30,0),
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 2,
|
||||
maxsize = 7,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_rain_raindrop_1.png"
|
||||
},
|
||||
{
|
||||
amount = 800,
|
||||
time = 0,
|
||||
minpos = vector.new(-15,5,-15),
|
||||
maxpos =vector.new(15,15,15),
|
||||
minvel = vector.new(0,-8,0),
|
||||
maxvel = vector.new(0,-12,0),
|
||||
minacc = vector.new(0,-20,0),
|
||||
maxacc = vector.new(0,-40,0),
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 1,
|
||||
maxsize = 5,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
glow = 1,
|
||||
texture="weather_pack_rain_raindrop_2.png"
|
||||
},
|
||||
{
|
||||
amount = 350,
|
||||
time = 0,
|
||||
minpos = vector.new(-5,0,-5),
|
||||
maxpos = vector.new(5,2,5),
|
||||
minvel = vector.new(-4, 5,-4),
|
||||
maxvel = vector.new(4,10,4),
|
||||
minacc = vector.new(0,10,0),
|
||||
maxacc = vector.new(0,30,0),
|
||||
minexptime = 0.01,
|
||||
maxexptime = 0.04,
|
||||
minsize = 1,
|
||||
maxsize = 3,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
vertical = true,
|
||||
texture = "weather_pack_rain_raindrop_2.png",
|
||||
glow = 1
|
||||
}
|
||||
},
|
||||
start = function()
|
||||
do_thunder(true)
|
||||
end,
|
||||
clear = function()
|
||||
mcl_weather.skycolor.remove_layer("lightning")
|
||||
end,
|
||||
at_pos = function(pos)
|
||||
local biome=minetest.get_biome_data(pos)
|
||||
if not mcl_worlds.has_weather(pos) and biome.heat > 95 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
})
|
||||
{r=0, g=0, b=0}})
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
player:set_clouds({color="#3D3D3FE8"})
|
||||
end
|
||||
mcl_weather.thunder.init_done = true
|
||||
end
|
||||
|
||||
if (mcl_weather.thunder.next_strike <= minetest.get_gametime()) then
|
||||
lightning.strike()
|
||||
local delay = math.random(mcl_weather.thunder.min_delay, mcl_weather.thunder.max_delay)
|
||||
mcl_weather.thunder.next_strike = minetest.get_gametime() + delay
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
mcl_weather.thunder.clear = function()
|
||||
mcl_weather.rain.clear()
|
||||
mcl_weather.skycolor.remove_layer("weather-pack-thunder-sky")
|
||||
mcl_weather.skycolor.remove_layer("lightning")
|
||||
mcl_weather.thunder.init_done = false
|
||||
end
|
||||
|
||||
-- register thunderstorm weather
|
||||
if mcl_weather.reg_weathers.thunder == nil then
|
||||
mcl_weather.reg_weathers.thunder = {
|
||||
clear = mcl_weather.thunder.clear,
|
||||
light_factor = 0.33333,
|
||||
-- 10min - 20min
|
||||
min_duration = 600,
|
||||
max_duration = 1200,
|
||||
transitions = {
|
||||
[100] = "rain",
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
local S = minetest.get_translator("mcl_weather")
|
||||
|
||||
-- weather states, 'none' is default, other states depends from active mods
|
||||
mcl_weather.state = "none"
|
||||
|
||||
-- player list for saving player meta info
|
||||
mcl_weather.players = {}
|
||||
|
||||
-- default weather check interval for global step
|
||||
mcl_weather.check_interval = 5
|
||||
|
||||
-- weather min duration
|
||||
mcl_weather.min_duration = 600
|
||||
|
||||
-- weather max duration
|
||||
mcl_weather.max_duration = 9000
|
||||
|
||||
-- weather calculated end time
|
||||
mcl_weather.end_time = nil
|
||||
|
||||
-- registered weathers
|
||||
mcl_weather.reg_weathers = {}
|
||||
|
||||
-- global flag to disable/enable ABM logic.
|
||||
mcl_weather.allow_abm = true
|
||||
|
||||
mcl_weather.reg_weathers["none"] = {
|
||||
min_duration = mcl_weather.min_duration,
|
||||
max_duration = mcl_weather.max_duration,
|
||||
light_factor = nil,
|
||||
transitions = {
|
||||
[50] = "rain",
|
||||
[100] = "snow",
|
||||
},
|
||||
clear = function() end,
|
||||
}
|
||||
|
||||
local storage = minetest.get_mod_storage()
|
||||
-- Save weather into mod storage, so it can be loaded after restarting the server
|
||||
local save_weather = function()
|
||||
storage:set_string("mcl_weather_state", mcl_weather.state)
|
||||
storage:set_int("mcl_weather_end_time", mcl_weather.end_time)
|
||||
minetest.log("verbose", "[mcl_weather] Weather data saved: state="..mcl_weather.state.." end_time="..mcl_weather.end_time)
|
||||
end
|
||||
minetest.register_on_shutdown(save_weather)
|
||||
|
||||
mcl_weather.get_rand_end_time = function(min_duration, max_duration)
|
||||
local r
|
||||
if min_duration ~= nil and max_duration ~= nil then
|
||||
r = math.random(min_duration, max_duration)
|
||||
else
|
||||
r = math.random(mcl_weather.min_duration, mcl_weather.max_duration)
|
||||
end
|
||||
return minetest.get_gametime() + r
|
||||
end
|
||||
|
||||
mcl_weather.get_current_light_factor = function()
|
||||
if mcl_weather.state == "none" then
|
||||
return nil
|
||||
else
|
||||
return mcl_weather.reg_weathers[mcl_weather.state].light_factor
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns true if pos is outdoor.
|
||||
-- Outdoor is defined as any node in the Overworld under open sky.
|
||||
-- FIXME: Nodes below glass also count as “outdoor”, this should not be the case.
|
||||
mcl_weather.is_outdoor = function(pos)
|
||||
local cpos = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
local dim = mcl_worlds.pos_to_dimension(cpos)
|
||||
if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- checks if player is undewater. This is needed in order to
|
||||
-- turn off weather particles generation.
|
||||
mcl_weather.is_underwater = function(player)
|
||||
local ppos = player:get_pos()
|
||||
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.
|
||||
mcl_weather.get_random_pos_by_player_look_dir = function(player)
|
||||
local look_dir = player:get_look_dir()
|
||||
local player_pos = player:get_pos()
|
||||
|
||||
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 + 10, player_pos.y + 15)
|
||||
return random_pos_x, random_pos_y, random_pos_z
|
||||
end
|
||||
|
||||
local t, wci = 0, mcl_weather.check_interval
|
||||
minetest.register_globalstep(function(dtime)
|
||||
t = t + dtime
|
||||
if t < wci then return end
|
||||
t = 0
|
||||
|
||||
if mcl_weather.end_time == nil then
|
||||
mcl_weather.end_time = mcl_weather.get_rand_end_time()
|
||||
end
|
||||
-- recalculate weather
|
||||
if mcl_weather.end_time <= minetest.get_gametime() then
|
||||
local changeWeather = minetest.settings:get_bool("mcl_doWeatherCycle")
|
||||
if changeWeather == nil then
|
||||
changeWeather = true
|
||||
end
|
||||
if changeWeather then
|
||||
mcl_weather.set_random_weather(mcl_weather.state, mcl_weather.reg_weathers[mcl_weather.state])
|
||||
else
|
||||
mcl_weather.end_time = mcl_weather.get_rand_end_time()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Sets random weather (which could be 'none' (no weather)).
|
||||
mcl_weather.set_random_weather = function(weather_name, weather_meta)
|
||||
if weather_meta == nil then return end
|
||||
local transitions = weather_meta.transitions
|
||||
local random_roll = math.random(0,100)
|
||||
local new_weather
|
||||
for v, weather in pairs(transitions) do
|
||||
if random_roll < v then
|
||||
new_weather = weather
|
||||
break
|
||||
end
|
||||
end
|
||||
if new_weather then
|
||||
mcl_weather.change_weather(new_weather)
|
||||
end
|
||||
end
|
||||
|
||||
-- Change weather to new_weather.
|
||||
-- * explicit_end_time is OPTIONAL. If specified, explicitly set the
|
||||
-- gametime (minetest.get_gametime) in which the weather ends.
|
||||
-- * changer is OPTIONAL, for logging purposes.
|
||||
mcl_weather.change_weather = function(new_weather, explicit_end_time, changer_name)
|
||||
local changer_name = changer_name or debug.getinfo(2).name.."()"
|
||||
|
||||
if (mcl_weather.reg_weathers ~= nil and mcl_weather.reg_weathers[new_weather] ~= nil) then
|
||||
if (mcl_weather.state ~= nil and mcl_weather.reg_weathers[mcl_weather.state] ~= nil) then
|
||||
mcl_weather.reg_weathers[mcl_weather.state].clear()
|
||||
end
|
||||
|
||||
local old_weather = mcl_weather.state
|
||||
|
||||
mcl_weather.state = new_weather
|
||||
|
||||
if old_weather == "none" then
|
||||
old_weather = "clear"
|
||||
end
|
||||
if new_weather == "none" then
|
||||
new_weather = "clear"
|
||||
end
|
||||
minetest.log("action", "[mcl_weather] " .. changer_name .. " changed the weather from " .. old_weather .. " to " .. new_weather)
|
||||
|
||||
local weather_meta = mcl_weather.reg_weathers[mcl_weather.state]
|
||||
if explicit_end_time then
|
||||
mcl_weather.end_time = explicit_end_time
|
||||
else
|
||||
mcl_weather.end_time = mcl_weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration)
|
||||
end
|
||||
mcl_weather.skycolor.update_sky_color()
|
||||
save_weather()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
mcl_weather.get_weather = function()
|
||||
return mcl_weather.state
|
||||
end
|
||||
|
||||
minetest.register_privilege("weather_manager", {
|
||||
description = S("Gives ability to control weather"),
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
-- Weather command definition. Set
|
||||
minetest.register_chatcommand("weather", {
|
||||
params = "(clear | rain | snow | thunder) [<duration>]",
|
||||
description = S("Changes the weather to the specified parameter."),
|
||||
privs = {weather_manager = true},
|
||||
func = function(name, param)
|
||||
if (param == "") then
|
||||
return false, S("Error: No weather specified.")
|
||||
end
|
||||
local new_weather, end_time
|
||||
local parse1, parse2 = string.match(param, "(%w+) ?(%d*)")
|
||||
if parse1 then
|
||||
if parse1 == "clear" then
|
||||
new_weather = "none"
|
||||
else
|
||||
new_weather = parse1
|
||||
end
|
||||
else
|
||||
return false, S("Error: Invalid parameters.")
|
||||
end
|
||||
if parse2 then
|
||||
if type(tonumber(parse2)) == "number" then
|
||||
local duration = tonumber(parse2)
|
||||
if duration < 1 then
|
||||
return false, S("Error: Duration can't be less than 1 second.")
|
||||
end
|
||||
end_time = minetest.get_gametime() + duration
|
||||
end
|
||||
end
|
||||
|
||||
local success = mcl_weather.change_weather(new_weather, end_time, name)
|
||||
if success then
|
||||
return true
|
||||
else
|
||||
return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("toggledownfall", {
|
||||
params = "",
|
||||
description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"),
|
||||
privs = {weather_manager = true},
|
||||
func = function(name, param)
|
||||
-- Currently rain/thunder/snow: Set weather to clear
|
||||
if mcl_weather.state ~= "none" then
|
||||
return mcl_weather.change_weather("none", nil, name)
|
||||
|
||||
-- Currently clear: Set weather randomly to rain/thunder/snow
|
||||
else
|
||||
local new = { "rain", "thunder", "snow" }
|
||||
local r = math.random(1, #new)
|
||||
return mcl_weather.change_weather(new[r], nil, name)
|
||||
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.settings:get_bool("weather_allow_abm")
|
||||
if weather_allow_abm ~= nil and weather_allow_abm == false then
|
||||
mcl_weather.allow_abm = false
|
||||
end
|
||||
|
||||
|
||||
local load_weather = function()
|
||||
local weather = storage:get_string("mcl_weather_state")
|
||||
if weather and weather ~= "" then
|
||||
mcl_weather.state = weather
|
||||
mcl_weather.end_time = storage:get_int("mcl_weather_end_time")
|
||||
mcl_weather.change_weather(weather, mcl_weather.end_time)
|
||||
if type(mcl_weather.end_time) ~= "number" then
|
||||
-- Fallback in case of corrupted end time
|
||||
mcl_weather.end_time = mcl_weather.min_duration
|
||||
end
|
||||
minetest.log("action", "[mcl_weather] Weather restored.")
|
||||
else
|
||||
minetest.log("action", "[mcl_weather] No weather data found. Starting with clear weather.")
|
||||
end
|
||||
end
|
||||
|
||||
load_weather()
|
|
@ -3,13 +3,106 @@ local N = function(s) return s end
|
|||
|
||||
local function get_tool_name(item)
|
||||
local name = item:get_meta():get_string("name")
|
||||
if name ~= "" then
|
||||
return name
|
||||
if name == "" then
|
||||
local def = item:get_definition()
|
||||
name=def._tt_original_description or def.description
|
||||
end
|
||||
local def = item:get_definition()
|
||||
return def._tt_original_description or def.description
|
||||
local sanitized_name, substitution_count = name:gsub("[\r\n]"," ")
|
||||
return sanitized_name
|
||||
end
|
||||
|
||||
local test_tool_1a = {
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return "foo 1a"
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_1a) == "foo 1a" )
|
||||
|
||||
local test_tool_1b = {
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return "bar\rbaz\n1b"
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_1b) == "bar baz 1b" )
|
||||
|
||||
local test_tool_2a = {
|
||||
get_definition = function()
|
||||
return {
|
||||
_tt_original_description = "foo 2a"
|
||||
}
|
||||
end,
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return ""
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_2a) == "foo 2a" )
|
||||
|
||||
local test_tool_2b = {
|
||||
get_definition = function()
|
||||
return {
|
||||
_tt_original_description = "bar\rbaz\n2b"
|
||||
}
|
||||
end,
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return ""
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_2b) == "bar baz 2b" )
|
||||
|
||||
local test_tool_3a = {
|
||||
get_definition = function()
|
||||
return {
|
||||
description = "foo 3a"
|
||||
}
|
||||
end,
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return ""
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_3a) == "foo 3a" )
|
||||
|
||||
local test_tool_3b = {
|
||||
get_definition = function()
|
||||
return {
|
||||
description = "bar\rbaz\n3b"
|
||||
}
|
||||
end,
|
||||
get_meta = function()
|
||||
return {
|
||||
get_string = function()
|
||||
return ""
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
assert( get_tool_name(test_tool_3b) == "bar baz 3b" )
|
||||
|
||||
mcl_death_messages = {}
|
||||
|
||||
-- Death messages
|
||||
|
|
|
@ -387,13 +387,15 @@ for colorid, colortab in pairs(mcl_banners.colors) do
|
|||
-- redraw the pattern textures as low-resolution pixel
|
||||
-- art and use that instead.
|
||||
|
||||
local layer = "(([combine:20x40:-2,-2="..pattern.."^[resize:16x24^[colorize:"..color..":"..layer_ratio.."))"
|
||||
local layer = "([combine:20x40:-2,-2=" .. pattern .. "^[resize:16x24^[colorize:" .. color .. ":" .. layer_ratio .. ")"
|
||||
local mask = "([combine:20x40:-2,-2=" .. pattern .. "^[resize:16x24" .. ")"
|
||||
|
||||
function escape(text)
|
||||
return text:gsub("%^", "\\%^"):gsub(":", "\\:") -- :gsub("%(", "\\%("):gsub("%)", "\\%)")
|
||||
end
|
||||
|
||||
finished_banner = "[combine:32x32:0,0=" .. escape(base) .. ":8,4=" .. escape(layer)
|
||||
local layer_masked = layer .. "^[mask:" .. escape(mask)
|
||||
finished_banner = "[combine:32x32:0,0=" .. escape(base) .. ":8,4=" .. escape(layer_masked)
|
||||
end
|
||||
|
||||
inv = finished_banner
|
||||
|
|
|
@ -1,6 +1,41 @@
|
|||
local S = minetest.get_translator("mcl_chests")
|
||||
local mod_doc = minetest.get_modpath("doc")
|
||||
|
||||
-- Christmas chest setup
|
||||
local it_is_christmas = false
|
||||
local date = os.date("*t")
|
||||
if (
|
||||
date.month == 12 and (
|
||||
date.day == 24 or
|
||||
date.day == 25 or
|
||||
date.day == 26
|
||||
)
|
||||
) then
|
||||
it_is_christmas = true
|
||||
end
|
||||
|
||||
local tiles_chest_normal_small = {"mcl_chests_normal.png"}
|
||||
local tiles_chest_normal_double = {"mcl_chests_normal_double.png"}
|
||||
|
||||
if it_is_christmas then
|
||||
tiles_chest_normal_small = {"mcl_chests_normal_present.png^mcl_chests_noise.png"}
|
||||
tiles_chest_normal_double = {"mcl_chests_normal_double_present.png^mcl_chests_noise_double.png"}
|
||||
end
|
||||
|
||||
local tiles_chest_trapped_small = {"mcl_chests_trapped.png"}
|
||||
local tiles_chest_trapped_double = {"mcl_chests_trapped_double.png"}
|
||||
|
||||
if it_is_christmas then
|
||||
tiles_chest_trapped_small = {"mcl_chests_trapped_present.png^mcl_chests_noise.png"}
|
||||
tiles_chest_trapped_double = {"mcl_chests_trapped_double_present.png^mcl_chests_noise_double.png"}
|
||||
end
|
||||
|
||||
local tiles_chest_ender_small = {"mcl_chests_ender.png"}
|
||||
|
||||
if it_is_christmas then
|
||||
tiles_chest_ender_small = {"mcl_chests_ender_present.png^mcl_chests_noise.png"}
|
||||
end
|
||||
|
||||
-- Chest Entity
|
||||
local animate_chests = (minetest.settings:get_bool("animated_chests") ~= false)
|
||||
local entity_animations = {
|
||||
|
@ -152,7 +187,10 @@ if minetest.get_modpath("screwdriver") then
|
|||
local nodename = node.name
|
||||
local nodedef = minetest.registered_nodes[nodename]
|
||||
local dir = minetest.facedir_to_dir(new_param2)
|
||||
find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false, nodedef._chest_entity_sound, nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir)
|
||||
|
||||
if animate_chests then
|
||||
find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false, nodedef._chest_entity_sound, nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir)
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
@ -181,6 +219,10 @@ local player_chest_open = function(player, pos, node_name, textures, param2, dou
|
|||
local dir = minetest.facedir_to_dir(param2)
|
||||
local blocked = not shulker and (back_is_blocked(pos, dir) or double and back_is_blocked(mcl_util.get_double_container_neighbor_pos(pos, param2, node_name:sub(-4)), dir))
|
||||
find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", dir):open(name, blocked)
|
||||
else
|
||||
minetest.sound_play(sound .. "_open", {
|
||||
pos = pos,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -212,11 +254,14 @@ local chest_update_after_close = function(pos)
|
|||
|
||||
if node.name == "mcl_chests:trapped_chest_on_small" then
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_small", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small")
|
||||
|
||||
if animate_chests then
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_small", tiles_chest_trapped_small, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small")
|
||||
end
|
||||
mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
|
||||
elseif node.name == "mcl_chests:trapped_chest_on_left" then
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
|
||||
|
||||
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
|
||||
|
@ -228,7 +273,7 @@ local chest_update_after_close = function(pos)
|
|||
|
||||
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||
minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2})
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules)
|
||||
end
|
||||
end
|
||||
|
@ -242,6 +287,10 @@ local player_chest_close = function(player)
|
|||
end
|
||||
if animate_chests then
|
||||
find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, open_chest.double, open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name)
|
||||
else
|
||||
minetest.sound_play(open_chest.sound .. "_close", {
|
||||
pos = open_chest.pos,
|
||||
})
|
||||
end
|
||||
chest_update_after_close(open_chest.pos)
|
||||
|
||||
|
@ -380,12 +429,21 @@ minetest.register_node(small_name, {
|
|||
_doc_items_longdesc = longdesc,
|
||||
_doc_items_usagehelp = usagehelp,
|
||||
_doc_items_hidden = hidden,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
drawtype = animate_chests and "nodebox" or "mesh",
|
||||
mesh = not animate_chests and "mcl_chests_chest.obj" or nil,
|
||||
node_box = animate_chests and {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
} or nil,
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
tiles = animate_chests and {"mcl_chests_blank.png"} or small_textures,
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
_chest_entity_textures = small_textures,
|
||||
_chest_entity_sound = "default_chest",
|
||||
|
@ -435,7 +493,10 @@ minetest.register_node(small_name, {
|
|||
minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_right", param2 = param2 })
|
||||
else
|
||||
minetest.swap_node(pos, { name = "mcl_chests:"..canonical_basename.."_small", param2 = param2 })
|
||||
create_entity(pos, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest")
|
||||
|
||||
if animate_chests then
|
||||
create_entity(pos, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest")
|
||||
end
|
||||
end
|
||||
end,
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
|
@ -823,8 +884,8 @@ register_chest("chest",
|
|||
chestusage,
|
||||
S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"),
|
||||
{
|
||||
small = {"mcl_chests_normal.png"},
|
||||
double = {"mcl_chests_normal_double.png"},
|
||||
small = tiles_chest_normal_small,
|
||||
double = tiles_chest_normal_double,
|
||||
inv = {"default_chest_top.png", "mcl_chests_chest_bottom.png",
|
||||
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
|
||||
"mcl_chests_chest_back.png", "default_chest_front.png"},
|
||||
|
@ -839,8 +900,8 @@ register_chest("chest",
|
|||
)
|
||||
|
||||
local traptiles = {
|
||||
small = {"mcl_chests_trapped.png"},
|
||||
double = {"mcl_chests_trapped_double.png"},
|
||||
small = tiles_chest_trapped_small,
|
||||
double = tiles_chest_trapped_double,
|
||||
inv = {"mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png",
|
||||
"mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png",
|
||||
"mcl_chests_chest_trapped_back.png", "mcl_chests_chest_trapped_front.png"},
|
||||
|
@ -865,7 +926,9 @@ register_chest("trapped_chest",
|
|||
}},
|
||||
function(pos, node, clicker)
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_small", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small")
|
||||
if animate_chests then
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", tiles_chest_trapped_small, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small")
|
||||
end
|
||||
mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
|
||||
end,
|
||||
function(pos, node, clicker)
|
||||
|
@ -873,7 +936,7 @@ register_chest("trapped_chest",
|
|||
meta:set_int("players", 1)
|
||||
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left")
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left")
|
||||
mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
|
||||
|
||||
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
|
||||
|
@ -887,7 +950,7 @@ register_chest("trapped_chest",
|
|||
mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
|
||||
|
||||
minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2})
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left")
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left")
|
||||
mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules)
|
||||
end
|
||||
)
|
||||
|
@ -908,13 +971,15 @@ local function close_if_trapped_chest(pos, player)
|
|||
|
||||
if node.name == "mcl_chests:trapped_chest_on_small" then
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_small", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small")
|
||||
if animate_chests then
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_small", tiles_chest_trapped_small, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small")
|
||||
end
|
||||
mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
|
||||
|
||||
player_chest_close(player)
|
||||
elseif node.name == "mcl_chests:trapped_chest_on_left" then
|
||||
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2})
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
find_or_create_entity(pos, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
|
||||
|
||||
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
|
||||
|
@ -928,7 +993,7 @@ local function close_if_trapped_chest(pos, player)
|
|||
|
||||
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||
minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2})
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", tiles_chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
|
||||
mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules)
|
||||
|
||||
player_chest_close(player)
|
||||
|
@ -976,7 +1041,7 @@ minetest.register_node("mcl_chests:ender_chest", {
|
|||
_doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."),
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_chests_chest.obj",
|
||||
tiles = {"mcl_chests_ender.png"},
|
||||
tiles = tiles_chest_ender_small,
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
|
@ -1002,22 +1067,38 @@ local formspec_ender_chest = "size[9,8.75]"..
|
|||
"listring[current_player;enderchest]"..
|
||||
"listring[current_player;main]"
|
||||
|
||||
minetest.register_chatcommand("enderchest", {
|
||||
description = S("Show ender chest inventory formspec."),
|
||||
privs = { debug = true },
|
||||
func = function(name, params)
|
||||
minetest.show_formspec(name, "enderchest:enderchest", formspec_ender_chest)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("mcl_chests:ender_chest_small", {
|
||||
description = S("Ender Chest"),
|
||||
_tt_help = S("27 interdimensional inventory slots") .. "\n" .. S("Put items inside, retrieve them from any ender chest"),
|
||||
_doc_items_longdesc = S("Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."),
|
||||
_doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
drawtype = animate_chests and "nodebox" or "mesh",
|
||||
mesh = not animate_chests and "mcl_chests_chest.obj" or nil,
|
||||
node_box = animate_chests and {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375},
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
} or nil,
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
_chest_entity_textures = {"mcl_chests_ender.png"},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
tiles = animate_chests and {"mcl_chests_blank.png"} or tiles_chest_ender_small,
|
||||
_chest_entity_textures = tiles_chest_ender_small,
|
||||
_chest_entity_sound = "mcl_chests_enderchest",
|
||||
_chest_entity_mesh = "mcl_chests_chest",
|
||||
_chest_entity_animation_type = "chest",
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
--[[{"mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png",
|
||||
"mcl_chests_ender_chest_right.png", "mcl_chests_ender_chest_left.png",
|
||||
|
@ -1034,10 +1115,13 @@ minetest.register_node("mcl_chests:ender_chest_small", {
|
|||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", formspec_ender_chest)
|
||||
create_entity(pos, "mcl_chests:ender_chest_small", {"mcl_chests_ender.png"}, minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest")
|
||||
|
||||
if animate_chests then
|
||||
create_entity(pos, "mcl_chests:ender_chest_small", tiles_chest_ender_small, minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest")
|
||||
end
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", {"mcl_chests_ender.png"}, node.param2, false, "mcl_chests_enderchest", "mcl_chests_chest")
|
||||
player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", tiles_chest_ender_small, node.param2, false, "mcl_chests_enderchest", "mcl_chests_chest")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields.quit then
|
||||
|
@ -1055,6 +1139,20 @@ minetest.register_on_joinplayer(function(player)
|
|||
inv:set_size("enderchest", 9*3)
|
||||
end)
|
||||
|
||||
minetest.register_allow_player_inventory_action(function(player, action, inv, info)
|
||||
if inv:get_location().type == "player" and (
|
||||
action == "move" and (info.from_list == "enderchest" or info.to_list == "enderchest")
|
||||
or action == "put" and info.listname == "enderchest"
|
||||
or action == "take" and info.listname == "enderchest"
|
||||
) then
|
||||
local def = player:get_wielded_item():get_definition()
|
||||
|
||||
if not minetest.find_node_near(player:get_pos(), def and def.range or ItemStack():get_definition().range, "mcl_chests:ender_chest_small", true) then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'mcl_chests:ender_chest',
|
||||
recipe = {
|
||||
|
@ -1215,8 +1313,9 @@ for color, desc in pairs(boxtypes) do
|
|||
_doc_items_entry_name = entry_name,
|
||||
_doc_items_longdesc = longdesc,
|
||||
_doc_items_usagehelp = usagehelp,
|
||||
drawtype = "nodebox",
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
drawtype = animate_chests and "nodebox" or "mesh",
|
||||
mesh = not animate_chests and "mcl_chests_shulker.obj" or nil,
|
||||
tiles = animate_chests and {"mcl_chests_blank.png"} or {mob_texture},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
_chest_entity_textures = {mob_texture},
|
||||
_chest_entity_sound = "mcl_chests_shulker",
|
||||
|
@ -1237,7 +1336,10 @@ for color, desc in pairs(boxtypes) do
|
|||
meta:set_string("formspec", formspec_shulker_box(nil))
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 9*3)
|
||||
create_entity(pos, small_name, {mob_texture}, minetest.get_node(pos).param2, false, "mcl_chests_shulker", "mcl_chests_shulker", "shulker")
|
||||
|
||||
if animate_chests then
|
||||
create_entity(pos, small_name, {mob_texture}, minetest.get_node(pos).param2, false, "mcl_chests_shulker", "mcl_chests_shulker", "shulker")
|
||||
end
|
||||
end,
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
local nmeta = minetest.get_meta(pos)
|
||||
|
@ -1356,6 +1458,11 @@ local function select_and_spawn_entity(pos, node)
|
|||
local node_name = node.name
|
||||
local node_def = minetest.registered_nodes[node_name]
|
||||
local double_chest = minetest.get_item_group(node_name, "double_chest") > 0
|
||||
|
||||
if not animate_chests and not double_chest then
|
||||
return
|
||||
end
|
||||
|
||||
find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type)
|
||||
end
|
||||
|
||||
|
|
After Width: | Height: | Size: 286 B |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 307 B |
After Width: | Height: | Size: 285 B |
After Width: | Height: | Size: 307 B |
After Width: | Height: | Size: 286 B |
|
@ -382,8 +382,14 @@ minetest.register_craft({
|
|||
}
|
||||
})
|
||||
|
||||
-- TODO: Add crafting recipe: 9 ice → 1 packed ice
|
||||
-- Add it when silk touch tools work.
|
||||
minetest.register_craft({
|
||||
output = 'mcl_core:packed_ice 1',
|
||||
recipe = {
|
||||
{'mcl_core:ice', 'mcl_core:ice', 'mcl_core:ice'},
|
||||
{'mcl_core:ice', 'mcl_core:ice', 'mcl_core:ice'},
|
||||
{'mcl_core:ice', 'mcl_core:ice', 'mcl_core:ice'},
|
||||
}
|
||||
})
|
||||
|
||||
--
|
||||
-- Crafting (tool repair)
|
||||
|
|
|
@ -165,7 +165,7 @@ local function eat_gapple(itemstack, placer, pointed_thing)
|
|||
mcl_potions.fire_resistance_func(placer, 1, 300)
|
||||
mcl_potions.leaping_func(placer, 1, 300)
|
||||
end
|
||||
mcl_potions.swiftness_func(placer, absorbtion_factor, 120) -- TODO: Absorbtion
|
||||
-- TODO: Absorbtion
|
||||
mcl_potions.regeneration_func(placer, 2, regen_duration)
|
||||
return gapple_hunger_restore(itemstack, placer, pointed_thing)
|
||||
end
|
||||
|
|
|
@ -388,12 +388,11 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
{ x=0, y=0, z=-1 },
|
||||
{ x=0, y=0, z=1 },
|
||||
}
|
||||
local floorpos, floor
|
||||
for n=#neighbors, 1, -1 do
|
||||
local offset = neighbors[n]
|
||||
local blockpos = vector.add(stempos, offset)
|
||||
floorpos = { x=blockpos.x, y=blockpos.y-1, z=blockpos.z }
|
||||
floor = minetest.get_node(floorpos)
|
||||
local floorpos = { x=blockpos.x, y=blockpos.y-1, z=blockpos.z }
|
||||
local floor = minetest.get_node(floorpos)
|
||||
local block = minetest.get_node(blockpos)
|
||||
local soilgroup = minetest.get_item_group(floor.name, "soil")
|
||||
if not ((minetest.get_item_group(floor.name, "grass_block") == 1 or floor.name=="mcl_core:dirt" or soilgroup == 2 or soilgroup == 3) and block.name == "air") then
|
||||
|
@ -407,6 +406,8 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
local r = math.random(1, #neighbors)
|
||||
local offset = neighbors[r]
|
||||
local blockpos = vector.add(stempos, offset)
|
||||
local floorpos = { x=blockpos.x, y=blockpos.y-1, z=blockpos.z }
|
||||
local floor = minetest.get_node(floorpos)
|
||||
local p2
|
||||
if offset.x == 1 then
|
||||
minetest.set_node(stempos, {name=connected_stem_names[1]})
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
local clamp = function(value, min, max)
|
||||
assert( min < max )
|
||||
if value < min then
|
||||
return min
|
||||
end
|
||||
if value > max then
|
||||
return max
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
assert( clamp(000, -100, 100) == 000 )
|
||||
assert( clamp(999, -100, 100) == 100 )
|
||||
assert( clamp(999, -999, 999) == 999 )
|
||||
assert( clamp(998, 999, 1999) == 999 )
|
||||
assert( clamp(999, 999, 1999) == 999 )
|
||||
|
||||
local clamp_s16 = function(value)
|
||||
-- seems minetest hangs on -32768 and 32767
|
||||
return clamp(value, -32767, 32766)
|
||||
end
|
||||
|
||||
assert( clamp_s16(000000) == 000000 )
|
||||
assert( clamp_s16(000001) == 000001 )
|
||||
assert( clamp_s16(000010) == 000010 )
|
||||
assert( clamp_s16(000100) == 000100 )
|
||||
assert( clamp_s16(001000) == 001000 )
|
||||
assert( clamp_s16(010000) == 010000 )
|
||||
assert( clamp_s16(100000) == 032766 )
|
||||
|
||||
assert( clamp_s16(-00000) == -00000 )
|
||||
assert( clamp_s16(-00009) == -00009 )
|
||||
assert( clamp_s16(-00099) == -00099 )
|
||||
assert( clamp_s16(-00999) == -00999 )
|
||||
assert( clamp_s16(-09999) == -09999 )
|
||||
assert( clamp_s16(-99999) == -32767 )
|
||||
|
||||
local minetest_find_nodes_in_area = minetest.find_nodes_in_area
|
||||
minetest.find_nodes_in_area = function(minp, maxp, ...)
|
||||
if
|
||||
minp.x >= 32767 or minp.x <= -32768 or
|
||||
minp.y >= 32767 or minp.y <= -32768 or
|
||||
minp.z >= 32767 or minp.z <= -32768 or
|
||||
maxp.x >= 32767 or maxp.x <= -32768 or
|
||||
maxp.y >= 32767 or maxp.y <= -32768 or
|
||||
maxp.z >= 32767 or maxp.z <= -32768
|
||||
then
|
||||
minetest.log(
|
||||
"warning",
|
||||
"find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " ..
|
||||
"minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " ..
|
||||
"maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } "
|
||||
)
|
||||
return minetest_find_nodes_in_area(
|
||||
{ x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) },
|
||||
{ x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) },
|
||||
...
|
||||
)
|
||||
else
|
||||
return minetest_find_nodes_in_area(
|
||||
minp,
|
||||
maxp,
|
||||
...
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
deep_compare = function(a, b)
|
||||
local type_a = type(a)
|
||||
local type_b = type(b)
|
||||
if type_a ~= type_b then
|
||||
return false
|
||||
end
|
||||
if type_a ~= "table" and type_b ~= "table" then
|
||||
return a == b
|
||||
end
|
||||
for key_a, value_a in pairs(a) do
|
||||
local value_b = b[key_a]
|
||||
if not deep_compare(value_a, value_b) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
for key_b, value_b in pairs(b) do
|
||||
local value_a = a[key_b]
|
||||
if not deep_compare(value_b, value_a) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
1,
|
||||
1.0
|
||||
) == true
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
true,
|
||||
"true"
|
||||
) == false
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
{ a=1, b=-2, c=3.4 },
|
||||
{ a=1, b=-2, c=3.4 }
|
||||
) == true
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
{ a={ 1, 2, 3 }, b="foo", c=false },
|
||||
{ a={ 1, 2, 3 }, b="foo", c=false }
|
||||
) == true
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
{ a={ 1, 2, 3 }, b="foo", c=false },
|
||||
{ a={ 4, 5, 6 }, b="foo", c=false }
|
||||
) == false
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
{ a={ 1, 2, 3 }, b={ c=false } },
|
||||
{ a={ 1, 2, 3 }, b={ c=false } }
|
||||
) == true
|
||||
)
|
||||
|
||||
assert(
|
||||
deep_compare(
|
||||
{ a={ 1, 2, 3 }, b={ } },
|
||||
{ a={ 1, 2, 3 }, b={ c=false } }
|
||||
) == false
|
||||
)
|
||||
|
||||
local test_minetest_find_nodes_in_area_implementation_equivalence = function()
|
||||
-- If any assertion in this test function fails, the wrapper
|
||||
-- for minetest.find_nodes_in_area() does not behave like the
|
||||
-- original function. If you are reading the code because your
|
||||
-- server crashed, please inform the Mineclonia developers.
|
||||
local fun_1 = minetest_find_nodes_in_area
|
||||
local fun_2 = minetest.find_nodes_in_area
|
||||
for x = -31000, 31000, 15500 do
|
||||
for y = -31000, 31000, 15500 do
|
||||
for z = -31000, 31000, 15500 do
|
||||
for d = 1, 9, 3 do
|
||||
local minp = { x=x, y=y, z=z }
|
||||
local maxp = { x=x+d, y=y+d, z=z+d }
|
||||
minetest.emerge_area(
|
||||
minp,
|
||||
maxp,
|
||||
function(blockpos, action, calls_remaining)
|
||||
local npos_1, nnum_1 = fun_1(
|
||||
minp,
|
||||
maxp,
|
||||
{ "air", "ignore" }
|
||||
)
|
||||
local npos_2, nnum_2 = fun_2(
|
||||
minp,
|
||||
maxp,
|
||||
{ "air", "ignore" }
|
||||
)
|
||||
assert(
|
||||
deep_compare(
|
||||
npos_1,
|
||||
npos_2
|
||||
) == true
|
||||
)
|
||||
assert(
|
||||
deep_compare(
|
||||
nnum_1,
|
||||
nnum_2
|
||||
) == true
|
||||
)
|
||||
local ntab_1 = fun_1(
|
||||
minp,
|
||||
maxp,
|
||||
{ "air", "ignore" },
|
||||
true
|
||||
)
|
||||
local ntab_2 = fun_2(
|
||||
minp,
|
||||
maxp,
|
||||
{ "air", "ignore" },
|
||||
true
|
||||
)
|
||||
assert(
|
||||
deep_compare(
|
||||
ntab_1,
|
||||
ntab_2
|
||||
) == true
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after( 0, test_minetest_find_nodes_in_area_implementation_equivalence )
|
|
@ -0,0 +1,121 @@
|
|||
local test_minetest_find_nodes_in_area_can_count = function(dtime)
|
||||
-- This function tests that minetest.find_nodes_in_area() can
|
||||
-- count nodes correctly. If it fails, the engine can not be
|
||||
-- trusted to actually count how many nodes of a given type
|
||||
-- are in a given volume. Yes, *it* is bad if that happens.
|
||||
--
|
||||
-- If you are looking at this function because it executes at
|
||||
-- startup and crashes the game, by far the most stupid thing
|
||||
-- you could do is disabling it. Only an absolute moron would
|
||||
-- disable tests that ensure basic functionality still works.
|
||||
--
|
||||
-- Experience has taught me that such warnings are mostly not
|
||||
-- taken seriously by both Minetest mod & core developers. As
|
||||
-- there are very few situations in which someone would read
|
||||
-- the code of the function without a crash, you are probably
|
||||
-- asking yourself how bad it can be. Surely you will want an
|
||||
-- example of what will break if this test breaks. The answer
|
||||
-- to this simple question is equally simple and consists of a
|
||||
-- heartfelt “What the fuck did you say, you stupid fuckwad?”.
|
||||
--
|
||||
-- Alrighty then, let's get it on …
|
||||
|
||||
local pos = { x=30999, y=30999, z=30999 }
|
||||
-- You think there is nothing there? Well, here is the thing:
|
||||
-- With standard settings you can only see map until x=30927,
|
||||
-- although the renderer can actually render up to x=31007 if
|
||||
-- you configure it to. Any statements given by minetest core
|
||||
-- devs that contradict the above assertion are probably lies.
|
||||
--
|
||||
-- In any way, this area should be so far out that no one has
|
||||
-- built here … yet. Now that you know it is possible, I know
|
||||
-- you want to. How though? I suggest to figure the technique
|
||||
-- out yourself, then go on and build invisible lag machines.
|
||||
|
||||
local radius = 3
|
||||
local minp = vector.subtract(pos, radius)
|
||||
local maxp = vector.add(pos, radius)
|
||||
local nodename = "air"
|
||||
local c_nodename = minetest.get_content_id(nodename)
|
||||
|
||||
-- Why not use minetest.set_node() here? Well, some mods do
|
||||
-- trigger on every placement of a node in the entire map …
|
||||
-- and we do not want to crash those mods in this test case.
|
||||
-- (Voxelmanip does not trigger callbacks – so all is well.)
|
||||
--
|
||||
-- And now for a funny story: I initially copied the following
|
||||
-- code from the Minetest developer wiki. Can you spot a typo?
|
||||
-- <https://dev.minetest.net/index.php?title=minetest.get_content_id&action=edit>
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(
|
||||
minp,
|
||||
maxp
|
||||
)
|
||||
local area = VoxelArea:new({
|
||||
MinEdge=emin,
|
||||
MaxEdge=emax
|
||||
})
|
||||
local data = vm:get_data()
|
||||
for z = minp.z, maxp.z do
|
||||
for y = minp.y, maxp.y do
|
||||
local vi = area:index(minp.x, y, z)
|
||||
for x = minp.x, maxp.y do
|
||||
data[vi] = c_nodename
|
||||
vi = vi + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
local npos, nnum = minetest.find_nodes_in_area(
|
||||
minp,
|
||||
maxp,
|
||||
{ nodename }
|
||||
)
|
||||
local nodes_expected = math.pow( 1 + (2 * radius), 3 )
|
||||
local nodes_counted = nnum[nodename]
|
||||
local nodes_difference = nodes_expected - nodes_counted
|
||||
-- Originally, there was an assertion here that made the game
|
||||
-- crash at startup if Minetest forgot how to count. This was
|
||||
-- originally intended to avoid buggy engine releases, but it
|
||||
-- mostly made people upset and hindered debugging. Also, the
|
||||
-- assertion contained no error message hinting at the reason
|
||||
-- for the crash, making it exceptionally user-unfriendly. It
|
||||
-- follows that a game or mod should only assert on behaviour
|
||||
-- of the Lua code, not the underlying implementation, unless
|
||||
-- engine bugs are bad enough to permanently corrupt a world.
|
||||
if ( 0 ~= nodes_difference ) then
|
||||
minetest.debug(
|
||||
"minetest.find_nodes_in_area() failed to find " ..
|
||||
nodes_difference .. " nodes that were placed. " ..
|
||||
"Downgrading to Minetest 5.4.1 might fix this."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after( 0, test_minetest_find_nodes_in_area_can_count )
|
||||
|
||||
local test_minetest_find_nodes_in_area_crash = function(dtime)
|
||||
-- And now for our feature presentation, where we call the
|
||||
-- function “minetest.find_nodes_in_area()” with a position
|
||||
-- out of bounds! Will it crash? Who knows‽ If it does, the
|
||||
-- workaround is not working though, so it should be patched.
|
||||
|
||||
local pos = { x=32767, y=32767, z=32767 }
|
||||
-- Note that not all out of bounds values actually crash the
|
||||
-- function minetest.find_nodes_in_area()“. In fact, the vast
|
||||
-- majority of out of bounds values do not crash the function.
|
||||
|
||||
local radius = 3
|
||||
local minp = vector.subtract(pos, radius)
|
||||
local maxp = vector.add(pos, radius)
|
||||
local nodename = "air"
|
||||
local npos, nnum = minetest.find_nodes_in_area(
|
||||
minp,
|
||||
maxp,
|
||||
{ nodename }
|
||||
)
|
||||
-- That's all, folks!
|
||||
end
|
||||
|
||||
minetest.after( 0, test_minetest_find_nodes_in_area_crash )
|
|
@ -19,8 +19,114 @@ end
|
|||
|
||||
local pitch, name, node_stand, node_stand_below, node_head, node_feet, pos
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
local function roundN(n, d)
|
||||
if type(n) ~= "number" then return n end
|
||||
local m = 10^d
|
||||
return math.floor(n * m + 0.5) / m
|
||||
end
|
||||
|
||||
local function close_enough(a,b)
|
||||
local rt=true
|
||||
if type(a) == "table" and type(b) == "table" then
|
||||
for k,v in pairs(a) do
|
||||
if roundN(v,2) ~= roundN(b[k],2) then
|
||||
rt=false
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
rt = roundN(a,2) == roundN(b,2)
|
||||
end
|
||||
return rt
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function props_changed(props,oldprops)
|
||||
local changed=false
|
||||
local p={}
|
||||
for k,v in pairs(props) do
|
||||
if not close_enough(v,oldprops[k]) then
|
||||
p[k]=v
|
||||
changed=true
|
||||
end
|
||||
end
|
||||
return changed,p
|
||||
end
|
||||
|
||||
--test if assert works
|
||||
assert(true)
|
||||
assert(not false)
|
||||
|
||||
--test data for == and ~=
|
||||
local test_equal1=42
|
||||
local test_equal2=42.0
|
||||
local test_equal3=42.1
|
||||
|
||||
assert(test_equal1==test_equal1)
|
||||
assert(test_equal1==test_equal2)
|
||||
assert(test_equal1~=test_equal3)
|
||||
|
||||
--testdata for roundN
|
||||
local test_round1=15
|
||||
local test_round2=15.00199999999
|
||||
local test_round3=15.00111111
|
||||
local test_round4=15.00999999
|
||||
|
||||
assert(roundN(test_round1,2)==roundN(test_round1,2)) --test again if basic equality works because wth not
|
||||
assert(roundN(test_round1,2)==roundN(test_round2,2))
|
||||
assert(roundN(test_round1,2)==roundN(test_round3,2))
|
||||
assert(roundN(test_round1,2)~=roundN(test_round4,2))
|
||||
|
||||
|
||||
-- tests for close_enough
|
||||
local test_cb = {-0.35,0,-0.35,0.35,0.8,0.35} --collisionboxes
|
||||
local test_cb_close = {-0.351213,0,-0.35,0.35,0.8,0.351212}
|
||||
local test_cb_diff = {-0.35,0,-1.35,0.35,0.8,0.35}
|
||||
|
||||
local test_eh = 1.65 --eye height
|
||||
local test_eh_close = 1.65123123
|
||||
local test_eh_diff = 1.35
|
||||
|
||||
local test_nt = { r = 225, b = 225, a = 225, g = 225 } --nametag
|
||||
local test_nt_diff = { r = 225, b = 225, a = 0, g = 225 }
|
||||
|
||||
assert(close_enough(test_cb,test_cb_close))
|
||||
assert(not close_enough(test_cb,test_cb_diff))
|
||||
|
||||
assert(close_enough(test_eh,test_eh_close))
|
||||
assert(not close_enough(test_eh,test_eh_diff))
|
||||
|
||||
assert(not close_enough(test_nt,test_nt_diff)) --no floats involved here
|
||||
|
||||
--tests for props_changed
|
||||
local test_properties_set1={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
||||
local test_properties_set2={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
||||
|
||||
local test_p1,p=props_changed(test_properties_set1,test_properties_set1)
|
||||
local test_p2,p=props_changed(test_properties_set1,test_properties_set2)
|
||||
|
||||
assert(not test_p1)
|
||||
assert(test_p2)
|
||||
|
||||
-- we still don't really know if lua is lying to us! but at least everything *seems* to be ok
|
||||
|
||||
local function set_properties_conditional(player,props)
|
||||
local changed,p=props_changed(props,player:get_properties())
|
||||
if changed then
|
||||
player:set_properties(p)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_bone_position_conditional(player,b,p,r) --bone,position,rotation
|
||||
local oldp,oldr=player:get_bone_position(b)
|
||||
if vector.equals(vector.round(oldp),vector.round(p)) and vector.equals(vector.round(oldr),vector.round(r)) then
|
||||
return
|
||||
end
|
||||
player:set_bone_position(b,p,r)
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
time = time + dtime
|
||||
|
||||
-- Update jump status immediately since we need this info in real time.
|
||||
|
@ -45,55 +151,55 @@ minetest.register_globalstep(function(dtime)
|
|||
|
||||
-- controls right and left arms pitch when shooting a bow or punching
|
||||
if string.find(player:get_wielded_item():get_name(), "mcl_bows:bow") and controls.RMB and not controls.LMB and not controls.up and not controls.down and not controls.left and not controls.right then
|
||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch+90,-30,pitch * -1 * .35))
|
||||
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3.5,5.785,0), vector.new(pitch+90,43,pitch * .35))
|
||||
set_bone_position_conditional(player,"Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch+90,-30,pitch * -1 * .35))
|
||||
set_bone_position_conditional(player,"Arm_Left_Pitch_Control", vector.new(3.5,5.785,0), vector.new(pitch+90,43,pitch * .35))
|
||||
elseif controls.LMB and player:get_attach() == nil then
|
||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0))
|
||||
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
||||
set_bone_position_conditional(player,"Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0))
|
||||
set_bone_position_conditional(player,"Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
||||
else
|
||||
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(0,0,0))
|
||||
set_bone_position_conditional(player,"Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
||||
set_bone_position_conditional(player,"Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(0,0,0))
|
||||
end
|
||||
|
||||
if controls.sneak and player:get_attach() == nil then
|
||||
-- controls head pitch when sneaking
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch+36,0,0))
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch+36,0,0))
|
||||
-- sets eye height, and nametag color accordingly
|
||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 0, g = 225 }})
|
||||
set_properties_conditional(player,{collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 0, g = 225 }})
|
||||
-- sneaking body conrols
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
elseif minetest.get_item_group(mcl_playerinfo[name].node_head, "water") ~= 0 and player:get_attach() == nil and mcl_sprint.is_sprinting(name) == true then
|
||||
-- set head pitch and yaw when swimming
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch+90-degrees(dir_to_pitch(player_velocity)),yaw - player_vel_yaw * -1,0))
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch+90-degrees(dir_to_pitch(player_velocity)),yaw - player_vel_yaw * -1,0))
|
||||
-- sets eye height, and nametag color accordingly
|
||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
set_properties_conditional(player,{collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
-- control body bone when swimming
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(degrees(dir_to_pitch(player_velocity)) - 90,player_vel_yaw * -1 - yaw + 180,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(degrees(dir_to_pitch(player_velocity)) - 90,player_vel_yaw * -1 - yaw + 180,0))
|
||||
|
||||
elseif player:get_attach() == nil then
|
||||
-- sets eye height, and nametag color accordingly
|
||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
set_properties_conditional(player,{collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
|
||||
if player_velocity.x > 0.35 or player_velocity.z > 0.35 or player_velocity.x < -0.35 or player_velocity.z < -0.35 then
|
||||
if player_vel_yaw * -1 - yaw < 90 or player_vel_yaw * -1 - yaw > 270 then
|
||||
-- controls head and Body_Control bones while moving backwards
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,yaw - player_vel_yaw * -1,0))
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,player_vel_yaw * -1 - yaw,0))
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch,yaw - player_vel_yaw * -1,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(0,player_vel_yaw * -1 - yaw,0))
|
||||
else
|
||||
-- controls head and Body_Control bones while moving forwards
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,yaw - player_vel_yaw * -1 + 180,0))
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,player_vel_yaw * -1 - yaw + 180,0))
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch,yaw - player_vel_yaw * -1 + 180,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(0,player_vel_yaw * -1 - yaw + 180,0))
|
||||
end
|
||||
else
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,0,0))
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch,0,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
end
|
||||
else
|
||||
local attached = player:get_attach(parent)
|
||||
local attached_yaw = degrees(attached:get_yaw())
|
||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,degrees(player:get_look_horizontal()) * -1 + attached_yaw,0))
|
||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
set_properties_conditional(player,{collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||
set_bone_position_conditional(player,"Head", vector.new(0,6.3,0), vector.new(pitch,degrees(player:get_look_horizontal()) * -1 + attached_yaw,0))
|
||||
set_bone_position_conditional(player,"Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||
end
|
||||
|
||||
if mcl_playerplus_internal[name].jump_cooldown > 0 then
|
||||
|
|
|
@ -91,6 +91,11 @@ flame_sound (Flame sound) bool true
|
|||
# Form: Image height / Image width
|
||||
fire_animation_frames (Fire Animation Frames) int 8
|
||||
|
||||
# How long to wait between frames of the fire animation in frames per second.
|
||||
# A higher number means it looks better but also results in a lot of additional network traffic. A low single digit value is recommended for multiplayer.
|
||||
#(default: 30)
|
||||
fire_animation_fps (Fire Animation FPS) int 30 0 60
|
||||
|
||||
# Whether to animate chests when open / close
|
||||
animated_chests (Animated chests) bool true
|
||||
|
||||
|
|