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: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 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, 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, 40, 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) 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 -- Custom on-hover effects 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 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 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() 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_leaveplayer(function(p) ns.players[p:get_player_name()]:deinit() 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 })