firefly/mods/firefly_player/init.lua
2026-02-14 12:37:11 -05:00

432 lines
14 KiB
Lua

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)