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,
})