376 lines
14 KiB
Lua
376 lines
14 KiB
Lua
|
|
local ns = artifact
|
|
ns.players = {}
|
|
local db = minetest.get_mod_storage()
|
|
|
|
include "radial_menu.lua"
|
|
|
|
Player = setmetatable({
|
|
new = function(p)
|
|
local m = setmetatable({
|
|
object = p,
|
|
pitch = 0,
|
|
yaw = 0
|
|
}, {__index = Player})
|
|
|
|
m.name = p:get_player_name()
|
|
m.meta = p:get_meta()
|
|
m.character = m.meta:get("character") or "key"
|
|
|
|
m.inv = p:get_inventory()
|
|
m.inv:set_stack("main", 1, ItemStack("input_"..m.character))
|
|
|
|
-- Generic black sky, since the whole game takes place underground.
|
|
p:set_sky{
|
|
type = "basic",
|
|
base_color = "#000",
|
|
clouds = false
|
|
}
|
|
p:set_sun{visible = false}
|
|
p:set_moon{visible = false}
|
|
p:set_stars{visible = false}
|
|
|
|
p:set_properties {
|
|
visual = "mesh",
|
|
mesh = "artifact_character.gltf",
|
|
shaded = false
|
|
}
|
|
|
|
if m.character == "vix" then
|
|
artifact.apply_vix(m)
|
|
else
|
|
artifact.apply_key(m)
|
|
end
|
|
|
|
p:hud_set_flags {
|
|
healthbar = false,
|
|
breathbar = false,
|
|
hotbar = artifact.debug,
|
|
minimap = false,
|
|
basic_debug = false
|
|
}
|
|
|
|
m.hud = {}
|
|
m.poi = {}
|
|
m:create_hud()
|
|
|
|
m:set_hotbar_size(8)
|
|
|
|
m.ctl = p:get_player_control()
|
|
|
|
return m
|
|
end,
|
|
tick = function(m)
|
|
local time = minetest.get_us_time()
|
|
local p = m.object
|
|
local pos = p:get_pos()
|
|
local yaw = p:get_look_horizontal()
|
|
local pitch = p:get_look_vertical()
|
|
local dir = p:get_look_dir()
|
|
local vel = p:get_velocity()
|
|
local speed = vel:length()
|
|
m.pos = pos
|
|
m.pos.y = m.pos.y +m.eye_height
|
|
|
|
-- MARK: Pointing callbacks
|
|
|
|
local pointed_found = nil
|
|
m.pointed_node = nil
|
|
for x in minetest.raycast(m.pos, m.pos +(dir *5)) do
|
|
if x and x.type == "object" then
|
|
local e = x.ref:get_luaentity()
|
|
-- Ignore players.
|
|
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
|
|
if m.pointed_obj.on_interact and m.interaction_marker then
|
|
m.object:hud_remove(m.interaction_marker)
|
|
m.interaction_marker = nil
|
|
m.interaction_start = nil
|
|
end
|
|
end
|
|
if e.on_interact and not e._no_interact and (not names_match or names_match and not m.interaction_marker) then
|
|
if m.interaction_marker then m.object:hud_remove(m.interaction_marker) end
|
|
local dst = e.object:get_pos()
|
|
if e._interact_marker_offset then dst = dst +e:_interact_marker_offset() end
|
|
m.interaction_marker = m.object:hud_add {
|
|
type = "image_waypoint",
|
|
world_pos = dst,
|
|
scale = {x=3, y=3},
|
|
text = "artifact_rmb.png"
|
|
}
|
|
end
|
|
if (m.pointed_obj and not names_match and e.on_hover) or not m.pointed_obj then
|
|
if e.on_hover then
|
|
e:on_hover(m)
|
|
end
|
|
pointed_found = true
|
|
m.pointed_obj = e
|
|
break
|
|
elseif m.pointed_obj and names_match then
|
|
pointed_found = true
|
|
break
|
|
end
|
|
end
|
|
elseif x and x.type == "node" then
|
|
m.pointed_node = x
|
|
if m.pointed_obj then
|
|
if m.pointed_obj.on_unhover then
|
|
m.pointed_obj:on_unhover(m)
|
|
end
|
|
if m.pointed_obj.on_interact and m.interaction_marker then
|
|
m.object:hud_remove(m.interaction_marker)
|
|
m.interaction_marker = nil
|
|
m.interaction_start = nil
|
|
end
|
|
m.pointed_obj = nil
|
|
end
|
|
break
|
|
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
|
|
if m.pointed_obj.on_interact and m.interaction_marker then
|
|
m.object:hud_remove(m.interaction_marker)
|
|
m.interaction_marker = nil
|
|
m.interaction_start = nil
|
|
end
|
|
m.pointed_obj = nil
|
|
end
|
|
local ctl = m.object:get_player_control()
|
|
|
|
-- MARK: Animations
|
|
|
|
local moving = (ctl.up or ctl.down or ctl.left or ctl.right) and speed > 0.1
|
|
if moving then
|
|
m.moving = true
|
|
if ctl.aux1 and ctl.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 ctl.left then fac = 30 elseif ctl.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.pi *2)
|
|
|
|
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}
|
|
})
|
|
|
|
-- MARK: Progressive interaction
|
|
|
|
if ctl.place and m.ctl.place and m.pointed_obj and m.pointed_obj.on_interact and not m.pointed_obj._no_interact then
|
|
if not m.interaction_start then
|
|
m.interaction_start = time
|
|
else
|
|
local duration = (m.pointed_obj._interact_time or 1) *1000000
|
|
local progress = (time -m.interaction_start) /duration
|
|
if progress > 1.1 then
|
|
m.pointed_obj:on_interact(m)
|
|
m.interaction_start = nil
|
|
m.object:hud_remove(m.interaction_marker)
|
|
m.interaction_marker = nil
|
|
elseif progress > 1 then
|
|
m.object:hud_change(m.interaction_marker, "text", "artifact_rmb_100.png")
|
|
elseif progress > 0.75 then
|
|
m.object:hud_change(m.interaction_marker, "text", "artifact_rmb_75.png")
|
|
elseif progress > 0.5 then
|
|
m.object:hud_change(m.interaction_marker, "text", "artifact_rmb_50.png")
|
|
elseif progress > 0.25 then
|
|
m.object:hud_change(m.interaction_marker, "text", "artifact_rmb_25.png")
|
|
end
|
|
end
|
|
elseif not ctl.place and m.interaction_start and (not m.pointed_obj or not m.pointed_obj._no_interact) then
|
|
m.interaction_start = nil
|
|
if m.interaction_marker then
|
|
m.object:hud_change(m.interaction_marker, "text", "artifact_rmb.png")
|
|
end
|
|
end
|
|
|
|
local wi = p:get_wielded_item()
|
|
|
|
m.wielded_item = wi
|
|
|
|
-- MARK: Radial menu handling
|
|
|
|
if ctl.place and not m.ctl.place and wi:get_name():find "artifact:input" and (not m.pointed_obj or m.pointed_obj._no_interact) then
|
|
artifact.show_radial_menu(m, {
|
|
name = "construct",
|
|
"test",
|
|
"test2",
|
|
"test3",
|
|
"test4",
|
|
"test5"
|
|
})
|
|
elseif m._menu and not (ctl.place and wi:get_name():find "artifact:input") or (m.pointed_obj and not m.pointed_obj._no_interact) then
|
|
artifact.dismiss_radial_menu(m, "construct")
|
|
elseif m._menu then
|
|
local dx = m.yaw -yaw
|
|
local dy = m.pitch -pitch
|
|
if dx ~= 0 and dy ~= 0 then
|
|
m._menu.pos.x = m._menu.pos.x +dx *200
|
|
m._menu.pos.y = m._menu.pos.y -dy *200
|
|
local r = m._menu.pos:distance(vector.zero())
|
|
if r > 50 then
|
|
r = 50
|
|
m._menu.pos = m._menu.pos:normalize() *50
|
|
end
|
|
p:hud_change(m._menu.cursor._id, "offset", m._menu.pos)
|
|
if r > 20 then
|
|
local angle = minetest.dir_to_yaw(vector.new(m._menu.pos.x, 0, m._menu.pos.y):normalize())
|
|
local idx = math.floor((-angle +math.pi +(m._menu.step /2)) %(math.pi *2) /m._menu.step) +1
|
|
if m._menu.selected and m._menu.selected ~= idx then
|
|
m._menu[m._menu.selected]:animate{
|
|
scale = {
|
|
value = {x=0.7, y=0.7},
|
|
duration = 0.2
|
|
},
|
|
opacity = {
|
|
value = 128,
|
|
duration = 0.2
|
|
}
|
|
}
|
|
end
|
|
if m._menu.selected ~= idx and m._menu[idx] then
|
|
m._menu.selected = idx
|
|
m._menu[m._menu.selected]:animate{
|
|
scale = {
|
|
value = {x=1, y=1},
|
|
duration = 0.2
|
|
},
|
|
opacity = {
|
|
value = 256,
|
|
duration = 0.2
|
|
}
|
|
}
|
|
end
|
|
elseif m._menu.selected then
|
|
m._menu[m._menu.selected]:animate{
|
|
scale = {
|
|
value = {x=0.7, y=0.7},
|
|
duration = 0.2
|
|
},
|
|
opacity = {
|
|
value = 128,
|
|
duration = 0.2
|
|
}
|
|
}
|
|
m._menu.selected = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
m.ctl = ctl
|
|
m.yaw = yaw
|
|
m.pitch = pitch
|
|
end,
|
|
set_character = function(m, to)
|
|
m.character = to
|
|
m.meta:set_string("character", to)
|
|
end,
|
|
-- Initialize the player's primary HUD display based on saved state.
|
|
create_hud = function(m)
|
|
m.healthbar = m.object:hud_add {
|
|
type = "statbar",
|
|
position = {x=0.5,y=1},
|
|
offset = {x=-27 *5,y=artifact.debug and -96 or -30},
|
|
scale = {x=4,y=4},
|
|
alignment = {x=-1, y=-1},
|
|
size = {x=27,y=27},
|
|
text = "artifact_heart_vix.png",
|
|
text2 = "artifact_heart_bg.png",
|
|
number = 20
|
|
}
|
|
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=artifact_hotbar_bg.png"
|
|
end
|
|
p:hud_set_hotbar_image("[combine:"..(21 *slots +1).."x22"..list)
|
|
p:hud_set_hotbar_selected_image("artifact_hotbar_selected_bg.png")
|
|
end,
|
|
}, {
|
|
__call = function(_, ...)
|
|
return Player.new(...)
|
|
end
|
|
})
|
|
|
|
local _hand = minetest.registered_items[""]
|
|
|
|
function artifact.register_input(name)
|
|
artifact.register_node("input_"..name, {
|
|
inventory_image = "artifact_rmb_100.png",
|
|
description = "",
|
|
paramtype = "light",
|
|
drawtype = "mesh",
|
|
mesh = "artifact_hand.gltf",
|
|
tiles = {"artifact_"..name..".png"},
|
|
use_texture_alpha = "opaque",
|
|
visual_scale = 1,
|
|
wield_scale = vector.new(2,2,2),
|
|
node_placement_prediction = "",
|
|
on_construct = function(pos)
|
|
minetest.remove_node(pos)
|
|
end,
|
|
drop = "",
|
|
range = 0,
|
|
on_drop = function(s, p, pos)
|
|
local m = artifact.players[p:get_player_name()]
|
|
if artifact.debug or artifat.story.state > artifact.story.states.pre_vix then
|
|
artifact.swap_character(m)
|
|
end
|
|
return s
|
|
end
|
|
})
|
|
end
|
|
artifact.register_input "key"
|
|
artifact.register_input "vix"
|
|
|
|
|
|
minetest.register_globalstep(function()
|
|
for _, m in pairs(artifact.players) do
|
|
m:tick()
|
|
end
|
|
end)
|
|
|
|
|
|
minetest.register_on_joinplayer(function(p)
|
|
artifact.players[p:get_player_name()] = Player(p)
|
|
if artifact.debug then
|
|
-- Make sure we don't have to `/grantme` a million times while testing.
|
|
minetest.registered_chatcommands.grantme.func(p:get_player_name(), "all")
|
|
end
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(p)
|
|
artifact.players[p:get_player_name()] = nil
|
|
end)
|