red_glazed_terracotta/mods/rgt_player/init.lua

499 lines
18 KiB
Lua

local ns = rgt
ns.players = {}
Player = {
new = function(p)
local e = setmetatable({
name = p:get_player_name(),
object = p
}, {__index = Player})
local inv = p:get_inventory()
inv:set_size("hand", 1)
e:set_hotbar_size(8)
e.textures = {_textures = {}}
setmetatable(e.textures, {
__newindex = function(tbl, key, value)
tbl._textures[idx] = value
p:set_properties {
textures = tbl._textures
}
end,
__index = e.textures._textures
})
e.hunger_bar = p:hud_add {
type = "statbar",
position = {x=0.5,y=1},
offset = {x=10,y=-96},
scale = {x=4,y=4},
alignment = {x=-1, y=-1},
size = {x=27,y=27},
text = "rgt_pumpkin_alt.png",
number = 20,
item = 20,
text2 = "rgt_pumpkin_empty.png"
}
e.props = p:get_properties()
e.eye_height = 1.6
e.hud = {}
e.poi = {}
e.wearing = {}
e.logical_height_offset = 0
e:update_hp(p:get_hp())
e.object:set_formspec_prepend [[
bgcolor[#000;true]
background9[0,0;0,0;rgt_container_bg.png;true;16,16]
style_type[button;border=false;bgimg=rgt_button_bg.png;bgimg_middle=8,8]
listcolors[#fff0;#fff3;#0000;#444;#aaa]
]]
e.inv = Inventory(p)
return e
end,
update_inv = function(m)
local fs = "\
formspec_version[10]\
size[12,10]\
"
for x = 0, 7 do
for y = 0, 3 do
fs = fs.."\
image["..(x *1.25 +1.125 -0.0625)..","..(y *1.25 +4.5 -0.0625)..";1.14,1.14;rgt_other_button_bg.png;8,8]\
"
end
end
fs = fs.."\
list[current_player;main;1.125,4.5;8,4;]\
list[current_player;craft;3,0.5;3,3;]\
listring[]\
list[current_player;craftpreview;7,1;1,1;]\
"
m.object:set_inventory_formspec(fs)
end,
set_logical_height_offset = function(m, offset)
m.logical_height_offset = offset
m.health_display:set_attach(m.object, nil, vector.new(0, 22 +m.logical_height_offset, 0))
end,
update_hp = function(m, hp)
if not (m.health_display and m.health_display:is_valid()) then
local pos = m.object:get_pos() or vector.zero()
m.health_display = minetest.add_entity(pos, "rgt_player:health_display")
m.health_display:set_attach(m.object, nil, vector.new(0, 22 +m.logical_height_offset, 0))
m.health_display:get_luaentity().owner = m
end
local tx = "[combine:90x90"
if hp < m.props.hp_max then
for i = math.floor(hp /2), math.floor(m.props.hp_max /2) -1 do
tx = tx..":"..(i *9)..",40=rgt_heart_empty.png"
end
for i = 0, math.floor(hp /2) -1 do
tx = tx..":"..(i *9)..",40=rgt_heart.png"
end
if hp %2 ~= 0 then
tx = tx..":"..((math.floor(hp /2)) *9)..",40=rgt_heart.png\\^[fill\\:5x9\\:4,0\\:#000\\^[makealpha\\:#000"
end
end
m.health_display:set_properties {
visual = "sprite",
textures = {tx},
shaded = false
}
end,
set_wielditem = function(m, def)
if not def then def = {name = ""} end
if not (m.wielditem_display and m.wielditem_display:is_valid()) then
local mp = m.object:get_pos() or vector.zero()
m.wielditem_display = minetest.add_entity(mp, "display")
m.wielditem_display:set_properties {
visual = "item"
}
local e = m.wielditem_display:get_luaentity()
e.owner = m
end
local pos = def.type == "node" and vector.new(0.2, -6, 2) or vector.new(0.2, -5.5, 2.5)
if def._wield_pos then
pos = pos +def._wield_pos
end
local rot = def._wield_rot or def.type == "node" and vector.new(-45, 165, -30) or (def.type == "tool" and vector.new(90, -45, 90) or vector.new(-90, -100, 90))
local scale = def._wield_scale or vector.new(0.2, 0.2, 0.2)
if type(scale) == "number" then
scale = vector.new(scale, scale, scale)
end
if def.name == "air" or def.name == "" or not (def.tiles or def.wield_image or def.inventory_image) then
m.wielditem_display:set_properties {
visual = "sprite",
textures = {"blank.png"}
}
m.wielditem_display:set_attach(m.object, "RightArm", pos, rot)
return
end
m.wielditem_display:set_properties {
visual = "item",
visual_size = scale,
wield_item = def.name
}
m.wielditem_display:set_attach(m.object, "RightArm", pos, rot)
-- Apparently this forces a resend so that properties and attachment position will sync up?
m.wielditem_display:set_pos(m.wielditem_display:get_pos())
end,
tick = function(m, dtime)
local time = minetest.get_us_time()
local p = m.object
if not p or not p:is_player() then
return
end
local pitch = p:get_look_vertical()
local yaw = p:get_look_horizontal()
m.dir = p:get_look_dir()
local pos = p:get_pos()
pos.y = pos.y +m.eye_height
-- Animation
if not m.in_third_person then
local c = p:get_player_control()
local moving = c.up or c.down or c.left or c.right
if moving then
m.moving = true
if c.aux1 and c.up then
if p:get_animation().y ~= 2 then p:set_animation({x=1, y=2}, 1.5, 0.2, true) end
p:set_physics_override{
speed = 1.5
}
else
if p:get_animation().y ~= 1 then p:set_animation({x=0, y=1}, 1.5, 0.2, true) end
p:set_physics_override{
speed = 1
}
end
else
m.moving = false
if p:get_animation().y ~= 0 then p:set_animation({x=0, y=0}) end
end
if not m.rot then m.rot = 0 end
if moving then
local fac = 0
if c.left then fac = 30 elseif c.right then fac = -30 end
m.rot = yaw +math.rad(fac)
elseif math.abs(yaw -m.rot) > math.rad(40) then
m.rot = m.rot +(yaw -(m.yaw or 0))
end
m.rot = m.rot %math.rad(360)
m.yaw = yaw
p:set_bone_override("Head", {
rotation = {vec = vector.new(math.min(math.max(pitch, math.rad(-60)), math.rad(60)),-(yaw -m.rot),0), interpolation = 0.1, absolute = true}
})
p:set_bone_override("root", {
rotation = {vec = vector.new(0,yaw -m.rot,0), interpolation = 0.1, absolute = true}
})
m.ctl = c
else -- Third-person camera
local c = m.camera.ref
local me = m.focus and m.focus.ref or false
local ctl = p:get_player_control()
-- local lv = p:get_look_vertical()
-- local lh = p:get_look_horizontal()
-- local ldir = p:get_look_dir()
local ppos = c:get_pos()
if not ppos then
m:enter_third_person_view(m.third_person_focus, me ~= false)
return
end
local dir = vector.new(0,0,1):rotate(vector.new(math.rad(-45),yaw,0))
-- if lv < 0 then
-- lv = 0
-- p:set_look_vertical(0)
-- end
local radius = m.third_person_camera_radius or 10
local t = (me and me:get_pos() or m.third_person_focus) +(-dir *radius)
-- local standing_on = minetest.get_node(pos:offset(0, -0.05, 0))
-- local can_jump = minetest.registered_nodes[standing_on.name].walkable
local dist = math.min(radius *2, t:distance(ppos) ^2)
local vel = t:direction(ppos) *dist
local tvel = vector.zero()
local was_moving = m.ctl.up or m.ctl.down or m.ctl.left or m.ctl.right or m.ctl.jump or m.ctl.sneak
local moving = ctl.up or ctl.down or ctl.left or ctl.right
-- if e.jumping == 0 and can_jump and ctl.jump then
-- e.jumping = 10
-- tvel = tvel +vector.new(0,5,0)
-- end
if ctl.jump then
tvel.y = 3
elseif ctl.sneak then
tvel.y = -3
end
if moving then
dir.y = 0
v_normalize(dir)
local rot = 0
if ctl.up then
if ctl.left then
rot = math.rad(45)
elseif ctl.right then
rot = math.rad(-45)
end
elseif ctl.down then
if ctl.left then
rot = math.rad(180 -45)
elseif ctl.right then
rot = math.rad(180 +45)
else
rot = math.rad(180)
end
elseif ctl.left then
rot = math.rad(90)
elseif ctl.right then
rot = math.rad(-90)
end
dir = dir:rotate(vector.new(0,rot,0))
if me then me:set_velocity(tvel +(dir *5)) end
vel = vel -(dir *5)
-- if not was_moving then
-- me:set_animation({x=0, y=1}, 1.5, 0.2, true)
-- end
-- me:set_bone_override("root", {
-- rotation = {
-- vec = -dir:dir_to_rotation(), interpolation = 0.07, absolute = true
-- }
-- })
elseif was_moving and me then
-- me:set_animation({x=0, y=0})
me:set_velocity(tvel)
end
-- e.jumping = math.max(0, e.jumping -1)
--vel.y = 0
c:set_velocity(-vel)
m.ctl = ctl
end
-- Health regen
-- TODO: Move to combat mod
if time -(m.last_time or 0) >= 10 *1000000 then
local hp = p:get_hp()
if hp < p:get_properties().hp_max then
p:set_hp(hp +1)
-- Make sure the health display is still loaded.
end
if not (m.health_display and m.health_display:is_valid()) then
m:update_hp(hp)
end
m.last_time = time
end
-- Run on-hover callbacks
m.pointed_node = nil
local pointed_found = false
for pointed in minetest.raycast(pos, pos +(m.dir *7)) do -- TODO: Automatic range
if pointed and pointed.type == "object" then
local e = pointed.ref:get_luaentity()
if e then
local names_match = m.pointed_obj and (m.pointed_obj._name or m.pointed_obj.name) == (e._name or e.name)
if m.pointed_obj and not names_match then
if m.pointed_obj.on_unhover then
m.pointed_obj:on_unhover(m)
end
m.hover_trigger_range = nil
end
if (m.pointed_obj and not names_match and e.on_hover) or not m.pointed_obj then
if e.on_hover then
m.hover_trigger_range = e:on_hover(m) or 8
end
pointed_found = true
m.pointed_obj = e
break
elseif m.pointed_obj and names_match and e.on_hover then
pointed_found = true
break
end
end
elseif pointed and pointed.type == "node" and not m.pointed_node then
pointed.node_under = minetest.get_node(pointed.under)
m.pointed_node = pointed
end
end
if not pointed_found and m.pointed_obj then
if m.pointed_obj.on_unhover then
m.pointed_obj:on_unhover(m)
end
m.pointed_obj = nil
m.hover_trigger_range = nil
end
m.pos = pos
-- Hold-to-interact handling
if m.interacting_with and m.ctl.place then
if not m.interaction_start then
m.interaction_start = time
m.interaction_marker = minetest.add_entity(m.pointed_node.under, "display")
m.interaction_marker:set_properties {
visual = "sprite",
textures = {"rgt_interact_progress_0.png"}
}
else
if time -m.interaction_start > 1100000 then
m.interaction_marker:remove()
minetest.registered_nodes[minetest.get_node(m.interacting_with).name].on_interact(m.interacting_with, m)
elseif time -m.interaction_start > 1000000 then
m.interaction_marker:set_properties {
textures = {"rgt_interact_progress_100.png"}
}
elseif time -m.interaction_start > 750000 then
m.interaction_marker:set_properties {
textures = {"rgt_interact_progress_75.png"}
}
elseif time -m.interaction_start > 500000 then
m.interaction_marker:set_properties {
textures = {"rgt_interact_progress_50.png"}
}
elseif time -m.interaction_start > 250000 then
m.interaction_marker:set_properties {
textures = {"rgt_interact_progress_25.png"}
}
end
end
elseif m.interacting_with and not m.ctl.place then
m.interacting_with = nil
m.interaction_start = nil
m.interaction_marker:remove()
m.interaction_marker = nil
end
-- Run on_wield callbacks
local w = p:get_wielded_item()
local wname = w:get_name()
local def = minetest.registered_items[wname]
if m.prev_wielditem ~= wname then
if m.prev_wielditem then
local def = minetest.registered_items[m.prev_wielditem]
local onunselect = def and def.on_unwield
if onunselect then onunselect(m) end
end
m.prev_wielditem = wname
m:set_wielditem(def)
local onselect = def and def.on_wield
if onselect then onselect(m, w) end
end
local while_wielded = def and def.while_wielded
if while_wielded then while_wielded(m, w) end
end,
set_hotbar_size = function(m, slots)
local p = m.object
p:hud_set_hotbar_itemcount(slots)
local list = ""
for i = 0, slots do
list = list..":"..(21*i)..",0=rgt_hotbar.png"
end
p:hud_set_hotbar_image("[combine:"..(21 *slots +1).."x22"..list)
p:hud_set_hotbar_selected_image("rgt_hotbar_selected.png")
end,
deinit = function(m)
m.health_display:remove()
m.wielditem_display:remove()
rgt.players[m.name] = nil
end
}
setmetatable(Player, {
__call = function(_, ...) return Player.new(...) end
})
minetest.register_craftitem(":red_glazed_terracotta:hand", {
inventory_image = "rgt_dirt.png",
tool_capabilities = {
groupcaps = {
hand_breakable = {times = {[3] = 0.7}, uses = 0, maxlevel = 1}
}
}
})
minetest.register_alias("hand", "red_glazed_terracotta:hand")
-- TODO: Replace builtin health system with custom health system
minetest.hud_replace_builtin("health", {
type = "statbar",
position = {x=0.5,y=1},
offset = {x=-27 *10 -10,y=-96},
scale = {x=4,y=4},
alignment = {x=-1, y=-1},
size = {x=27,y=27},
text = "rgt_heart.png",
text2 = "rgt_heart_empty.png"
})
minetest.register_on_joinplayer(function(p)
ns.players[p:get_player_name()] = Player(p)
end)
minetest.register_on_mods_loaded(function()
minetest.register_on_leaveplayer(function(p)
ns.players[p:get_player_name()]:deinit()
end)
end)
minetest.register_globalstep(function(time)
for _, x in pairs(rgt.players) do
x:tick(time)
end
end)
minetest.register_on_player_hpchange(function(p, hp_change)
rgt.players[p:get_player_name()]:update_hp(p:get_hp() +hp_change)
end)
minetest.register_entity("rgt_player:health_display", {
initial_properties = {
visual = "sprite",
textures = {"blank.png"},
pointable = false,
static_save = false
},
on_activate = function(e)
e.object:set_armor_groups{immortal = 1}
end,
on_detach = function(e)
e.object:remove()
end,
on_deactivate = function(e)
if e.owner then
minetest.after(0, function() e.owner:update_hp(e.owner.object:get_hp()) end)
end
end
})
minetest.register_chatcommand("hungerbar", {
func = function(name, args)
local m = rgt.players[name]
if args == "pumpkin_alt" then
m.object:hud_change(m.hunger_bar, "text", "rgt_"..args..".png")
else
m.object:hud_change(m.hunger_bar, "text", "rgt_pumpkin.png")
end
end
})
minetest.register_chatcommand("wi", {
func = function(name, args)
local m = rgt.players[name]
local x, y, z = args:match "(%-?%d+)%s+(%-?%d+)%s+(%-?%d+)"
m.wielditem_display:set_attach(m.object, "RightArm", vector.new(0.2, -5.5, 2.5), vector.new(tonumber(x), tonumber(y), tonumber(z)))
end
})