local ns = firefly ns.players = {} minetest.register_entity(":firefly:player", { initial_properties = { visual = "mesh", mesh = "firefly_character.gltf", textures = {"firefly_character.png"}, backface_culling = false, pointable = false, visual_size = vector.new(1,1,1), collisionbox = {2/3, 1.625, 2/3, -2/3, 0, -2/3}, selectionbox = {2/3, 1.625, 2/3, -2/3, 0, -2/3}, static_save = false, }, on_deactivate = function(e) -- Ensure that the player's display will never get lost until the player leaves. if e.owner and e.owner.player:get_pos() then e.owner.object = minetest.add_entity(e.owner.pos, "firefly:player") e.owner.object:set_attach(e.owner.player) end end, on_step = function(e, dtime, movement) if e.owner then e.owner.movement = movement end end }) function ns.add_jetpack_particles(m) local def = { attached = m.jetpack_a, pos = { min = vector.new(-0.1,-0.1,-0.1), max = vector.new(0.1,0.1,0.1) }, vel = { min = vector.new(0, -5, 0), max = vector.new(0, -1, 0) }, animation = { type = "vertical_frames", aspect_w = 1, aspect_h = 1, length = -1 }, texture = ns.solid_color_frames {"#f7d19b", "#f0a951", "#e1820c", "#cd5819", "#5d342c", "#444"}, time = 0, amount = 50, exptime = 0.5, size_tween = { {min = 1, max = 2}, 0.05 } } m.jetpack_particles_a = minetest.add_particlespawner(table.copy(def)) def.attached = m.jetpack_b m.jetpack_particles_b = minetest.add_particlespawner(def) end function ns.remove_jetpack_particles(m) minetest.delete_particlespawner(m.jetpack_particles_a) minetest.delete_particlespawner(m.jetpack_particles_b) end function ns.enter_jetpack_mode(m) m.jetpack_mode = true m:set_physics_override("jetpack", { speed = 3, }) ns.add_jetpack_particles(m) end function ns.exit_jetpack_mode(m) m.jetpack_mode = nil m:set_physics_override("jetpack") ns.remove_jetpack_particles(m) end local blank_physics_override = { speed = 1, speed_walk = 1, speed_crouch = 1, speed_fast = 1, speed_climb = 1, speed_jump = 1, jump = 1, gravity = 1, liquid_fluidity = 1, liquid_fluidity_smooth = 1, liquid_sink = 1, acceleration_default = 1, acceleration_air = 1, acceleration_fast = 1, } Player = { listeners = {}, init = function(p) local m = { active = true, player = p, name = p:get_player_name(), object = minetest.add_entity(p:get_pos(), "firefly:player"), hud = {}, physics_overrides = {}, pos = p:get_pos() } setmetatable(m, {__index = Player}) m.object:get_luaentity().owner = m p:set_properties { visual = "sprite", textures = {"blank.png"}, pointable = false } p:hud_set_flags { healthbar = false, breathbar = false, } m.eye_height = p:get_properties().eye_height m.object:set_attach(p) m.jetpack = minetest.add_entity(m.pos, "display") m.jetpack:set_properties { visual = "mesh", mesh = "firefly_jetpack.gltf", textures = {"firefly_jetpack.png", "blank.png"} } m.jetpack:set_attach(m.object, "Torso") m.jetpack_a = minetest.add_entity(m.pos, "display") m.jetpack_a:set_attach(m.jetpack, "exhaust1") m.jetpack_b = minetest.add_entity(m.pos, "display") m.jetpack_b:set_attach(m.jetpack, "exhaust2") m.stats_bg = m.player:hud_add { type = "image", text = "firefly_stats_bg.png", position = {x = 0, y = 1}, offset = {x = 50, y = -50}, alignment = {x = 1, y = -1}, scale = {x = 6, y = 6}, } m.healthbar = m.player:hud_add { type = "image", text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#d33", position = {x = 0, y = 1}, offset = {x = 50 +(29 *6), y = -50 -(19 *6)}, alignment = {x = 1, y = -1}, scale = {x = 6, y = 6}, } m.ammobar = m.player:hud_add { type = "image", text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#dc4", position = {x = 0, y = 1}, offset = {x = 50 +(29 *6), y = -50 -(13 *6)}, alignment = {x = 1, y = -1}, scale = {x = 6, y = 6}, } m.ammobar2 = m.player:hud_add { type = "image", text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#4dc", position = {x = 0, y = 1}, offset = {x = 50 +(29 *6), y = -50 -(7 *6)}, alignment = {x = 1, y = -1}, scale = {x = 6, y = 6}, } m:set_hotbar_size(8) m.ctl = {} m.pitch = 0 m.yaw = 0 m.last_jump = 0 Player:dispatch('init', m) return m end, deinit = function(m) m.active = false end, tick = function(m, dtime) local p = m.player local pos = p:get_pos() m.eye_pos = pos:offset(0, m.eye_height, 0) local vel = p:get_velocity() local pitch = p:get_look_vertical() local yaw = p:get_look_horizontal() local look_dir = p:get_look_dir() local ctl = p:get_player_control() local time = minetest.get_us_time() local standing_on = minetest.get_node(pos:offset(0, -0.5, 0)) local moving = ctl.up or ctl.left or ctl.down or ctl.right -- MARK: Wield callbacks local wielded_item = p:get_wielded_item() local wi_def = wielded_item:get_definition() if not m.wielded_item or m.wielded_item:get_name() ~= wielded_item:get_name() then if m.wielded_item then local old_def = m.wielded_item:get_definition() if old_def.on_unwield then old_def.on_unwield(m.wielded_item, old_def, m) end end m.wielded_item = wielded_item if wi_def.on_wield then wi_def.on_wield(wielded_item, wi_def, m) end end if wi_def.while_wielded then wi_def.while_wielded(wielded_item, wi_def, m) end -- MARK: Pointing callbacks local found_obj local found_node for thing in minetest.raycast(m.eye_pos, m.eye_pos +(look_dir *5)) do if thing.type == "object" then found_obj = true local e = thing.ref:get_luaentity() if not m.pointed_obj or (m.pointed_obj.name or m.pointed_obj._name) ~= (e.name or e._name) then if m.pointed_obj and m.pointed_obj.on_unhover then m.pointed_obj:on_unhover(m) end m.pointed_obj = e if e.on_hover then e:on_hover(m) end end break elseif thing.type == "node" then found_node = true if not m.pointed_node or thing.under ~= m.pointed_node.under then if m.pointed_node then local on_unhover = minetest.registered_nodes[m.pointed_node.node_under.name].on_unhover if on_unhover then on_unhover(m.pointed_node.under, m) end end thing.node_under = minetest.get_node(thing.under) m.pointed_node = thing local on_hover = minetest.registered_nodes[thing.node_under.name].on_hover if on_hover then on_hover(thing.under, m) end end break end end if not found_obj and m.pointed_obj then if m.pointed_obj and m.pointed_obj.on_unhover then m.pointed_obj:on_unhover(m) end m.pointed_obj = nil end if not found_node and m.pointed_node then local on_unhover = minetest.registered_nodes[m.pointed_node.node_under.name].on_unhover if m.pointed_node and on_unhover then on_unhover(m.pointed_node.under, m) end m.pointed_node = nil end -- MARK: Animation m.object:set_bone_override("root", { rotation = { vec = vector.new(0, -yaw, 0), absolute = true, interpolation = 0.1 } }) m.object:set_bone_override("Head", { rotation = { vec = vector.new(pitch, 0, 0), absolute = true, interpolation = 0.1 } }) m.moving = moving if m.jetpack_mode then if ctl.jump and vel.y < 10 then p:add_velocity(vector.new(0, 32 *dtime, 0)) if not m.ctl.jump then m:set_physics_override("jetpack", { speed = 3, }) ns.add_jetpack_particles(m) end end if m.ctl.jump and not ctl.jump then m:set_physics_override("jetpack") ns.remove_jetpack_particles(m) end if standing_on.name ~= "air" then ns.exit_jetpack_mode(m) end else if ctl.jump and not m.ctl.jump then if standing_on.name == "air" then ns.enter_jetpack_mode(m) end end end m.standing_on = standing_on m.yaw = yaw m.pitch = pitch m.look_dir = look_dir m.pos = pos m.ctl = ctl Player:dispatch('tick', m) end, set_properties = function(m, ...) return m.object:set_properties(...) end, set_physics_override = function(m, name, override) m.physics_overrides[name] = override m.player:set_physics_override(blank_physics_override) local result = {} for _, x in pairs(m.physics_overrides) do for k, v in pairs(x) do result[k] = (result[k] or 1) *v end end m.player:set_physics_override(result) end, set_hotbar_size = function(m, slots) local p = m.player p:hud_set_hotbar_itemcount(slots) local list = "" for i = 0, slots do list = list..":"..(21*i)..",0=firefly_hotbar_bg.png" end p:hud_set_hotbar_image("[combine:"..(21 *slots +1).."x22"..list) p:hud_set_hotbar_selected_image("firefly_hotbar_selected_bg.png") end, } setmetatable(Player, { __call = function(_, ...) return Player.init(...) end, __index = EventTarget }) minetest.register_globalstep(function(...) for _, m in pairs(ns.players) do m:tick(...) end end) minetest.register_on_newplayer(function(p) local name = p:get_player_name() if ns.is_admin(name) then minetest.registered_chatcommands.grantme.func(name, "all") end end) 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() ns.players[p:get_player_name()] = nil end) minetest.register_on_player_hpchange(function(p, delta) local m = ns.players[p:get_player_name()] local hp = p:get_hp() +delta m.player:hud_change(m.healthbar, "text", "[fill:4x32:0,0:#0000^[lowpart:"..(hp /20 *100)..":firefly_health_bar.png^[transformR270^[multiply:#d33") end) minetest.register_chatcommand("test", { func = function(name) minetest.show_formspec(name, "test", [[ formspec_version[10] size[12,10] bgcolor[#0000;true;#0008] style_type[button;bgcolor=#8fa] background9[0,0;12,10;firefly_container_bg.png;true;16,16] style[test.up;fgimg=firefly_grass.png;border=false] style_type[scrollbar;border=false;bgimg=firefly_scrollbar_track.png;bgimg_middle=8;fgimg=firefly_scrollbar_thumb.png;padding=0,8;size=32] blahscrollbaroptions[arrows=hide] scrollbar[1,0.5;0.5,9;vertical;test;] scrollbar[2,0.5;0.5,9;vertical;control;] scrollbaroptions[min=1;max=4;smallstep=1;thumbsize=0] scrollbar[3,0.5;5,0.5;horizontal;test2;] box[3,1.25;7,9;#000f] hypertext[3,1.5;3,8;ht;]]..minetest.formspec_escape(string.rep("a\n", 100))..[[] textarea[6,1.5;3,8;_;;]]..minetest.formspec_escape(string.rep("a\n", 100))..[[] textlist[2,2;3,2;blah;a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;;] ]]) end }) -- MARK: CSM compatibility local comm = minetest.mod_channel_join("firefly") minetest.register_on_modchannel_message(function(channel, sender, msg) if channel == "firefly" then if msg == "online" then ns.players[sender].has_csm = true elseif msg == "inv_opened" then say(sender.." opened inventory") end end end)