Fix crash when obj tele's Nether→Overworld @ rain
[MineClone/MineClone2.git] / mods / CORE / mcl_worlds / init.lua
blobd032426db633587e2664a2eae6aaab43da8cda6a
1 mcl_worlds = {}
3 -- For a given position, returns a 2-tuple:
4 -- 1st return value: true if pos is in void
5 -- 2nd return value: true if it is in the deadly part of the void
6 function mcl_worlds.is_in_void(pos)
7 local void =
8 not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
9 (pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or
10 (pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
12 local void_deadly = false
13 local deadly_tolerance = 64 -- the player must be this many nodes “deep” into the void to be damaged
14 if void then
15 -- Overworld → Void → End → Void → Nether → Void
16 if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
17 void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
18 elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
19 -- The void between End and Nether. Like usual, but here, the void
20 -- *above* the Nether also has a small tolerance area, so player
21 -- can fly above the Nether without getting hurt instantly.
22 void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
23 elseif pos.y < mcl_vars.mg_nether_min then
24 void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
25 end
26 end
27 return void, void_deadly
28 end
30 -- Takes an Y coordinate as input and returns:
31 -- 1) The corresponding Minecraft layer (can be nil if void)
32 -- 2) The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if it is in the void
33 -- If the Y coordinate is not located in any dimension, it will return:
34 -- nil, "void"
35 function mcl_worlds.y_to_layer(y)
36 if y >= mcl_vars.mg_overworld_min then
37 return y - mcl_vars.mg_overworld_min, "overworld"
38 elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then
39 return y - mcl_vars.mg_nether_min, "nether"
40 elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
41 return y - mcl_vars.mg_end_min, "end"
42 else
43 return nil, "void"
44 end
45 end
47 -- Takes a pos and returns the dimension it belongs to (same as above)
48 function mcl_worlds.pos_to_dimension(pos)
49 local _, dim = mcl_worlds.y_to_layer(pos.y)
50 return dim
51 end
53 -- Takes a Minecraft layer and a “dimension” name
54 -- and returns the corresponding Y coordinate for
55 -- MineClone 2.
56 -- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
57 function mcl_worlds.layer_to_y(layer, mc_dimension)
58 if mc_dimension == "overworld" or mc_dimension == nil then
59 return layer + mcl_vars.mg_overworld_min
60 elseif mc_dimension == "nether" then
61 return layer + mcl_vars.mg_nether_min
62 elseif mc_dimension == "end" then
63 return layer + mcl_vars.mg_end_min
64 end
65 end
67 -- Takes a position and returns true if this position can have weather
68 function mcl_worlds.has_weather(pos)
69 -- Weather in the Overworld and the high part of the void below
70 return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
71 end
73 -- Takes a position (pos) and returns true if compasses are working here
74 function mcl_worlds.compass_works(pos)
75 -- It doesn't work in Nether and the End, but it works in the Overworld and in the high part of the void below
76 local _, dim = mcl_worlds.y_to_layer(pos.y)
77 if dim == "nether" or dim == "end" then
78 return false
79 elseif dim == "void" then
80 return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
81 else
82 return true
83 end
84 end
86 -- Takes a position (pos) and returns true if clocks are working here
87 mcl_worlds.clock_works = mcl_worlds.compass_works
89 --------------- CALLBACKS ------------------
90 mcl_worlds.registered_on_dimension_change = {}
92 -- Register a callback function func(player, dimension).
93 -- It will be called whenever a player changes between dimensions.
94 -- The void counts as dimension.
95 -- * player: The player who changed the dimension
96 -- * dimension: The new dimension of the player ("overworld", "nether", "end", "void").
97 function mcl_worlds.register_on_dimension_change(func)
98 table.insert(mcl_worlds.registered_on_dimension_change, func)
99 end
101 -- Playername-indexed table containig the name of the last known dimension the
102 -- player was in.
103 local last_dimension = {}
105 -- Notifies this mod about a dimension change of a player.
106 -- * player: Player who changed the dimension
107 -- * dimension: New dimension ("overworld", "nether", "end", "void")
108 function mcl_worlds.dimension_change(player, dimension)
109 for i=1, #mcl_worlds.registered_on_dimension_change do
110 mcl_worlds.registered_on_dimension_change[i](player, dimension)
111 last_dimension[player:get_player_name()] = dimension
115 ----------------------- INTERNAL STUFF ----------------------
117 -- Update the dimension callbacks every DIM_UPDATE seconds
118 local DIM_UPDATE = 1
119 local dimtimer = 0
121 minetest.register_on_joinplayer(function(player)
122 last_dimension[player:get_player_name()] = mcl_worlds.pos_to_dimension(player:get_pos())
123 end)
125 minetest.register_globalstep(function(dtime)
126 -- regular updates based on iterval
127 dimtimer = dimtimer + dtime;
128 if dimtimer >= DIM_UPDATE then
129 local players = minetest.get_connected_players()
130 for p=1, #players do
131 local dim = mcl_worlds.pos_to_dimension(players[p]:get_pos())
132 local name = players[p]:get_player_name()
133 if dim ~= last_dimension[name] then
134 mcl_worlds.dimension_change(players[p], dim)
137 dimtimer = 0
139 end)