artifact_one/mods/artifact_story/init.lua
2025-12-01 19:07:49 -05:00

1025 lines
41 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

artifact.story = {
states = enum { -- We use an enum for this so that we can use relational operators to determine if the current state is before or after a target state.
"loading", -- Mapgen is not yet complete.
"init", -- For the opening cutscene.
"pre_vix", -- The player doesn't have Vix yet.
"main", -- The main game state. Progress is managed by checkpoints here.
"ended", -- The game is over.
},
poi = {
initial_cutscene = {
start_pos = vector.new(0, 0, 0),
end_pos = vector.new(0,0,0)
}
}
}
local ns = artifact.story
local db = minetest.get_mod_storage()
local state = db:get_int("state") -- Defaults to zero, i.e. "loading".
artifact.origin = vector.from_string(db:get("origin") or "(0,0,0)")
if artifact.debug then state = ns.states.main end
function ns.enter_init_state()
ns.play_intro_cutscene()
end
local vix_scene
minetest.register_entity(":artifact:vix_scene", {
initial_properties = {
visual = "mesh",
mesh = "artifact_scene_vix.gltf",
textures = {"artifact_vix.png"},
backface_culling = false
},
on_activate = function(e)
if state > ns.states.pre_vix then
e.object:remove()
return
end
e.object:set_armor_groups{immortal = 1}
e.object:set_animation({x=0,y=2}, 0.5, 0.1, true)
e.particles = minetest.add_particlespawner {
pos = e.object:get_pos():offset(0, 0.15, 0),
vel = {
min = vector.new(-0.5, 0,-0.5),
max = vector.new( 0.5, 0, 0.5)
},
acc = vector.new(0, -9.81, 0),
texture = {
name = "artifact_light_gold.png",
alpha_tween = {0.75, 0}
},
collisiondetection = true,
time = 0,
amount = 100,
exptime = 5
}
e._track_pos = e.object:get_pos():offset(0, 0.5, 0)
e._rot = vector.zero()
vix_scene = e
end,
on_deactivate = function(e)
vix_scene = nil
if e.particles then
minetest.delete_particlespawner(e.particles)
end
end,
on_step = function(e)
if e._can_check then
for _, m in pairs(artifact.players) do
if m.pos:distance(e.object:get_pos()) < 15 and m.dir:distance(m.pos:direction(e.object:get_pos())) < 0.2 then
artifact.push_chat_message("Hmm...", "Key", "artifact_key_splash_low.png")
e._can_check = false
end
end
elseif e._track then
local m = artifact.players[next(artifact.players)]
local rot = e._track_pos:direction(m.pos):rotate(e._rot):dir_to_rotation()
if rot.y > -math.pi *(2/3) and rot.y < math.pi *(2/3) then
rot.y = math.pi *(2/3) *math.sign(rot.y)
end
rot.y = -rot.y +math.pi
rot.x = -math.max(-math.pi /3, math.min(math.pi /3, rot.x)) +0.25
e.object:set_bone_override("Head", {
rotation = {
vec = rot,
interpolation = 0.4
}
})
end
end
})
local help
local function pre_vix_on_whacked(type, target)
local checkpoint = db:get("checkpoint:pre_vix")
if checkpoint == "begin" then -- We're still in the start closet.
if type == "object" then
help:cancel()
vix_scene._can_check = true
end
db:set_string("checkpoint:pre_vix", "in_room")
elseif checkpoint == "in_room" then -- We're actually in the room.
if type == "node" and target.node_under.name:find "forcefield_generator" then
local num = db:get_int("checkpoint:pre_vix_fields_broken") +1
db:set_int("checkpoint:pre_vix_fields_broken", num)
if num == 1 then -- Key breaks his first generator.
minetest.after(1, function()
artifact.push_chat_message("Hehe.", "Key", "artifact_key_splash_low.png")
end)
elseif num >= 5 then -- All generators are down.
vix_scene._can_check = nil
-- Wait just a bit, so the player can look up.
minetest.after(0.5, function()
ns.play_vix_scene()
end)
end
end
end
end
function ns.enter_pre_vix_state()
for _, m in pairs(artifact.players) do
m:add_health_bar()
m.object:hud_set_flags {
crosshair = true,
wielditem = true,
}
m.object:set_pos(artifact.origin:offset(0, -73.5, -4))
m:set_spawnpoint(artifact.origin:offset(0, -73.5, -4))
end
minetest.add_entity(artifact.origin:offset(-16.5, -72.5, -17), "artifact:vix_scene")
help = minetest.after(15, function()
for _, m in pairs(artifact.players) do
artifact.show_help_message(m, "Certain nodes can be broken by punching them with the blackrod.", "info")
end
end)
db:set_string("checkpoint:pre_vix", "begin")
artifact.on_whacked = pre_vix_on_whacked
end
function ns.enter_main_state()
minetest.add_entity(vix_scene.object:get_pos():offset(1.5, -0.8, -0.5), "artifact:sidekick"):set_rotation(vector.new(0, -math.pi /2, 0))
vix_scene.object:remove()
end
function ns.enter_ended_state()
for _, m in pairs(artifact.players) do
local bg = artifact.hud_add(m, {
type = "image",
image = "[fill:16x16:0,0:#000",
opacity = 0,
pos = {x=0.5,y=0.5},
scale = {x=1000,y=1000},
})
bg:animate {
opacity = {
value = 256,
duration = 0.3
}
}
minetest.after(0.3, function()
local txt = artifact.hud_add(m, {
type = "text",
color = "#000",
text = "To be continued...",
pos = {x=0.5,y=0.5},
size = {x=3,y=0},
color = minetest.colorspec_to_table("#000")
})
txt:animate {
color = {
value = minetest.colorspec_to_table("#888"),
duration = 0.3
}
}
minetest.after(8, function()
txt:animate {
color = {
value = minetest.colorspec_to_table("#000"),
duration = 0.3
}
}
minetest.after(0.3, function()
minetest.request_shutdown("You completed the game.")
end)
end)
end)
end
end
function ns.enter_state(to)
state = to
if state == ns.states.init then
ns.enter_init_state()
elseif state == ns.states.pre_vix then
ns.enter_pre_vix_state()
elseif state == ns.states.main then
ns.enter_main_state()
elseif state == ns.states.ended then
ns.enter_ended_state()
end
db:set_int("state", state)
end
function ns.get_state()
return state
end
-- Used for marking the start position in schematics.
-- Disappears when not in debug mode.
artifact.register_node("start_pos", {
drawtype = not artifact.debug and "airlike" or nil,
paramtype = "light",
walkable = artifact.debug or false,
pointable = artifact.debug or false,
tiles = {artifact.debug and "artifact_start_pos.png" or "blank.png"}
})
function artifact.look_at(m, pos, pos2)
local rot = (pos2 and pos or m.object:get_pos()):direction(pos2 or pos):dir_to_rotation()
m.object:set_look_horizontal(rot.y)
-- Pitch seems to be flipped on the player?
m.object:set_look_vertical(-rot.x)
end
if artifact.debug then
minetest.register_chatcommand("splash", {
func = function(name)
local m = artifact.players[name]
minetest.show_formspec(m.name, "artifact:lock_camera", [[
formspec_version[10]
size[32,18]
padding[0,0]
bgcolor[#000]
animated_image[0,0;32,18;;artifact_splash.png;60;100;;]
]])
end
})
minetest.register_chatcommand("flash", {
func = function(name)
minetest.add_particle {
pos = artifact.origin:offset(-16.5, -71.5, -17),
velocity = vector.zero(),
texture = {
name = "artifact_flash.png",
alpha_tween = {0, 1}
},
size = 60,
expirationtime = 0.1
}
end
})
end
artifact.register_node("stasis_beacon", {
drawtype = "mesh",
mesh = "artifact_stasis_beacon.gltf",
tiles = {"artifact_stasis_beacon.png"},
use_texture_alpha = "blend",
paramtype = "light"
})
-- The 'cutscene' played after the player frees Vix.
function ns.play_vix_scene()
-- Kaboom.
minetest.add_particle {
pos = artifact.origin:offset(-16.5, -71.5, -17),
velocity = vector.zero(),
texture = {
name = "artifact_flash.png",
alpha_tween = {0, 1}
},
size = 60,
expirationtime = 0.1
}
artifact.play_sound {
name = "artifact_free_vix",
pos = artifact.origin:offset(-16.5, -72.5, -17)
}
vix_scene.object:set_animation({x=3,y=150}, 1, 0.1, false)
minetest.delete_particlespawner(vix_scene.particles)
vix_scene.particles = nil
-- Key can walk around freely, so Vix should try to face him once she wakes.
local i = 7
minetest.after(i, function()
vix_scene._track = true
end)
i = i +1
minetest.after(i, function()
-- Is he the one they sent?
artifact.push_chat_message("...Who are you?", minetest.colorize("#f5dd66", "???"), "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
-- Awkward.
artifact.push_chat_message("Uh... Wow, this is awkward. I did not think that through far enough.", "Key", "artifact_key_splash_low.png")
end)
i = i +7
minetest.after(i, function()
-- Oops, that didn't help.
artifact.push_chat_message("Wait, that was bad, let's try this again.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
-- Better.
artifact.push_chat_message("Hi. I'm Key.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
-- ...He's not?
artifact.push_chat_message("...Hi.", minetest.colorize("#f5dd66", "???"), "artifact_vix_splash_low.png")
-- [ She sits up ]
vix_scene._track = nil
vix_scene.object:set_bone_override("Head", {
rotation = {
vec = vector.zero(), interpolation = 0.4
}
})
end)
i = i +5
minetest.after(i, function()
vix_scene._track = true
-- So what's the deal?
artifact.push_chat_message("How did you get here?", minetest.colorize("#f5dd66", "???"), "artifact_vix_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("It's actually a long story, but the short version is that I fell in a hole.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("...", minetest.colorize("#f5dd66", "???"), "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Maintenance might want to have a look at the closet over there.", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
-- Okay... Wait, how long has it been, anyway? The room is awfully overgrown...
artifact.push_chat_message("I don't think maintenance will mind...", minetest.colorize("#f5dd66", "???"), "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Probably so...", "Key", "artifact_key_splash_low.png")
-- [ Vix climbs off the pedestal ]
vix_scene._track = nil
vix_scene.object:set_bone_override("Head", {
rotation = {
vec = vector.zero(), interpolation = 0.4
}
})
vix_scene._track_pos = vix_scene.object:get_pos():offset(1.5, 0, -0.5)
vix_scene._rot = vector.new(0, -math.pi /2, 0)
end)
i = i +7
minetest.after(i, function()
vix_scene._track = true
artifact.push_chat_message("So, uh, what's _your_ name?", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("...Vix.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Formally pleased to meet you.", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
-- How trustworthy is he?
artifact.push_chat_message("Why did you... break the forcefields?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Well, when I saw you locked inside all those forcefields, I thought 'Wow, she doesn't look too happy'.", "Key", "artifact_key_splash_low.png")
end)
i = i +7
minetest.after(i, function()
artifact.push_chat_message("Then I thought, 'Hmm, she must have been there a while, what with all the vines over here and not over there.'", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("_Then_ I thought, 'Whoever's fault this is probably isn't going to be back. Why not help her out?'", "Key", "artifact_key_splash_low.png")
end)
i = i +7
minetest.after(i, function()
artifact.push_chat_message("So then I used this cool stick I found to trash all the forcefields and let you out.", "Key", "artifact_key_splash_low.png")
end)
i = i +7
minetest.after(i, function()
-- ..._Are_ they going to be back...?
artifact.push_chat_message("What year is it?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("I dunno, I lost count a while back. Somewhere around the turn of the century.", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
-- ... I guess not.
artifact.push_chat_message("...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
-- You know what, what am I even doing here?
-- (It's not like staying is a workable option, but that's not really what Vix has in mind.)
artifact.push_chat_message("We need to leave... I don't suppose we could get out the way you got in?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("...Nah.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("I guess that leaves the hard way.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Let's see if this works...", "Vix", "artifact_vix_splash_low.png")
minetest.after(1, function()
local burst = minetest.add_entity(vix_scene.object:get_pos():offset(0, 1, -1.5), "artifact:burst")
burst:get_luaentity()._critical = true
burst:set_attach(vix_scene.object, "RightArm", vector.new(0, 100, 0))
minetest.after(0, function()
burst:set_detach()
artifact.play_sound {
name = "artifact_burst_fire",
pos = burst:get_pos()
}
burst:get_luaentity():impulse(burst:get_pos():direction(artifact.origin:offset(18, -71, -13)) *30)
end)
end)
end)
i = i +8
minetest.after(i, function()
artifact.push_chat_message("Wow, that's epic.", "Key", "artifact_key_splash_low.png")
end)
i = i +2
minetest.after(i, function()
ns.enter_state(ns.states.main)
minetest.after(10, function()
local m = artifact.players[next(artifact.players)]
if m.character ~= "vix" then
artifact.show_help_message(m, "You can switch between Key and Vix using the Drop control.", "info")
end
end)
end)
i = i +45
minetest.after(i, ns.dialogue_1)
end
function ns.dialogue_1()
local i = 0
artifact.push_chat_message("So, what's the long version of you you got here?", "Vix", "artifact_vix_splash_low.png")
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Well, one fine day, I was strolling along through a little town called Birchwood.", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("On the street corner, some guy was doing a huge ad for an adventuring supply company.", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Naturally, I suggested that his wares were overpriced because he was spending so much on advertising.", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("I won't bore you with the details, but there was a whole thing that ended with me betting him five gold pieces that I could steal some artifact whose name I forget without any supplies at all.", "Key", "artifact_key_splash_low.png")
end)
i = i +8
minetest.after(i, function()
artifact.push_chat_message("(I told him it was probably guarded by a few bats and a couple average-sized spiders, but he wouldn't believe me.)", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("I was wrong about there not being any traps, though. That's why I'm down here and not five gold pieces richer. (Yet.)", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("So you came all this way for five gold pieces?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("No, no, I came all this way to prove a point. I'd probably forget I had the gold pieces within the hour.", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Wow.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +15
minetest.after(i, function()
artifact.push_chat_message("So, how about you? What's the story with that awesome burst ability?", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Have you ever heard of Iron Dragon?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Nope.", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("They're... Well, they were... a vigilante group, who were fairly powerful fifty-some years ago. My father was one of the generals.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Early on, they learned to synthesize a controlled form of energy to power tools; you saw some of that in the forcefields.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Eventually, someone — my aunt, actually — floated the idea that feeding that energy into a human might give them new abilities.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("No one liked it at first. Eventually, though, when we were weakened and all but defeated, my father became desperate enough to consider it.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("He wanted me to be the first test subject. Looking back, I think he wanted to protect me, keep me out of the combat if things got that bad.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("I trusted his judgement.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Incredibly, though, he was right... It makes no sense physiologically, but somehow it worked.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +8
minetest.after(i, function()
artifact.push_chat_message("Now here I am half a century later, and it's all gone...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Wow. Imagine an experiment that worked on the first try.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Right? I though there could be something to it, but this? It's been half a century, and I feel even better than I did before.", "Vix", "artifact_vix_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("That's what I was thinking. You look sixteen, not 60.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Really?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Yep.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("...Huh. That's...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Really weird?", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Yeah. That's exactly how old I was when this happened...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Most intriguing...", "Key", "artifact_key_splash_low.png")
end)
end
-- Play the final scene.
function ns.play_final_scene()
local scn = minetest.add_entity(artifact.origin:offset(132, -69.5, -22), "display")
scn:set_properties {
visual = "mesh",
mesh = "artifact_scene_final.gltf",
textures = {"artifact_tav.png"}
}
local i = 2
minetest.after(i, function()
artifact.push_chat_message("Hey, who's that?", "Key", "artifact_key_splash_low.png")
minetest.after(1, function()
scn:set_animation({x=0,y=120}, 1, 0, false)
end)
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("I don't know...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("You.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +3
minetest.after(i, function()
artifact.push_chat_message("Huh?", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("What have you done?", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("I just walked in here, why?", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Vix.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("What?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Hm.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +3
minetest.after(i, function()
artifact.push_chat_message("So you really did?", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("I don't understand...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Is that so?", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Yes?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Traitor.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +2
minetest.after(i, function()
artifact.push_chat_message("Wow.", "Key", "artifact_key_splash_low.png")
end)
i = i +2
minetest.after(i, function()
artifact.push_chat_message("Huh? Iron Dragon is gone. What are you accusing me of?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("So you don't know. Unfortunate.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Who are you, anyway?", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("A ghost.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Whoa, stop being so ambiguous! I don't think we know what you think we know.", "Key", "artifact_key_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("I'd love to banter, but right now I have more important things to do.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("As far as I'm concerned, you can rot down here — mother.", minetest.colorize("#666", "???"), "artifact_tav_splash_low.png")
minetest.after(1, function()
local door = artifact.large_doors[artifact.origin:offset(137, -69, -22):round():to_string()]
door:open()
minetest.after(3, function()
door:close()
end)
end)
end)
i = i +5
minetest.after(i, function()
artifact.push_chat_message("Excuse me!?", "Key", "artifact_key_splash_low.png")
artifact.push_chat_message("Excuse me!?", "Vix", "artifact_vix_splash_low.png")
end)
i = i +3
minetest.after(i, function()
artifact.push_chat_message("Hey! You're being so cliche right now!", "Key", "artifact_key_splash_low.png")
end)
i = i +7
minetest.after(i, function()
artifact.push_chat_message("Well, that was weird.", "Key", "artifact_key_splash_low.png")
end)
i = i +9
minetest.after(i, function()
artifact.push_chat_message("...So, Vix, you wouldn't happen to know of any ventilation shafts conveniently placed nearby, would you?", "Key", "artifact_key_splash_low.png")
end)
i = i +6
minetest.after(i, function()
artifact.push_chat_message("Well, there is one...", "Vix", "artifact_vix_splash_low.png")
end)
i = i +4
minetest.after(i, function()
artifact.push_chat_message("Nice. Let's get out of this place.", "Key", "artifact_key_splash_low.png")
end)
i = i +4
minetest.after(i, function()
ns.enter_state(ns.states.ended)
end)
end
-- Play the opening cutscene.
function ns.play_intro_cutscene()
ns.camera = minetest.add_entity(artifact.origin:offset(0,-0.75,0), "display")
ns.scene = minetest.add_entity(artifact.origin:offset(-2.25,-0.5,7 -1/16), "display")
ns.scene:set_properties {
visual = "mesh",
mesh = "artifact_cutscene_a.gltf",
textures = {"artifact_key.png", "artifact_statue.png"}
}
ns.scene:set_animation({x=0,y=25}, 1, 0.1, false)
for _, m in pairs(artifact.players) do
m.object:set_attach(ns.camera)
minetest.show_formspec(m.name, "artifact:lock_camera", [[
formspec_version[10]
size[32,18]
padding[0,0]
allow_close[false]
bgcolor[#0000]
]])
m.object:set_look_vertical(0)
m.object:set_look_horizontal(0)
end
-- Begin mess.
minetest.after(17, function()
for x = -1, 1 do
for z = -1, 1 do
minetest.remove_node(artifact.origin:offset(x, -1, z -5))
minetest.add_particlespawner {
pos = {
min = artifact.origin:offset(x -0.5, -1, z -5 -0.5),
max = artifact.origin:offset(x +0.5, -0.5, z -5 +0.5)
},
vel = {
min = vector.new(-1, 0, -1) *1.5,
max = vector.new(1, 2, 1) *1.5
},
acc = vector.new(0,-9.81,0),
collisiondetection = true,
amount = 50,
node = {name = "artifact:stone_tile_brown"},
time = 0.1
}
end
end
end)
minetest.after(3.5, function()
-- Slowly move back as Key walks forward.
ns.camera:set_acceleration(vector.new(0,0,-0.5))
minetest.after(1, function()
-- Decelerate before switching angles, for smoothness.
ns.camera:set_acceleration(vector.new(0,0,0.3))
end)
minetest.after(2, function()
ns.camera:set_acceleration(vector.zero())
ns.camera:set_velocity(vector.zero())
ns.camera:set_pos(artifact.origin:offset(-5, 3, -4))
for _, m in pairs(artifact.players) do
artifact.look_at(m, ns.camera:get_pos(), artifact.origin:offset(0,0,1))
end
local time = minetest.get_us_time()
-- Pan to follow Key as he moves toward the pedestal.
local function interpolate()
local fac = (minetest.get_us_time() -time) /4000000
local offset = artifact.interpolate(1, 4, fac)
for _, m in pairs(artifact.players) do
artifact.look_at(m, ns.camera:get_pos(), artifact.origin:offset(0,0,-offset))
end
-- Do a globalstep callback the lazy way.
if fac < 1 then minetest.after(0, interpolate) end
end
minetest.after(0, interpolate)
minetest.after(4, function()
-- Dramatically move backward as Key stares at the statue.
ns.camera:set_pos(artifact.origin:offset(-0.2, -0.5, -9))
ns.camera:set_velocity(vector.new(0,0,-0.5))
for _, m in pairs(artifact.players) do
m.object:set_look_vertical(0)
m.object:set_look_horizontal(0)
end
minetest.after(6, function()
-- Cut back to where we were before, so we get a good view of Key falling in the hole.
ns.camera:set_pos(artifact.origin:offset(-5, 3, -4))
ns.camera:set_velocity(vector.new(0,0,0))
for _, m in pairs(artifact.players) do
artifact.look_at(m, ns.camera:get_pos(), artifact.origin:offset(0,0,-4))
end
minetest.after(3, function()
-- Show epic splash animation while Key finishes falling down the hole.
for _, m in pairs(artifact.players) do
artifact.hud_add(m, {
type = "image",
name = "background",
pos = {x=0.5,y=0.5},
scale = {x=1000,y=1000},
image = "[fill:16x16:0,0:#000",
opacity = 0
})
m.hud.background:animate {
opacity = {
value = 256,
duration = 0.3
}
}
end
minetest.after(0.3, function()
for _, m in pairs(artifact.players) do
minetest.show_formspec(m.name, "artifact:lock_camera", [[
formspec_version[10]
size[32,18]
padding[0,0]
allow_close[false]
bgcolor[#0000]
animated_image[0,0;32,18;;artifact_splash.png;60;100;;]
]])
end
end)
minetest.after(6.3, function()
for _, m in pairs(artifact.players) do
minetest.show_formspec(m.name, "artifact:lock_camera", [[
formspec_version[10]
size[32,18]
padding[0,0]
allow_close[false]
bgcolor[#0000]
]])
m.hud.background:animate {
opacity = {
value = 0,
duration = 0.3
}
}
m.hud.background.remove_after = 0.3
end
ns.camera:set_pos(artifact.origin:offset(-1, -73, -6))
ns.camera:set_velocity(vector.new(0,0,0))
for _, m in pairs(artifact.players) do
artifact.look_at(m, ns.camera:get_pos(), artifact.origin:offset(1, -74, -3))
end
ns.scene:remove()
ns.scene = minetest.add_entity(artifact.origin:offset(-1, -73.5, -6), "display")
ns.scene:set_properties {
visual = "mesh",
mesh = "artifact_cutscene_b.gltf",
textures = {"artifact_key.png", "artifact_blackrod.png"}
}
minetest.after(0.3, function()
artifact.push_chat_message("Ow.", "Key", "artifact_key_splash_low.png")
minetest.after(1, function()
ns.scene:set_animation({x=0,y=25}, 1, 0.1, false)
end)
minetest.after(9, function()
artifact.push_chat_message("Interesting...", "Key", "artifact_key_splash_low.png")
end)
minetest.after(13, function()
ns.scene:remove()
for _, m in pairs(artifact.players) do
m.object:set_detach()
minetest.close_formspec(m.name, "artifact:lock_camera")
artifact.look_at(m, ns.camera:get_pos(), artifact.origin:offset(0, -73.5, -8))
end
ns.enter_state(ns.states.pre_vix)
minetest.after(3, function()
artifact.push_chat_message("I'd better take a few practice swings.", "Key", "artifact_key_splash_low.png")
end)
end)
end)
end)
end)
end)
end)
end)
end)
-- End mess.
end
-- Do mapgen. This isn't technically story-related, but it's here
-- anyway because we only need to do it for state == "loading"
-- and it's the only mapgen we do.
function ns.load_map()
-- Notify the player that we must mapgen first.
for _, m in pairs(artifact.players) do
artifact.hud_add(m, {
type = "image",
name = "loading_map_bg",
pos = {x=0.5,y=0.5},
scale = {x=1000,y=1000}, -- Cover the whole window without having to get the window size.
image = "[fill:16x16:0,0:#000"
})
artifact.hud_add(m, {
type = "text",
name = "loading_map",
text = "Loading map...",
pos = {x=0.5,y=0.5},
size = {x=3,y=0},
color = minetest.colorspec_to_table("#000")
})
m.hud.loading_map:animate {
color = {
value = minetest.colorspec_to_table("#888"),
duration = 0.3,
}
}
end
-- Make sure the loading HUD fades in first.
minetest.after(0.3, function()
-- Emerge the area so we have something to write to.
-- This is one of the relatively few cases where the
-- Promise API is actually more than a little helpful,
-- because we can simply 'yield' until the emerge is
-- completely finished in a semi-clean way.
Promise(function(r)
minetest.emerge_area(vector.new(-1,-1,-1) *100, vector.new(1, 1, 1) *100, function(bpos, action, remaining) if remaining == 0 then r() end end)
end).after(function()
for _, m in pairs(artifact.players) do
m.hud.loading_map:animate {
color = {
value = minetest.colorspec_to_table("#000"),
duration = 0.3,
}
}
end
-- The mapgen code is here, but the actual world schematic should live in artifact_world.
local path = minetest.get_modpath("artifact_world").."/schems/map"
local pos1 = vector.new(-5,-7,-5)
local pos2 = pos1 +artifact.get_schem_size(path)
artifact.load_schematic(pos1, path)
-- Wait a bit to make quite sure that the schematic is in place (and allow the HUD to fade out).
minetest.after(0.3, function()
local nodes = minetest.find_nodes_in_area(pos1, pos2, "start_pos")
-- If we can't find a start marker, fall back to the global origin.
local start = nodes[1] or vector.zero()
artifact.origin = start
db:set_string("origin", start:to_string())
for _, m in pairs(artifact.players) do
m.hud.loading_map:remove(m)
m.hud.loading_map_bg:animate {
opacity = {
value = 0,
duration = 0.3
}
}
m.hud.loading_map_bg.remove_after = 0.3
m.object:set_pos(start)
end
ns.enter_state(artifact.story.states.init)
end)
end)
end)
end
include "objectives.lua"
local started = false
minetest.register_on_joinplayer(function(p)
local m = artifact.players[p:get_player_name()]
if state == ns.states.ended then
minetest.request_shutdown("You completed the game.")
end
-- Only add the HUD etc. if the player is actually in the game.
if state == ns.states.init then
m.object:set_attach(ns.camera)
minetest.show_formspec(m.name, "artifact:lock_camera", [[
formspec_version[10]
allow_close[false]
bgcolor[#0000]
]])
elseif state > ns.states.init then
m:add_health_bar()
m.object:hud_set_flags {
crosshair = true,
wielditem = true
}
end
if state == ns.states.pre_vix then
artifact.on_whacked = pre_vix_on_whacked
end
if state == ns.states.pre_vix and db:get_int("checkpoint:pre_vix_fields_broken") >= 5 then
-- If the player left during the scene for some reason, start it over when they join back.
ns.play_vix_scene()
end
-- So we only call this when the _first_ player joins.
-- Sure, we're technically a singleplayer game, but,
-- as they say, better to have it and not need it than
-- need it and not have it.
if not started then
started = true
if state == ns.states.loading then
ns.load_map()
elseif state == "init" then
ns.play_intro_cutscene()
end
end
end)
if artifact.debug then
minetest.register_chatcommand("map", {
func = function()
local path = minetest.get_modpath("artifact_world").."/schems/map"
local pos1 = vector.new(-5,-7,-5)
artifact.load_schematic(pos1, path)
end
})
end