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 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' playerd 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