345 lines
12 KiB
Lua
345 lines
12 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.health_display = minetest.add_entity(p:get_pos(), "display")
|
|
e.health_display:set_attach(p, nil, vector.new(0, 17, 0))
|
|
e:update_hp(p:get_hp())
|
|
|
|
return e
|
|
end,
|
|
update_hp = function(m, hp)
|
|
if not (m.health_display and m.health_display:is_valid()) then
|
|
m.health_display = minetest.add_entity(m.object:get_pos(), "display")
|
|
m.health_display:set_attach(m.object, nil, vector.new(0, 17, 0))
|
|
end
|
|
local tx = "[combine:90x90"
|
|
for i = math.floor(hp /2), math.floor(m.props.hp_max /2) -1 do
|
|
tx = tx..":"..(i *9)..",0=rgt_heart_empty.png"
|
|
end
|
|
for i = 0, math.floor(hp /2) -1 do
|
|
tx = tx..":"..(i *9)..",0=rgt_heart.png"
|
|
end
|
|
if hp %2 ~= 0 then
|
|
tx = tx..":"..((math.floor(hp /2)) *9)..",0=rgt_heart.png\\^[fill\\:5x9\\:4,0\\:#000\\^[makealpha\\:#000"
|
|
end
|
|
m.health_display:set_properties {
|
|
visual = "sprite",
|
|
textures = {tx},
|
|
shaded = false
|
|
}
|
|
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()
|
|
local 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.
|
|
elseif not (m.health_display and m.health_display:is_valid()) then
|
|
m.health_display = minetest.add_entity(m.object:get_pos(), "display")
|
|
m.health_display:set_attach(m.object, nil, vector.new(0, 17, 0))
|
|
end
|
|
|
|
m.last_time = time
|
|
end
|
|
|
|
-- Custom on-hover effects
|
|
|
|
m.pointed_node = nil
|
|
|
|
local pointed_found = false
|
|
for pointed in minetest.raycast(pos, pos +(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
|
|
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
|
|
|
|
-- 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
|
|
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
|
|
}
|
|
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_leaveplayer(function(p)
|
|
ns.players[p:get_player_name()] = nil
|
|
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_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
|
|
})
|