508 lines
15 KiB
Lua
508 lines
15 KiB
Lua
|
|
--[[
|
|
|
|
ITB (insidethebox) minetest game - Copyright (C) 2017-2018 sofar & nore
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public License
|
|
as published by the Free Software Foundation; either version 2.1
|
|
of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
MA 02111-1307 USA
|
|
|
|
]]--
|
|
|
|
local S = minetest.get_translator("player")
|
|
|
|
--[[
|
|
|
|
handle player events
|
|
|
|
--]]
|
|
|
|
players = {
|
|
sprinting = {}
|
|
}
|
|
|
|
minetest.register_privilege("zoom", {
|
|
description = "May zoom in using the `z` key",
|
|
on_grant = function(name, granter_name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if player then
|
|
player:set_properties({zoom_fov = 25})
|
|
end
|
|
end,
|
|
|
|
on_revoke = function(name, revoker_name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if player then
|
|
player:set_properties({zoom_fov = 0})
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_privilege("sprint", {
|
|
description = "May sprint in the lobby",
|
|
})
|
|
|
|
function players.return_to_lobby(player, spawn)
|
|
local name = player:get_player_name()
|
|
if boxes.teleport_to_tutorial_exit[name] then
|
|
boxes.teleport_to_tutorial_exit[name] = nil
|
|
player:set_pos(conf.tutorial.exit)
|
|
player:set_look_horizontal(3 * math.pi / 2)
|
|
elseif spawn then
|
|
player:set_pos({x = 0, y = 0, z = 0})
|
|
player:set_look_horizontal(3 * math.pi / 2)
|
|
else
|
|
-- put players around a central location
|
|
local p = conf.lobby.respawn_center
|
|
local r = math.random() * 2 * math.pi
|
|
local d = conf.lobby.respawn_radius
|
|
local d1, d2 = string.match(d, "(.*),(.*)")
|
|
d = math.random(d2 - d1) + d1
|
|
local n = {
|
|
x = math.cos(r),
|
|
y = 0,
|
|
z = math.sin(r),
|
|
}
|
|
|
|
player:set_pos(vector.add(p, vector.multiply(n, d)))
|
|
player:set_look_horizontal((math.pi / 2) + r)
|
|
player:set_look_vertical(0)
|
|
end
|
|
-- clean up other attributes
|
|
skybox.set(player, 6)
|
|
sfinv.set_page(player, "menu:lobby")
|
|
sfinv.set_player_inventory_formspec(player)
|
|
player:set_hp(20)
|
|
if minetest.check_player_privs(player, "server") then
|
|
player:get_inventory():set_stack("hand", 1, "tools:edit")
|
|
players.restore_inventory(player)
|
|
else
|
|
player:get_inventory():set_stack("hand", 1, "")
|
|
players.clear_inventory(player)
|
|
|
|
-- remove fly
|
|
local privs = minetest.get_player_privs(name)
|
|
privs.fly = nil
|
|
minetest.set_player_privs(name, privs)
|
|
end
|
|
|
|
if minetest.check_player_privs(player, "sprint") then
|
|
local privs = minetest.get_player_privs(name)
|
|
privs.fast = true
|
|
minetest.set_player_privs(name, privs)
|
|
end
|
|
end
|
|
|
|
-- heal players slowly, or kill them if they fell in the abyss
|
|
local function player_health_adjust()
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
local hp = player:get_hp()
|
|
if player:get_pos().y < -50 and hp > 0 then
|
|
player:set_hp(hp - 100)
|
|
player:set_velocity({x = 0, y = 0, z = 0})
|
|
player:set_acceleration({x = 0, y = 0, z = 0})
|
|
else
|
|
local breath = player:get_breath()
|
|
if hp > 0 and hp < 20 and breath > 0 then
|
|
player:set_hp(hp + 1)
|
|
end
|
|
end
|
|
end
|
|
minetest.after(2.0, player_health_adjust)
|
|
end
|
|
minetest.after(2.0, player_health_adjust)
|
|
|
|
function players.clear_inventory(player)
|
|
local inv = player:get_inventory()
|
|
for i = 1, inv:get_size("main") do
|
|
inv:set_stack("main", i, "")
|
|
end
|
|
inv:set_stack("hand", 1, "")
|
|
end
|
|
|
|
local player_saved_inventory = {}
|
|
function players.save_inventory(player)
|
|
local name = player:get_player_name()
|
|
local inv = player:get_inventory()
|
|
player_saved_inventory[name] = {}
|
|
for i = 1, inv:get_size("main") do
|
|
player_saved_inventory[name][i] = inv:get_stack("main", i):to_string()
|
|
end
|
|
end
|
|
|
|
function players.restore_inventory(player)
|
|
local name = player:get_player_name()
|
|
if not player_saved_inventory[name] then
|
|
return
|
|
end
|
|
local inv = player:get_inventory()
|
|
for i = 1, inv:get_size("main") do
|
|
inv:set_stack("main", i, player_saved_inventory[name][i] or "")
|
|
end
|
|
player_saved_inventory[name] = nil
|
|
end
|
|
|
|
function players.give_box_inventory(player)
|
|
sfinv.set_page(player, "menu:play")
|
|
sfinv.set_player_inventory_formspec(player)
|
|
player:get_inventory():set_stack("hand", 1, "")
|
|
local name = player:get_player_name()
|
|
if minetest.check_player_privs(player, "server") and
|
|
not player_saved_inventory[name] then
|
|
players.save_inventory(player)
|
|
end
|
|
players.clear_inventory(player)
|
|
--local inv = player:get_inventory()
|
|
--for i, stack in ipairs({}) do
|
|
-- inv:set_stack("main", i, stack)
|
|
--end
|
|
end
|
|
|
|
function players.give_edit_inventory(player)
|
|
sfinv.set_page(player, "menu:edit")
|
|
sfinv.set_player_inventory_formspec(player)
|
|
if minetest.check_player_privs(player, "server") then
|
|
return
|
|
end
|
|
players.clear_inventory(player)
|
|
local inv = player:get_inventory()
|
|
inv:set_stack("hand", 1, "tools:edit")
|
|
for i, stack in ipairs({
|
|
"nodes:marble",
|
|
"nodes:iron",
|
|
"boxes:set_door",
|
|
"tools:player",
|
|
"nodes:chest_with_boxes_nexus",
|
|
"boxes:pedestal",
|
|
}) do
|
|
inv:set_stack("main", i, stack)
|
|
end
|
|
-- give fly
|
|
local name = player:get_player_name()
|
|
local privs = minetest.get_player_privs(name)
|
|
privs.fly = true
|
|
minetest.set_player_privs(name, privs)
|
|
end
|
|
|
|
-- Disallow dropping items
|
|
function minetest.item_drop(itemstack, dropper, pos)
|
|
return itemstack
|
|
end
|
|
|
|
-- Prevent attached nodes from being dropped as items
|
|
function minetest.add_item(pos, item)
|
|
return nil
|
|
end
|
|
|
|
local model = {
|
|
animation_speed = 30,
|
|
animations = {
|
|
-- Standard animations.
|
|
stand = { x= 0, y= 79, },
|
|
lay = { x=162, y=166, },
|
|
walk = { x=168, y=187, },
|
|
mine = { x=189, y=198, },
|
|
walk_mine = { x=200, y=219, },
|
|
sit = { x= 81, y=160, },
|
|
},
|
|
}
|
|
|
|
-- Player stats and animations
|
|
local player_anim = {}
|
|
local player_sneak = {}
|
|
local player_afk = {}
|
|
|
|
local function player_set_animation(player, anim_name, speed)
|
|
local name = player:get_player_name()
|
|
if player_anim[name] == anim_name then
|
|
return
|
|
end
|
|
player_anim[name] = anim_name
|
|
local anim = model.animations[anim_name]
|
|
player:set_animation(anim, speed or 30, 0)
|
|
end
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
local time = os.time()
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
-- Check node the player is standing on and run callbacks
|
|
local ppos = player:get_pos()
|
|
local pos = vector.round(ppos)
|
|
if ppos.y - pos.y <= 0.1 then
|
|
local pos1 = vector.new(pos)
|
|
--local pos2 = vector.new(pos)
|
|
--pos2.y = pos2.y - 1
|
|
local node1 = minetest.get_node(pos1)
|
|
if minetest.registered_nodes[node1.name] and minetest.registered_nodes[node1.name].on_walk_over then
|
|
minetest.registered_nodes[node1.name].on_walk_over(pos1, node1, player)
|
|
end
|
|
--local node2 = minetest.get_node(pos2)
|
|
--if minetest.registered_nodes[node2.name] and minetest.registered_nodes[node2.name].on_walk_over then
|
|
-- minetest.registered_nodes[node2.name].on_walk_over(pos2, node2, player)
|
|
--end
|
|
end
|
|
|
|
-- Check each player and apply animations
|
|
local name = player:get_player_name()
|
|
local controls = player:get_player_control()
|
|
local walking = false
|
|
local animation_speed_mod = 30
|
|
|
|
-- Determine if the player is walking
|
|
if controls.up or controls.down or controls.left or controls.right then
|
|
walking = true
|
|
player_afk[name] = time
|
|
else
|
|
if not player_afk[name] then
|
|
player_afk[name] = time
|
|
elseif time - player_afk[name] > 3600 then
|
|
minetest.log("action", "AFK player: " .. name)
|
|
minetest.kick_player(name, " \n \nYou didn't do anything for too long.\n \nPlease come back any time!")
|
|
end
|
|
end
|
|
|
|
-- Determine if the player is sneaking, and reduce animation speed if so
|
|
if controls.sneak then
|
|
animation_speed_mod = animation_speed_mod / 2
|
|
end
|
|
|
|
-- Apply animations based on what the player is doing
|
|
if player:get_hp() == 0 then
|
|
player_set_animation(player, "lay")
|
|
elseif walking then
|
|
if player_sneak[name] ~= controls.sneak then
|
|
player_anim[name] = nil
|
|
player_sneak[name] = controls.sneak
|
|
end
|
|
if controls.LMB then
|
|
player_set_animation(player, "walk_mine", animation_speed_mod)
|
|
else
|
|
player_set_animation(player, "walk", animation_speed_mod)
|
|
end
|
|
elseif controls.LMB then
|
|
player_set_animation(player, "mine")
|
|
else
|
|
player_set_animation(player, "stand", animation_speed_mod)
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- event handlers
|
|
minetest.register_on_joinplayer(function(player)
|
|
-- set appearance
|
|
local pmeta = player:get_meta()
|
|
local skin = pmeta:get_string("skin")
|
|
if not skin then
|
|
local default_skins = {
|
|
"skin_jack.png",
|
|
"skin_jane.png",
|
|
"skin_jesse.png",
|
|
"skin_jj.png",
|
|
"skin_jean.png",
|
|
"skin_julio.png"
|
|
}
|
|
skin = default_skins[math.random(#default_skins)]
|
|
end
|
|
|
|
if minetest.check_player_privs(player, "review") then
|
|
skin = skin .. "^skin_overlay_admin.png"
|
|
end
|
|
|
|
player:set_properties({
|
|
mesh = "character.b3d",
|
|
textures = {skin},
|
|
visual = "mesh",
|
|
visual_size = {x=1, y=1},
|
|
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.75, 0.3},
|
|
stepheight = 0.6,
|
|
eye_height = 1.625,
|
|
})
|
|
|
|
if minetest.check_player_privs(player, "zoom") then
|
|
player:set_properties({zoom_fov = 25})
|
|
end
|
|
|
|
player:set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, 30)
|
|
player_set_animation(player, "stand")
|
|
-- nerf: can't make lots of jumps since 5.0
|
|
-- reduce gravity by 10%, and increase speed and jump by 10%
|
|
-- this makes very little noticable effect in game, but combined
|
|
-- it helps a lot.
|
|
player:set_physics_override({sneak_glitch=false, sneak=false, speed=1.1, jump=1, gravity=0.91})
|
|
player:get_inventory():set_size("hand", 1)
|
|
player:get_inventory():set_stack("hand", 1, "")
|
|
player:hud_set_hotbar_image("gui_hotbar.png")
|
|
player:hud_set_hotbar_selected_image("gui_hotbar_selected.png")
|
|
|
|
-- welcome banner
|
|
local hud = player:hud_add({
|
|
hud_elem_type = "image",
|
|
position = {x = 1/2, y = 1/2},
|
|
text = "itb-logo.png",
|
|
scale = {x = 10, y = 10},
|
|
alignment = {x=0, y=0},
|
|
})
|
|
minetest.after(5, function(p, h)
|
|
if not p then
|
|
return
|
|
end
|
|
p:hud_remove(h)
|
|
end, player, hud)
|
|
|
|
local name = player:get_player_name()
|
|
-- protovers check
|
|
minetest.after(15, function()
|
|
if not minetest.get_player_by_name(name) then return end
|
|
local pinfo = minetest.get_player_information(name)
|
|
if pinfo and pinfo.protocol_version < 32 then
|
|
minetest.log("action", name .. " uses a client with prot_version=" .. pinfo.protocol_version)
|
|
minetest.chat_send_player(name, "***\n\n"..S("You are using an outdated client version.").."\n" ..
|
|
S("This server will soon no longer support your client.").."\n" ..
|
|
S("Please upgrade to the official 0.4.16 build to prevent not being able to log into the server!").."\n\n***")
|
|
end
|
|
end)
|
|
|
|
-- return all players to the lobby, or throw them into the tutorial
|
|
if conf.tutorial.required and pmeta:get_string("tutorial_completed") ~= "1" then
|
|
if boxes.next_series(player, conf.tutorial.series) then
|
|
-- should be in a box now
|
|
return
|
|
end
|
|
end
|
|
players.return_to_lobby(player, true)
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
-- free memory
|
|
local name = player:get_player_name()
|
|
player_anim[name] = nil
|
|
player_sneak[name] = nil
|
|
player_afk[name] = nil
|
|
end)
|
|
|
|
sfinv.register_page("player:skin", {
|
|
title = "Skin",
|
|
is_in_nav = function()
|
|
return true
|
|
end,
|
|
get = function(self, player, context)
|
|
return sfinv.make_formspec(player, context,
|
|
"image_button[0.4,0.4;2,4;skin_preview_jack.png;skin;Jack]" ..
|
|
"image_button[2.8,0.4;2,4;skin_preview_jane.png;skin;Jane]" ..
|
|
"image_button[5.2,0.4;2,4;skin_preview_jesse.png;skin;Jesse]" ..
|
|
"image_button[0.4,4.4;2,4;skin_preview_jj.png;skin;JJ]" ..
|
|
"image_button[2.8,4.4;2,4;skin_preview_jean.png;skin;Jean]" ..
|
|
"image_button[5.2,4.4;2,4;skin_preview_julio.png;skin;Julio]" ..
|
|
sfinv.style.gui_bg .. sfinv.style.gui_bg_img .. sfinv.style.gui_slots
|
|
, false)
|
|
|
|
end,
|
|
on_player_receive_fields = function(self, player, context, fields)
|
|
if fields.skin then
|
|
local skin = "skin_" .. fields.skin:lower() .. ".png"
|
|
if minetest.check_player_privs(player, "review") then
|
|
skin = skin .. "^skin_overlay_admin.png"
|
|
end
|
|
local pmeta = player:get_meta()
|
|
pmeta:set_string("skin", skin)
|
|
player:set_properties({textures = {skin}})
|
|
end
|
|
end,
|
|
})
|
|
|
|
|
|
-- monitor players violating box boundries
|
|
local function check_player_is_in_box(name, box)
|
|
local player = minetest.get_player_by_name(name)
|
|
if not player or minetest.check_player_privs(player, "review") then
|
|
return
|
|
end
|
|
local pos = player:get_pos()
|
|
if pos.x < box.minp.x or pos.y < box.minp.y or pos.z < box.minp.z or
|
|
pos.x > box.maxp.x or pos.y > box.maxp.y or pos.z > box.maxp.z then
|
|
minetest.log("error", name .. " was outside of their box space at " ..
|
|
minetest.pos_to_string(vector.floor(pos)))
|
|
-- remove fly
|
|
local privs = minetest.get_player_privs(name)
|
|
privs.fly = nil
|
|
minetest.set_player_privs(name, privs)
|
|
--FIXME move them back to their box start pos
|
|
|
|
-- kill them
|
|
player:set_hp(player:get_hp() - 100)
|
|
end
|
|
end
|
|
|
|
local function player_box_check()
|
|
for name, box in pairs(boxes.players_in_boxes) do
|
|
check_player_is_in_box(name, box)
|
|
end
|
|
for name, box in pairs(boxes.players_editing_boxes) do
|
|
check_player_is_in_box(name, box)
|
|
end
|
|
-- reschedule
|
|
minetest.after(5.0, player_box_check)
|
|
end
|
|
|
|
local old_is_protected = minetest.is_protected
|
|
function minetest.is_protected(pos, name)
|
|
if minetest.check_player_privs(name, "server") then
|
|
return false
|
|
end
|
|
local box = boxes.players_in_boxes[name]
|
|
if not box then
|
|
box = boxes.players_editing_boxes[name]
|
|
end
|
|
if not box then
|
|
return true
|
|
end
|
|
if pos.x <= box.minp.x or pos.y <= box.minp.y or pos.z <= box.minp.z or
|
|
pos.x >= box.maxp.x or pos.y >= box.maxp.y or pos.z >= box.maxp.z then
|
|
return true
|
|
end
|
|
return old_is_protected(pos, name)
|
|
end
|
|
|
|
minetest.after(5.0, player_box_check)
|
|
|
|
minetest.register_chatcommand("attr", {
|
|
params = "attr server",
|
|
description = S("Show, and modify player attributes"),
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
local params = {}
|
|
for w in param:gmatch("%S+") do
|
|
table.insert(params, w)
|
|
end
|
|
if params[1] == "set" then
|
|
local p = minetest.get_player_by_name(params[2])
|
|
if not p then
|
|
return false, S("No such player.")
|
|
end
|
|
local pmeta = p:get_meta()
|
|
pmeta:set_string(params[3], params[4])
|
|
return true, S("Attribute set.")
|
|
elseif params[1] == "get" then
|
|
local p = minetest.get_player_by_name(params[2])
|
|
if not p then
|
|
return false, S("No such player.")
|
|
end
|
|
local pmeta = player:get_meta()
|
|
return true, params[3] .. "=" .. dump(pmeta:get_string(params[3]))
|
|
else
|
|
return false, S("Usage: /attr <get|set> <name> <attribute> <value>")
|
|
end
|
|
end,
|
|
})
|