diff --git a/mods/PLAYER/mcl_playerplus/init.lua b/mods/PLAYER/mcl_playerplus/init.lua index 0349f7326b..62f91fb150 100644 --- a/mods/PLAYER/mcl_playerplus/init.lua +++ b/mods/PLAYER/mcl_playerplus/init.lua @@ -1,287 +1,294 @@ -local S = minetest.get_translator("mcl_playerplus") - --- Internal player state -local mcl_playerplus_internal = {} - -local def = {} -local time = 0 - --- converts yaw to degrees -local function degrees(rad) - return rad * 180.0 / math.pi -end - -local pitch, name, node_stand, node_stand_below, node_head, node_feet, pos - -minetest.register_globalstep(function(dtime) - - time = time + dtime - - -- Update jump status immediately since we need this info in real time. - -- WARNING: This section is HACKY as hell since it is all just based on heuristics. - for _,player in pairs(minetest.get_connected_players()) do - local controls = player:get_player_control() - name = player:get_player_name() - - -- controls head bone - pitch = degrees(player:get_look_vertical()) * -1 - - if controls.LMB then - player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0)) - else - player:set_bone_position("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)) - -- sets collisionbox, eye height, and nametag color accordingly - if player:get_properties().collisionbox ~= {-0.35,0,-0.35,0.35,1.35,0.35} then - player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.35,0.35}, eye_height = 1.35, nametag_color = { r = 255, b = 225, a = 0, g = 225 }}) - end - else - -- controls head pitch when not sneaking - player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,0,0)) - -- sets collisionbox, eye height, and nametag color accordingly - if player:get_properties().collisionbox ~= {-0.35,0,-0.35,0.35,1.8,0.35} then - player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 255, b = 225, a = 225, g = 225 }}) - end - end - - if mcl_playerplus_internal[name].jump_cooldown > 0 then - mcl_playerplus_internal[name].jump_cooldown = mcl_playerplus_internal[name].jump_cooldown - dtime - end - if player:get_player_control().jump and mcl_playerplus_internal[name].jump_cooldown <= 0 then - - pos = player:get_pos() - - node_stand = mcl_playerinfo[name].node_stand - node_stand_below = mcl_playerinfo[name].node_stand_below - node_head = mcl_playerinfo[name].node_head - node_feet = mcl_playerinfo[name].node_feet - if not node_stand or not node_stand_below or not node_head or not node_feet then - return - end - if not minetest.registered_nodes[node_stand] or not minetest.registered_nodes[node_stand_below] or not minetest.registered_nodes[node_head] or not minetest.registered_nodes[node_feet] then - return - end - - -- Cause buggy exhaustion for jumping - - --[[ Checklist we check to know the player *actually* jumped: - * Not on or in liquid - * Not on or at climbable - * On walkable - * Not on disable_jump - FIXME: This code is pretty hacky and it is possible to miss some jumps or detect false - jumps because of delays, rounding errors, etc. - What this code *really* needs is some kind of jumping “callback” which this engine lacks - as of 0.4.15. - ]] - - if minetest.get_item_group(node_feet, "liquid") == 0 and - minetest.get_item_group(node_stand, "liquid") == 0 and - not minetest.registered_nodes[node_feet].climbable and - not minetest.registered_nodes[node_stand].climbable and - (minetest.registered_nodes[node_stand].walkable or minetest.registered_nodes[node_stand_below].walkable) - and minetest.get_item_group(node_stand, "disable_jump") == 0 - and minetest.get_item_group(node_stand_below, "disable_jump") == 0 then - -- Cause exhaustion for jumping - if mcl_sprint.is_sprinting(name) then - mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SPRINT_JUMP) - else - mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_JUMP) - end - - -- Reset cooldown timer - mcl_playerplus_internal[name].jump_cooldown = 0.45 - end - end - end - - -- Run the rest of the code every 0.5 seconds - if time < 0.5 then - return - end - - -- reset time for next check - -- FIXME: Make sure a regular check interval applies - time = 0 - - -- check players - for _,player in pairs(minetest.get_connected_players()) do - -- who am I? - local name = player:get_player_name() - - -- where am I? - local pos = player:get_pos() - - -- what is around me? - local node_stand = mcl_playerinfo[name].node_stand - local node_stand_below = mcl_playerinfo[name].node_stand_below - local node_head = mcl_playerinfo[name].node_head - local node_feet = mcl_playerinfo[name].node_feet - if not node_stand or not node_stand_below or not node_head or not node_feet then - return - end - - -- set defaults - def.speed = 1 - - -- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots) - if node_stand == "mcl_nether:soul_sand" then - -- TODO: Tweak walk speed - -- TODO: Also slow down mobs - -- Slow down even more when soul sand is above certain block - local boots = player:get_inventory():get_stack("armor", 5) - local soul_speed = mcl_enchanting.get_enchantment(boots, "soul_speed") - if soul_speed > 0 then - playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", soul_speed * 0.105 + 1.3) - else - 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 - playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.1) - else - playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.4) - end - end - else - -- Reset speed decrease - playerphysics.remove_physics_factor(player, "speed", "mcl_playerplus:surface") - end - - -- Is player suffocating inside node? (Only for solid full opaque cube type nodes - -- without group disable_suffocation=1) - local ndef = minetest.registered_nodes[node_head] - - if (ndef.walkable == nil or ndef.walkable == true) - and (ndef.collision_box == nil or ndef.collision_box.type == "regular") - and (ndef.node_box == nil or ndef.node_box.type == "regular") - and (ndef.groups.disable_suffocation ~= 1) - and (ndef.groups.opaque == 1) - and (node_head ~= "ignore") - -- Check privilege, too - and (not minetest.check_player_privs(name, {noclip = true})) then - if player:get_hp() > 0 then - mcl_death_messages.player_damage(player, S("@1 suffocated to death.", name)) - player:set_hp(player:get_hp() - 1) - end - end - - -- Am I near a cactus? - local near = minetest.find_node_near(pos, 1, "mcl_core:cactus") - if not near then - near = minetest.find_node_near({x=pos.x, y=pos.y-1, z=pos.z}, 1, "mcl_core:cactus") - end - if near then - -- Am I touching the cactus? If so, it hurts - local dist = vector.distance(pos, near) - local dist_feet = vector.distance({x=pos.x, y=pos.y-1, z=pos.z}, near) - if dist < 1.1 or dist_feet < 1.1 then - if player:get_hp() > 0 then - mcl_death_messages.player_damage(player, S("@1 was prickled to death by a cactus.", name)) - player:set_hp(player:get_hp() - 1, { type = "punch", from = "mod" }) - end - end - end - - --[[ Swimming: Cause exhaustion. - NOTE: As of 0.4.15, it only counts as swimming when you are with the feet inside the liquid! - Head alone does not count. We respect that for now. ]] - if minetest.get_item_group(node_feet, "liquid") ~= 0 or - minetest.get_item_group(node_stand, "liquid") ~= 0 then - local lastPos = mcl_playerplus_internal[name].lastPos - if lastPos then - local dist = vector.distance(lastPos, pos) - mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance + dist - if mcl_playerplus_internal[name].swimDistance >= 1 then - local superficial = math.floor(mcl_playerplus_internal[name].swimDistance) - mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SWIM * superficial) - mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance - superficial - end - end - - end - - -- Underwater: Spawn bubble particles - if minetest.get_item_group(node_head, "water") ~= 0 then - - minetest.add_particlespawner({ - amount = 10, - time = 0.15, - minpos = { x = -0.25, y = 0.3, z = -0.25 }, - maxpos = { x = 0.25, y = 0.7, z = 0.75 }, - attached = player, - minvel = {x = -0.2, y = 0, z = -0.2}, - maxvel = {x = 0.5, y = 0, z = 0.5}, - minacc = {x = -0.4, y = 4, z = -0.4}, - maxacc = {x = 0.5, y = 1, z = 0.5}, - minexptime = 0.3, - maxexptime = 0.8, - minsize = 0.7, - maxsize = 2.4, - texture = "mcl_particles_bubble.png" - }) - end - - -- Show positions of barriers when player is wielding a barrier - local wi = player:get_wielded_item():get_name() - if wi == "mcl_core:barrier" or wi == "mcl_core:realm_barrier" then - local pos = vector.round(player:get_pos()) - local r = 8 - local vm = minetest.get_voxel_manip() - 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}) - local area = VoxelArea:new{ - MinEdge = emin, - MaxEdge = emax, - } - local data = vm:get_data() - for x=pos.x-r, pos.x+r do - for y=pos.y-r, pos.y+r do - for z=pos.z-r, pos.z+r do - local vi = area:indexp({x=x, y=y, z=z}) - local nodename = minetest.get_name_from_content_id(data[vi]) - local tex - if nodename == "mcl_core:barrier" then - tex = "mcl_core_barrier.png" - elseif nodename == "mcl_core:realm_barrier" then - tex = "mcl_core_barrier.png^[colorize:#FF00FF:127^[transformFX" - end - if tex then - minetest.add_particle({ - pos = {x=x, y=y, z=z}, - expirationtime = 1, - size = 8, - texture = tex, - glow = 14, - playername = name - }) - end - end - end - end - end - - -- Update internal values - mcl_playerplus_internal[name].lastPos = pos - - end - -end) - --- set to blank on join (for 3rd party mods) -minetest.register_on_joinplayer(function(player) - local name = player:get_player_name() - - mcl_playerplus_internal[name] = { - lastPos = nil, - swimDistance = 0, - jump_cooldown = -1, -- Cooldown timer for jumping, we need this to prevent the jump exhaustion to increase rapidly - } -end) - --- clear when player leaves -minetest.register_on_leaveplayer(function(player) - local name = player:get_player_name() - - mcl_playerplus_internal[name] = nil -end) +local S = minetest.get_translator("mcl_playerplus") + +-- Internal player state +local mcl_playerplus_internal = {} + +local def = {} +local time = 0 + +-- converts yaw to degrees +local function degrees(rad) + return rad * 180.0 / math.pi +end + +local pitch, name, node_stand, node_stand_below, node_head, node_feet, pos + +minetest.register_globalstep(function(dtime) + + time = time + dtime + + -- Update jump status immediately since we need this info in real time. + -- WARNING: This section is HACKY as hell since it is all just based on heuristics. + for _,player in pairs(minetest.get_connected_players()) do + local controls = player:get_player_control() + name = player:get_player_name() + + -- controls head bone + pitch = degrees(player:get_look_vertical()) * -1 + + if controls.LMB then + player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0)) + else + player:set_bone_position("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)) + -- sets collisionbox, eye height, and nametag color accordingly + if player:get_properties().collisionbox ~= {-0.35,0,-0.35,0.35,1.35,0.35} then + player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.35,0.35}, eye_height = 1.35, nametag_color = { r = 255, b = 225, a = 0, g = 225 }}) + end + elseif minetest.get_item_group(mcl_playerinfo[name].node_stand, "water") ~= 0 then + -- controls head pitch when swiming + player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch+90,0,0)) + -- sets collisionbox, eye height, and nametag color accordingly + if player:get_properties().collisionbox ~= {-0.35,0.2,-0.35,0.35,1.8,0.35} then + player:set_properties({collisionbox = {-0.35,0.2,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 255, b = 225, a = 225, g = 225 }}) + end + else + -- controls head pitch when not sneaking + player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch,0,0)) + -- sets collisionbox, eye height, and nametag color accordingly + if player:get_properties().collisionbox ~= {-0.35,0,-0.35,0.35,1.8,0.35} then + player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.65, nametag_color = { r = 255, b = 225, a = 225, g = 225 }}) + end + end + + if mcl_playerplus_internal[name].jump_cooldown > 0 then + mcl_playerplus_internal[name].jump_cooldown = mcl_playerplus_internal[name].jump_cooldown - dtime + end + if player:get_player_control().jump and mcl_playerplus_internal[name].jump_cooldown <= 0 then + + pos = player:get_pos() + + node_stand = mcl_playerinfo[name].node_stand + node_stand_below = mcl_playerinfo[name].node_stand_below + node_head = mcl_playerinfo[name].node_head + node_feet = mcl_playerinfo[name].node_feet + if not node_stand or not node_stand_below or not node_head or not node_feet then + return + end + if not minetest.registered_nodes[node_stand] or not minetest.registered_nodes[node_stand_below] or not minetest.registered_nodes[node_head] or not minetest.registered_nodes[node_feet] then + return + end + + -- Cause buggy exhaustion for jumping + + --[[ Checklist we check to know the player *actually* jumped: + * Not on or in liquid + * Not on or at climbable + * On walkable + * Not on disable_jump + FIXME: This code is pretty hacky and it is possible to miss some jumps or detect false + jumps because of delays, rounding errors, etc. + What this code *really* needs is some kind of jumping “callback” which this engine lacks + as of 0.4.15. + ]] + + if minetest.get_item_group(node_feet, "liquid") == 0 and + minetest.get_item_group(node_stand, "liquid") == 0 and + not minetest.registered_nodes[node_feet].climbable and + not minetest.registered_nodes[node_stand].climbable and + (minetest.registered_nodes[node_stand].walkable or minetest.registered_nodes[node_stand_below].walkable) + and minetest.get_item_group(node_stand, "disable_jump") == 0 + and minetest.get_item_group(node_stand_below, "disable_jump") == 0 then + -- Cause exhaustion for jumping + if mcl_sprint.is_sprinting(name) then + mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SPRINT_JUMP) + else + mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_JUMP) + end + + -- Reset cooldown timer + mcl_playerplus_internal[name].jump_cooldown = 0.45 + end + end + end + + -- Run the rest of the code every 0.5 seconds + if time < 0.5 then + return + end + + -- reset time for next check + -- FIXME: Make sure a regular check interval applies + time = 0 + + -- check players + for _,player in pairs(minetest.get_connected_players()) do + -- who am I? + local name = player:get_player_name() + + -- where am I? + local pos = player:get_pos() + + -- what is around me? + local node_stand = mcl_playerinfo[name].node_stand + local node_stand_below = mcl_playerinfo[name].node_stand_below + local node_head = mcl_playerinfo[name].node_head + local node_feet = mcl_playerinfo[name].node_feet + if not node_stand or not node_stand_below or not node_head or not node_feet then + return + end + + -- set defaults + def.speed = 1 + + -- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots) + if node_stand == "mcl_nether:soul_sand" then + -- TODO: Tweak walk speed + -- TODO: Also slow down mobs + -- Slow down even more when soul sand is above certain block + local boots = player:get_inventory():get_stack("armor", 5) + local soul_speed = mcl_enchanting.get_enchantment(boots, "soul_speed") + if soul_speed > 0 then + playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", soul_speed * 0.105 + 1.3) + else + 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 + playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.1) + else + playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:surface", 0.4) + end + end + else + -- Reset speed decrease + playerphysics.remove_physics_factor(player, "speed", "mcl_playerplus:surface") + end + + -- Is player suffocating inside node? (Only for solid full opaque cube type nodes + -- without group disable_suffocation=1) + local ndef = minetest.registered_nodes[node_head] + + if (ndef.walkable == nil or ndef.walkable == true) + and (ndef.collision_box == nil or ndef.collision_box.type == "regular") + and (ndef.node_box == nil or ndef.node_box.type == "regular") + and (ndef.groups.disable_suffocation ~= 1) + and (ndef.groups.opaque == 1) + and (node_head ~= "ignore") + -- Check privilege, too + and (not minetest.check_player_privs(name, {noclip = true})) then + if player:get_hp() > 0 then + mcl_death_messages.player_damage(player, S("@1 suffocated to death.", name)) + player:set_hp(player:get_hp() - 1) + end + end + + -- Am I near a cactus? + local near = minetest.find_node_near(pos, 1, "mcl_core:cactus") + if not near then + near = minetest.find_node_near({x=pos.x, y=pos.y-1, z=pos.z}, 1, "mcl_core:cactus") + end + if near then + -- Am I touching the cactus? If so, it hurts + local dist = vector.distance(pos, near) + local dist_feet = vector.distance({x=pos.x, y=pos.y-1, z=pos.z}, near) + if dist < 1.1 or dist_feet < 1.1 then + if player:get_hp() > 0 then + mcl_death_messages.player_damage(player, S("@1 was prickled to death by a cactus.", name)) + player:set_hp(player:get_hp() - 1, { type = "punch", from = "mod" }) + end + end + end + + --[[ Swimming: Cause exhaustion. + NOTE: As of 0.4.15, it only counts as swimming when you are with the feet inside the liquid! + Head alone does not count. We respect that for now. ]] + if minetest.get_item_group(node_feet, "liquid") ~= 0 or + minetest.get_item_group(node_stand, "liquid") ~= 0 then + local lastPos = mcl_playerplus_internal[name].lastPos + if lastPos then + local dist = vector.distance(lastPos, pos) + mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance + dist + if mcl_playerplus_internal[name].swimDistance >= 1 then + local superficial = math.floor(mcl_playerplus_internal[name].swimDistance) + mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_SWIM * superficial) + mcl_playerplus_internal[name].swimDistance = mcl_playerplus_internal[name].swimDistance - superficial + end + end + + end + + -- Underwater: Spawn bubble particles + if minetest.get_item_group(node_head, "water") ~= 0 then + + minetest.add_particlespawner({ + amount = 10, + time = 0.15, + minpos = { x = -0.25, y = 0.3, z = -0.25 }, + maxpos = { x = 0.25, y = 0.7, z = 0.75 }, + attached = player, + minvel = {x = -0.2, y = 0, z = -0.2}, + maxvel = {x = 0.5, y = 0, z = 0.5}, + minacc = {x = -0.4, y = 4, z = -0.4}, + maxacc = {x = 0.5, y = 1, z = 0.5}, + minexptime = 0.3, + maxexptime = 0.8, + minsize = 0.7, + maxsize = 2.4, + texture = "mcl_particles_bubble.png" + }) + end + + -- Show positions of barriers when player is wielding a barrier + local wi = player:get_wielded_item():get_name() + if wi == "mcl_core:barrier" or wi == "mcl_core:realm_barrier" then + local pos = vector.round(player:get_pos()) + local r = 8 + local vm = minetest.get_voxel_manip() + 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}) + local area = VoxelArea:new{ + MinEdge = emin, + MaxEdge = emax, + } + local data = vm:get_data() + for x=pos.x-r, pos.x+r do + for y=pos.y-r, pos.y+r do + for z=pos.z-r, pos.z+r do + local vi = area:indexp({x=x, y=y, z=z}) + local nodename = minetest.get_name_from_content_id(data[vi]) + local tex + if nodename == "mcl_core:barrier" then + tex = "mcl_core_barrier.png" + elseif nodename == "mcl_core:realm_barrier" then + tex = "mcl_core_barrier.png^[colorize:#FF00FF:127^[transformFX" + end + if tex then + minetest.add_particle({ + pos = {x=x, y=y, z=z}, + expirationtime = 1, + size = 8, + texture = tex, + glow = 14, + playername = name + }) + end + end + end + end + end + + -- Update internal values + mcl_playerplus_internal[name].lastPos = pos + + end + +end) + +-- set to blank on join (for 3rd party mods) +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + + mcl_playerplus_internal[name] = { + lastPos = nil, + swimDistance = 0, + jump_cooldown = -1, -- Cooldown timer for jumping, we need this to prevent the jump exhaustion to increase rapidly + } +end) + +-- clear when player leaves +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + + mcl_playerplus_internal[name] = nil +end)