Use mcl_playerphysics to handle player physics
[MineClone/MineClone2.git] / mods / PLAYER / mcl_playerplus / init.lua
bloba07b63bec3af5676e6d28098b3c1c1bfd1bd6802
1 -- Internal player state
2 local mcl_playerplus_internal = {}
4 local armor_mod = minetest.get_modpath("3d_armor")
5 local def = {}
6 local time = 0
8 minetest.register_globalstep(function(dtime)
10 time = time + dtime
12 -- Update jump status immediately since we need this info in real time.
13 -- WARNING: This section is HACKY as hell since it is all just based on heuristics.
14 for _,player in pairs(minetest.get_connected_players()) do
15 local name = player:get_player_name()
16 if mcl_playerplus_internal[name].jump_cooldown > 0 then
17 mcl_playerplus_internal[name].jump_cooldown = mcl_playerplus_internal[name].jump_cooldown - dtime
18 end
19 if player:get_player_control().jump and mcl_playerplus_internal[name].jump_cooldown <= 0 then
21 local pos = player:getpos()
23 local node_stand = mcl_playerinfo[name].node_stand
24 local node_stand_below = mcl_playerinfo[name].node_stand_below
25 local node_head = mcl_playerinfo[name].node_head
26 local node_feet = mcl_playerinfo[name].node_feet
27 if not node_stand or not node_stand_below or not node_head or not node_feet then
28 return
29 end
31 -- Cause buggy exhaustion for jumping
33 --[[ Checklist we check to know the player *actually* jumped:
34 * Not on or in liquid
35 * Not on or at climbable
36 * On walkable
37 * Not on disable_jump
38 FIXME: This code is pretty hacky and it is possible to miss some jumps or detect false
39 jumps because of delays, rounding errors, etc.
40 What this code *really* needs is some kind of jumping “callback” which this engine lacks
41 as of 0.4.15.
44 if minetest.get_item_group(node_feet, "liquid") == 0 and
45 minetest.get_item_group(node_stand, "liquid") == 0 and
46 not minetest.registered_nodes[node_feet].climbable and
47 not minetest.registered_nodes[node_stand].climbable and
48 (minetest.registered_nodes[node_stand].walkable or minetest.registered_nodes[node_stand_below].walkable)
49 and minetest.get_item_group(node_stand, "disable_jump") == 0
50 and minetest.get_item_group(node_stand_below, "disable_jump") == 0 then
51 -- Cause exhaustion for jumping
52 if mcl_sprint.is_sprinting(name) then
53 mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SPRINT_JUMP)
54 else
55 mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_JUMP)
56 end
58 -- Reset cooldown timer
59 mcl_playerplus_internal[name].jump_cooldown = 0.45
60 end
61 end
62 end
64 -- Run the rest of the code every 0.5 seconds
65 if time < 0.5 then
66 return
67 end
69 -- reset time for next check
70 -- FIXME: Make sure a regular check interval applies
71 time = 0
73 -- check players
74 for _,player in pairs(minetest.get_connected_players()) do
75 -- who am I?
76 local name = player:get_player_name()
78 -- where am I?
79 local pos = player:getpos()
81 -- what is around me?
82 local node_stand = mcl_playerinfo[name].node_stand
83 local node_stand_below = mcl_playerinfo[name].node_stand_below
84 local node_head = mcl_playerinfo[name].node_head
85 local node_feet = mcl_playerinfo[name].node_feet
86 if not node_stand or not node_stand_below or not node_head or not node_feet then
87 return
88 end
90 -- set defaults
91 def.speed = 1
93 -- Standing on soul sand? If so, walk slower
94 if node_stand == "mcl_nether:soul_sand" then
95 -- TODO: Tweak walk speed
96 -- TODO: Also slow down mobs
97 -- Slow down even more when soul sand is above certain block
98 if node_stand_below == "mcl_core:ice" or node_stand_below == "mcl_core:packed_ice" or node_stand_below == "mcl_core:slimeblock" or node_stand_below == "mcl_core:water_source" then
99 mcl_playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.1)
100 else
101 mcl_playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.4)
103 else
104 -- Reset speed decrease
105 mcl_playerphysics.remove_physics_factor(player, "speed", "mcl_playerplus:surface")
108 -- Is player suffocating inside node? (Only for solid full opaque cube type nodes
109 -- without group disable_suffocation=1)
110 local ndef = minetest.registered_nodes[node_head]
112 if (ndef.walkable == nil or ndef.walkable == true)
113 and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
114 and (ndef.node_box == nil or ndef.node_box.type == "regular")
115 and (ndef.groups.disable_suffocation ~= 1)
116 and (ndef.groups.opaque == 1)
117 and (node_head ~= "ignore")
118 -- Check privilege, too
119 and (not minetest.check_player_privs(name, {noclip = true})) then
120 if player:get_hp() > 0 then
121 mcl_death_messages.player_damage(player, string.format("%s suffocated to death.", name))
122 player:set_hp(player:get_hp() - 1)
126 -- Am I near a cactus?
127 local near = minetest.find_node_near(pos, 1, "mcl_core:cactus")
128 if not near then
129 near = minetest.find_node_near({x=pos.x, y=pos.y-1, z=pos.z}, 1, "mcl_core:cactus")
131 if near then
132 -- Am I touching the cactus? If so, it hurts
133 local dist = vector.distance(pos, near)
134 local dist_feet = vector.distance({x=pos.x, y=pos.y-1, z=pos.z}, near)
135 if dist < 1.1 or dist_feet < 1.1 then
136 if player:get_hp() > 0 then
137 mcl_death_messages.player_damage(player, string.format("%s was prickled by a cactus.", name))
138 mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_DAMAGE)
139 player:set_hp(player:get_hp() - 1)
144 -- Deal Void damage
145 local void, void_deadly = mcl_worlds.is_in_void(pos)
146 if void_deadly then
147 -- Player is deep into the void, deal void damage
148 if minetest.settings:get_bool("enable_damage") then
149 if player:get_hp() > 0 then
150 mcl_death_messages.player_damage(player, string.format("%s fell into the endless void.", name))
151 player:set_hp(player:get_hp() - 4)
153 else
154 -- If damge is disabled, we can't kill the player.
155 -- So we just teleport the player back to spawn.
156 local spawn = mcl_spawn.get_spawn_pos(player)
157 player:set_pos(spawn)
158 mcl_worlds.dimension_change(player, mcl_worlds.pos_to_dimension(spawn))
159 minetest.chat_send_player(name, "The void is off-limits to you!")
163 --[[ Swimming: Cause exhaustion.
164 NOTE: As of 0.4.15, it only counts as swimming when you are with the feet inside the liquid!
165 Head alone does not count. We respect that for now. ]]
166 if minetest.get_item_group(node_feet, "liquid") ~= 0 or
167 minetest.get_item_group(node_stand, "liquid") ~= 0 then
168 local lastPos = mcl_playerplus_internal[name].lastPos
169 if lastPos then
170 local dist = vector.distance(lastPos, pos)
171 mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance + dist
172 if mcl_playerplus_internal[name].swimDistance >= 1 then
173 local superficial = math.floor(mcl_playerplus_internal[name].swimDistance)
174 mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SWIM * superficial)
175 mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance - superficial
181 -- Underwater: Spawn bubble particles
182 if minetest.get_item_group(node_head, "water") ~= 0 then
184 minetest.add_particlespawner({
185 amount = 10,
186 time = 0.15,
187 minpos = { x = -0.25, y = 0.3, z = -0.25 },
188 maxpos = { x = 0.25, y = 0.7, z = 0.75 },
189 attached = player,
190 minvel = {x = -0.2, y = 0, z = -0.2},
191 maxvel = {x = 0.5, y = 0, z = 0.5},
192 minacc = {x = -0.4, y = 4, z = -0.4},
193 maxacc = {x = 0.5, y = 1, z = 0.5},
194 minexptime = 0.3,
195 maxexptime = 0.8,
196 minsize = 0.7,
197 maxsize = 2.4,
198 texture = "mcl_particles_bubble.png"
202 -- Show positions of barriers when player is wielding a barrier
203 local wi = player:get_wielded_item():get_name()
204 if wi == "mcl_core:barrier" or wi == "mcl_core:realm_barrier" then
205 local pos = vector.round(player:getpos())
206 local r = 8
207 local vm = minetest.get_voxel_manip()
208 local emin, emax = vm:read_from_map({x=pos.x-r, y=pos.y-r, z=pos.z-r}, {x=pos.x+r, y=pos.y+r, z=pos.z+r})
209 local area = VoxelArea:new{
210 MinEdge = emin,
211 MaxEdge = emax,
213 local data = vm:get_data()
214 for x=pos.x-r, pos.x+r do
215 for y=pos.y-r, pos.y+r do
216 for z=pos.z-r, pos.z+r do
217 local vi = area:indexp({x=x, y=y, z=z})
218 local nodename = minetest.get_name_from_content_id(data[vi])
219 local tex
220 if nodename == "mcl_core:barrier" then
221 tex = "mcl_core_barrier.png"
222 elseif nodename == "mcl_core:realm_barrier" then
223 tex = "mcl_core_barrier.png^[colorize:#FF00FF:127^[transformFX"
225 if tex then
226 minetest.add_particle({
227 pos = {x=x, y=y, z=z},
228 expirationtime = 1,
229 size = 8,
230 texture = tex,
231 playername = name
239 -- Update internal values
240 mcl_playerplus_internal[name].lastPos = pos
244 end)
246 -- set to blank on join (for 3rd party mods)
247 minetest.register_on_joinplayer(function(player)
248 local name = player:get_player_name()
250 mcl_playerplus_internal[name] = {
251 lastPos = nil,
252 swimDistance = 0,
253 jump_cooldown = -1, -- Cooldown timer for jumping, we need this to prevent the jump exhaustion to increase rapidly
255 end)
257 -- clear when player leaves
258 minetest.register_on_leaveplayer(function(player)
259 local name = player:get_player_name()
261 mcl_playerplus_internal[name] = nil
262 end)