samz/mods/bedz/api.lua
2022-04-09 13:50:11 +02:00

396 lines
11 KiB
Lua

local S, modname = ...
local singleplayer
if minetest.is_singleplayer() then
singleplayer = true
else
singleplayer = false
end
local multiplayer_timeofday = nil
local default_hours = 8
local _contexts = {}
local function get_context(name)
local context = _contexts[name] or {}
_contexts[name] = context
return context
end
local function compose_formspec(player_name, await)
local players_sleeping = 0
if not(singleplayer) then
players_sleeping = playerz.count_sleeping()
end
local formspec
if singleplayer or not(await) then
local context = get_context(player_name)
local hours = context.hours or tonumber(default_hours)
local timeofday = minetest.get_timeofday()
local btn_dawn_dusk, img_dawn_dusk, lbl_dawn_dusk
if timeofday > 0.2 and timeofday < 0.805 then
lbl_dawn_dusk = S("Dusk")
btn_dawn_dusk = "btn_dusk"
img_dawn_dusk = "moon.png^[transformR180"
else
lbl_dawn_dusk = S("Dawn")
btn_dawn_dusk = "btn_dawn"
img_dawn_dusk = "sun_icon.png"
end
formspec = [[
formspec_version[5]
size[6,4.25]
image[2,0;1,1;clock_white.png]
label[3,0.5;]]..helper.to_clock()..[[]
image_button_exit[2,1.75;2,1;;btn_hours;]]..hours.." "..S("Hours")..[[]
image_button_exit[1.5,3;1,1;]]..img_dawn_dusk..[[;]]..btn_dawn_dusk..[[;]]..lbl_dawn_dusk..[[]
button_exit[3.5,3;2,1;btn_leave;]]..S("Leave Bed")..[[]
scrollbaroptions[min=1;max=12;smallstep=1;largestep=12]
scrollbar[0.5,1;5,0.5;;scrollbar;]]..hours..[[]
]]
else --awaiting formspec
formspec = [[
formspec_version[5]
size[6,4.25]
label[0.5,0.5;]]..S("Players sleeping")..": "..tostring(players_sleeping)..[[]
label[0.5,1;]]..S("Connected players")..": "..tostring(playerz.count)..[[]
label[0.5,1.5;]]..S("Required players to sleep")..": "..tostring(math.floor(playerz.count/2)+ 1)..[[]
button_exit[3.5,3;2,1;btn_leave;]]..S("Leave Bed")..[[]
]]
end
return formspec
end
local function get_look_yaw(pos)
local rotation = minetest.get_node(pos).param2
if rotation > 3 then
rotation = rotation % 4 -- Mask colorfacedir values
end
if rotation == 1 then
return math.pi / 2, rotation
elseif rotation == 3 then
return -math.pi / 2, rotation
elseif rotation == 0 then
return math.pi, rotation
else
return 0, rotation
end
end
function bedz.check_bed(pos)
local node = minetest.get_node_or_nil(pos)
if (not node) or (minetest.get_node_group(node.name, "bed") == 0) then --not a bed
return false
else
return true
end
end
local function unmark_bed(pos)
if bedz.check_bed(pos) then
minetest.get_meta(pos):set_string("bedside", "") --unmark the bed
end
end
local function rest_player(player, rest_hours)
local hp = player:get_hp()
hp = hp + (rest_hours * 0.5)
player:set_hp(hp)
end
local function stop_sleep(player)
local meta = player:get_meta()
local player_name = player:get_player_name()
unmark_bed(minetest.string_to_pos(meta:get_string("bedz:bed_pos")))
playerphysics.remove_physics_factor(player, "speed", "bedz")
playerphysics.remove_physics_factor(player, "jump", "bedz")
playerphysics.remove_physics_factor(player, "gravity", "bedz")
playerz.set_status(player, "normal")
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
minetest.close_formspec(player_name, "bedz:form")
--playerz.player_attached[player_name] = false
end
local function awake(player, rest_hours)
if playerz.get_status(player) == "sleep" then
stop_sleep(player)
if rest_hours then
rest_player(player, rest_hours)
playerz.change_hunger(player, -(rest_hours*(playerz.max_hunger/playerz.starving_hours))) --decrease hunger
sound.play("player", player, "bedz_yawn")
end
return true
else
return false
end
end
local function close_all_forms()
for _, _player in ipairs(minetest.get_connected_players()) do
if playerz.get_status(_player) == "sleep" then
awake(_player, nil)
minetest.close_formspec(_player:get_player_name(), "bedz:form")
end
end
end
local function calculate_hours(timeofday)
local current_hour = helper.what_hour()
--minetest.chat_send_all(tostring(current_hour))
local hour = helper.what_hour(timeofday)
--minetest.chat_send_all(tostring(hour))
local hours
if (current_hour > hour) then
hours = 24 - current_hour + hour
else
hours = hour - current_hour
end
return tostring(hours)
end
local function multiplayer_sleep()
local players_sleeping = playerz.count_sleeping()
local slept
if multiplayer_timeofday and (players_sleeping > (playerz.count / 2)) then
local hours = calculate_hours(multiplayer_timeofday)
minetest.set_timeofday(multiplayer_timeofday)
for _, _player in ipairs(minetest.get_connected_players()) do
minetest.chat_send_player(_player:get_player_name(), "You have slept".." "..hours.." ".."hours")
awake(_player, hours) --awake all players
end
multiplayer_timeofday = nil
slept = true
else
slept = false
end
return slept, players_sleeping
end
local function sleep(pos, player)
local player_name = player:get_player_name()
local meta_bed = minetest.get_meta(pos)
meta_bed:set_string("bedside", player_name) --mark the bed
--playerz.player_attached[player_name] = true
-- physics, eye_offset, etc
player:set_eye_offset({x = 0, y = -13, z = 0}, {x = 0, y = 0, z = 0})
local yaw, param2 = get_look_yaw(pos)
player:set_look_horizontal(yaw)
local dir = minetest.facedir_to_dir(param2)
local player_pos = {
x = pos.x + dir.x / 3,
y = pos.y + 0.31,
z = pos.z + dir.z / 3
}
playerphysics.add_physics_factor(player, "speed", "bedz", 0)
playerphysics.add_physics_factor(player, "jump", "bedz", 0)
playerphysics.add_physics_factor(player, "gravity", "bedz", 0)
player:set_pos(player_pos)
player:get_meta():set_string("bedz:bed_pos", minetest.pos_to_string(pos))
playerz.set_status(player, "sleep")
local await
if singleplayer or (playerz.count == 1) then
await = false
else
local slept, players_sleeping = multiplayer_sleep()
if slept then
return
end
if (players_sleeping) == 1 and not(multiplayer_timeofday) then
await = false
else
await = true
end
end
minetest.show_formspec(player_name, "bedz:form", compose_formspec(player_name, await))
end
local function use_bed(pos, player)
local player_name = player:get_player_name()
-- Check if player is moving
if vector.length(player:get_velocity()) > 0.001 then
return false, "You have to stop moving before going to bed!"
end
--Check if player is attached to an object
if player:get_attach() then
return false, "You are already attached to another thing"
end
--Check if already in bed or bed occupied
local bedside = minetest.get_meta(pos):get_string("bedside")
--Check if really bed occupied. This is a case of a crash when a player is sleeping (no bed unmarking)
if not(bedside == "") then
local player_bedside = minetest.get_player_by_name(bedside)
if not(player_bedside) or not(playerz.get_status(player_bedside) == "sleep")
or not(minetest.string_to_pos(player_bedside:get_meta():get_string("bedz:bed_pos")) == pos) then
bedside = ""
unmark_bed(pos)
end
end
--Check if player is hungry
if playerz.is_starving(player) then
return false, "You are hungry"
end
if bedside == player_name then
return false, S("You are already in bed")
elseif not(bedside == "") then
return false, S("This bed is already occupied")
end
--Sleep
sleep(pos, player)
return true
end
local function place_bed(bed_name, placer, pointed_thing)
local above_pos = pointed_thing.above
local bed_dir = helper.dir_to_compass(placer:get_look_dir())
--minetest.chat_send_all("placer dir: "..minetest.pos_to_string(placer_dir))
local behind_pos = vector.offset(above_pos, bed_dir.x, bed_dir.y, bed_dir.z)
local node_behind = minetest.get_node_or_nil(behind_pos)
if node_behind and helper.get_nodedef_field(node_behind.name, "drawtype") == "airlike" then
local dir = minetest.dir_to_facedir(placer:get_look_dir()) or 0
minetest.set_node(above_pos, {name = bed_name, param2 = dir})
return true
else
return false
end
end
function bedz.register_bed(name, def)
local bed_name = modname..":"..name
minetest.register_node(bed_name, {
description = def.description,
inventory_image = def.inventory_image or "",
wield_image = def.wield_image or def.inventory_image,
drawtype = "mesh",
mesh = "bedz_simple_bed.obj",
tiles = def.tiles,
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
stack_max = 1,
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 3, bed = 1},
sounds = sound.wood(),
selection_box = {
type = "fixed",
fixed = def.selectionbox,
},
on_place = function(itemstack, placer, pointed_thing)
if place_bed(bed_name, placer, pointed_thing) then
itemstack:take_item()
end
return itemstack
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local bed_used, msg = use_bed(pos, clicker)
if not bed_used then
local player_name = clicker:get_player_name()
minetest.chat_send_player(player_name, msg)
end
end,
on_construct = function(pos)
unmark_bed(pos)
end,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
local bedside = meta:get_string("bedside")
if not(bedside == "") then
local player = minetest.get_player_by_name(bedside)
if player then
stop_sleep(player, nil)
end
end
end
})
if def.recipe then
minetest.register_craft({
output = bed_name,
type = "shaped",
recipe = def.recipe
})
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "bedz:form" then
return
end
local player_name = player:get_player_name()
local context = get_context(player_name)
local new_timeofday = nil
if fields.btn_dawn then
new_timeofday = 0.23
elseif fields.btn_dusk then
new_timeofday = 0.805
elseif fields.btn_hours then
local sleep_hours = tonumber(context.hours) or default_hours
local hours = sleep_hours/24
local timeofday = minetest.get_timeofday()
if (timeofday+hours) > 1 then
timeofday = (timeofday + hours) - 1
else
timeofday = timeofday + hours
end
new_timeofday = timeofday
elseif fields.quit then
awake(player, nil)
if not singleplayer then
multiplayer_timeofday = nil
close_all_forms()
end
return
elseif fields.scrollbar then
local scrollbar = minetest.explode_scrollbar_event(fields.scrollbar)
if scrollbar.type == "CHG" then
context.hours = scrollbar.value
minetest.show_formspec(player_name, "bedz:form", compose_formspec(player_name))
return
end
end
if singleplayer or (playerz.count == 1) then
--singleplayer or only one player in multiplayer
local hours = calculate_hours(new_timeofday)
minetest.chat_send_player(player_name, S("You have slept").." "..hours.." "
..S("hours"))
minetest.set_timeofday(new_timeofday)
awake(player, hours)
else --multiplayer, #players>1
if new_timeofday then --set the multiplayer new timeofday for the first time
multiplayer_timeofday = new_timeofday
end
local slept = multiplayer_sleep()
if slept then
return
else --awating formspec
minetest.show_formspec(player_name, "bedz:form", compose_formspec(player_name, true))
end
end
end)
--Player Status
minetest.register_on_dieplayer(function(player, reason)
awake(player, nil)
end)
minetest.register_on_leaveplayer(function(player)
awake(player, nil)
_contexts[player:get_player_name()] = nil
end)
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
return awake(player, nil)
end)