Compare commits
1 Commits
codegenera
...
efe903110b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efe903110b |
@@ -39,10 +39,6 @@ globals = {
|
||||
"MysteriousManScreen",
|
||||
"DiscussionWindow",
|
||||
"EndWindow",
|
||||
"PlayerNameWindow",
|
||||
"TextInput",
|
||||
"CodeGenerator",
|
||||
"CreditsWindow",
|
||||
"GameOverWindow",
|
||||
"mset",
|
||||
"mget",
|
||||
|
||||
@@ -6,7 +6,6 @@ init/init.context.lua
|
||||
system/system.util.lua
|
||||
system/system.print.lua
|
||||
system/system.input.lua
|
||||
system/system.textinput.lua
|
||||
system/system.mouse.lua
|
||||
system/system.asciiart.lua
|
||||
system/system.rle.lua
|
||||
@@ -17,7 +16,6 @@ logic/logic.timer.lua
|
||||
logic/logic.trigger.lua
|
||||
logic/logic.minigame.lua
|
||||
logic/logic.glitch.lua
|
||||
logic/logic.codegenerator.lua
|
||||
logic/logic.discussion.lua
|
||||
system/system.ui.lua
|
||||
audio/audio.manager.lua
|
||||
@@ -52,8 +50,10 @@ decision/decision.go_to_sleep.lua
|
||||
decision/decision.do_work.lua
|
||||
decision/decision.have_a_coffee.lua
|
||||
decision/decision.sumphore_discussion.lua
|
||||
decision/decision.eating_fast_food.lua
|
||||
discussion/discussion.sumphore.lua
|
||||
discussion/discussion.coworker.lua
|
||||
discussion/discussion.pizza_vendor.lua
|
||||
map/map.manager.lua
|
||||
map/map.bedroom.lua
|
||||
map/map.street.lua
|
||||
@@ -83,7 +83,6 @@ window/window.minigame.ddr.lua
|
||||
window/window.discussion.lua
|
||||
window/window.continued.lua
|
||||
window/window.credits.lua
|
||||
window/window.player_name.lua
|
||||
window/window.game.lua
|
||||
system/system.main.lua
|
||||
meta/meta.assets.lua
|
||||
|
||||
@@ -10,6 +10,7 @@ Decision.register({
|
||||
modes_for_ascension_levels[1] = "only_special"
|
||||
modes_for_ascension_levels[2] = "only_left"
|
||||
modes_for_ascension_levels[3] = "only_nothing"
|
||||
modes_for_ascension_levels[4] = "normal"
|
||||
|
||||
MinigameDDRWindow.start("game", "generated", {
|
||||
on_win = function(game_context)
|
||||
@@ -19,8 +20,6 @@ Decision.register({
|
||||
Context.should_ascend = true
|
||||
elseif (game_context.special_mode_condition and Context.ascension.level == 3) then
|
||||
Context.should_ascend = true
|
||||
elseif (game_context.special_mode_condition and Context.ascension.level == 4) then
|
||||
Context.should_ascend = true
|
||||
end
|
||||
|
||||
Meter.show()
|
||||
@@ -28,7 +27,7 @@ Decision.register({
|
||||
Window.set_current("game")
|
||||
Context.have_done_work_today = true
|
||||
end,
|
||||
special_mode = modes_for_ascension_levels[Ascension.get_level()]
|
||||
special_mode = modes_for_ascension_levels[Ascension.get_level()] or "normal"
|
||||
})
|
||||
end,
|
||||
})
|
||||
|
||||
11
inc/decision/decision.eating_fast_food.lua
Normal file
11
inc/decision/decision.eating_fast_food.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
Decision.register({
|
||||
id = "eating_fast_food",
|
||||
label = "Eat Fast Food",
|
||||
condition = function()
|
||||
return Context.fast_food_eaten_today < 3
|
||||
end,
|
||||
handle = function()
|
||||
Context.fast_food_approaching = true
|
||||
Discussion.start("pizza_vendor_disc", "game")
|
||||
end,
|
||||
})
|
||||
@@ -4,10 +4,17 @@ Decision.register({
|
||||
handle = function()
|
||||
local level = Ascension.get_level()
|
||||
local disc_id = "coworker_disc_0"
|
||||
-- TODO: Add more discussions for levels above 3
|
||||
if level >= 1 and level <= 3 then
|
||||
if level >= 1 and level <= 5 then
|
||||
local suffix = Context.have_done_work_today and ("_asc_" .. level) or ("_" .. level)
|
||||
disc_id = "coworker_disc" .. suffix
|
||||
elseif level == 6 then
|
||||
if not Context.glitch_conversation_done_today and Context.glitch_conversation_count < 6 then
|
||||
Context.glitch_conversation_done_today = true
|
||||
Context.glitch_conversation_count = Context.glitch_conversation_count + 1
|
||||
Glitch.show()
|
||||
Discussion.start("coworker_disc_asc_6_" .. Context.glitch_conversation_count, "game")
|
||||
return
|
||||
end
|
||||
end
|
||||
Discussion.start(disc_id, "game")
|
||||
end,
|
||||
|
||||
@@ -13,9 +13,14 @@ Decision.register({
|
||||
end
|
||||
local level = Ascension.get_level()
|
||||
|
||||
-- TODO: Add more discussions for levels above 3
|
||||
if level >= 1 and level <= 3 then
|
||||
if level >= 1 and level <= 5 then
|
||||
Discussion.start("sumphore_disc_asc_" .. level, "game")
|
||||
elseif level == 6 then
|
||||
if Context.glitch_conversation_count >= 6 then
|
||||
Discussion.start("sumphore_disc_asc_6", "game")
|
||||
else
|
||||
Discussion.start("sumphore_disc_asc_6_waiting", "game")
|
||||
end
|
||||
else
|
||||
Discussion.start("homeless_guy", "game", 4)
|
||||
end
|
||||
|
||||
@@ -142,3 +142,226 @@ Discussion.register({
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_4",
|
||||
on_end = Meter.apply_coworker_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "Normaaan! Same spot, same cup, same time. You're like a statue that drinks coffee!",
|
||||
answers = {
|
||||
{ label = "I'm a person.", next_step = 2 },
|
||||
{ label = "Yep. Still here.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "I love that about you! So reliable! If the coffee machine breaks we still have Norman!",
|
||||
answers = {
|
||||
{ label = "Please don't.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_4",
|
||||
on_end = Meter.apply_coworker_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "Norman, you were seriously locked in today. What on earth were you building?",
|
||||
answers = {
|
||||
{ label = "Just some things.", next_step = 2 },
|
||||
{ label = "Nothing important.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "So modest! I heard the seniors literally whispering your name!",
|
||||
answers = {
|
||||
{ label = "Concerning.", next_step = 3 },
|
||||
{ label = "That's... fine.", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "You should celebrate! Coffee's on me tomorrow!",
|
||||
answers = {
|
||||
{ label = "Already have one.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_5",
|
||||
on_end = Meter.apply_coworker_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "Morning! Funny thought — I feel like we do this exact same thing every single day!",
|
||||
answers = {
|
||||
{ label = "We do.", next_step = 2 },
|
||||
{ label = "Yes. We do.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Ha! Routine is such a comfort, right? Same coffee, same smile, same everything!",
|
||||
answers = {
|
||||
{ label = "Sure. Very comforting.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_5",
|
||||
on_end = Meter.apply_coworker_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "Norman! You were staring right THROUGH your screen today. What was going on up there?",
|
||||
answers = {
|
||||
{ label = "Coffee was cold.", next_step = 2 },
|
||||
{ label = "Maybe I was.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Were you meditating? Or doing your intense bug-stare thing?",
|
||||
answers = {
|
||||
{ label = "Something like that.", next_step = 3 },
|
||||
{ label = "Bug stare thing?", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "You're always somewhere else in your head, Norman. Must be nice up there!",
|
||||
answers = {
|
||||
{ label = "It's complicated.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
local function _glitch_on_end()
|
||||
Meter.apply_coworker_discussion_reward()
|
||||
Context.glitch_conversation_count = Context.glitch_conversation_count + 1
|
||||
Glitch.hide()
|
||||
end
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_1",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "Hey Norman, good morning! Good morning! Good morning! ...Sorry. I don't know why I keep saying that.",
|
||||
answers = {
|
||||
{ label = "Are you feeling ok?", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Good morning. Good morning. Good— I can't stop. Why can't I stop?",
|
||||
answers = {
|
||||
{ label = "...", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_2",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "Hey... Marcus. How's it going?",
|
||||
answers = {
|
||||
{ label = "My name is Norman.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Right, sorry. Marcus. You look tired today.",
|
||||
answers = {
|
||||
{ label = "Norman. It's Norman.", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Sure, sure. You should get some rest, Marcus.",
|
||||
answers = {
|
||||
{ label = "...", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_3",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "We've had this conversation before, haven't we? Exact same words. Same coffee. Same spot.",
|
||||
answers = {
|
||||
{ label = "I don't think so.", next_step = 2 },
|
||||
{ label = "Maybe.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Every day. I always say the same thing and you always say that. It's very strange.",
|
||||
answers = {
|
||||
{ label = "That's just routine.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_4",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "Do you ever look at the walls here? Really look? Sometimes they don't feel... solid.",
|
||||
answers = {
|
||||
{ label = "They're just walls.", next_step = 2 },
|
||||
{ label = "I know what you mean.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Like they're only there because we expect them to be. Like set dressing. Does any of this feel load-bearing to you?",
|
||||
answers = {
|
||||
{ label = "I need more coffee.", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_5",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "Norman, I'm not supposed to— I mean. How are you doing today?",
|
||||
answers = {
|
||||
{ label = "What weren't you supposed to say?", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "...",
|
||||
answers = {
|
||||
{ label = "Hello?", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "coworker_disc_asc_6_6",
|
||||
on_end = _glitch_on_end,
|
||||
steps = {
|
||||
{
|
||||
question = "Forget it. You won't remember this conversation anyway. None of us do.",
|
||||
answers = {
|
||||
{ label = "What do you mean?", next_step = 2 },
|
||||
{ label = "That's a strange thing to say.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Tomorrow you'll come back and it'll be like this never happened. Same coffee. Same office. Same Norman.",
|
||||
answers = {
|
||||
{ label = "...", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
28
inc/discussion/discussion.pizza_vendor.lua
Normal file
28
inc/discussion/discussion.pizza_vendor.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
Discussion.register({
|
||||
id = "pizza_vendor_disc",
|
||||
on_end = function()
|
||||
Context.fast_food_approaching = false
|
||||
end,
|
||||
steps = {
|
||||
{
|
||||
question = "Hey friend! Hot slice, fresh out of the oven. You look like a man who knows good food!",
|
||||
answers = {
|
||||
{
|
||||
label = "Devour a juicy one",
|
||||
next_step = nil,
|
||||
on_select = function()
|
||||
local max = Meter.get_max()
|
||||
Meter.add("wpm", math.floor(max * 0.05))
|
||||
Meter.add("ism", math.floor(max * -0.05))
|
||||
Meter.add("bm", math.floor(max * -0.05))
|
||||
Context.fast_food_eaten_today = Context.fast_food_eaten_today + 1
|
||||
end,
|
||||
},
|
||||
{
|
||||
label = "Stay lean and sharp",
|
||||
next_step = nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -89,6 +89,125 @@ Discussion.register({
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "sumphore_disc_asc_4",
|
||||
on_end = Meter.apply_sumphore_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "The alarm wakes you every morning without fail, I bet.",
|
||||
answers = {
|
||||
{ label = "It's how it works.", next_step = 2 },
|
||||
{ label = "Sometimes I wish it didn't.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "What if the alarm is part of the problem?",
|
||||
answers = {
|
||||
{ label = "That doesn't make any sense.", next_step = 3 },
|
||||
{ label = "Go on.", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "One morning, Norman. When the choice comes, choose the bed. See what the world does without you in it.",
|
||||
answers = {
|
||||
{ label = "You're drunk.", next_step = nil },
|
||||
{ label = "What choice?", next_step = nil, on_select = function()
|
||||
Meter.add("ism", 5)
|
||||
end },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "sumphore_disc_asc_5",
|
||||
on_end = Meter.apply_sumphore_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "You saw something you weren't supposed to, didn't you.",
|
||||
answers = {
|
||||
{ label = "I don't know what you mean.", next_step = 2 },
|
||||
{ label = "Maybe.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "The world around you has seams. Your coworkers slip sometimes. Say things that don't quite fit.",
|
||||
answers = {
|
||||
{ label = "They seem fine to me.", next_step = nil },
|
||||
{ label = "I've noticed something odd.", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Count those moments. Six of them should be enough to see the whole picture.",
|
||||
answers = {
|
||||
{ label = "Six of what, exactly?", next_step = nil, on_select = function()
|
||||
Meter.add("ism", 5)
|
||||
end },
|
||||
{ label = "How would you know any of this?", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "sumphore_disc_asc_6_waiting",
|
||||
on_end = Meter.apply_sumphore_discussion_reward,
|
||||
steps = {
|
||||
{
|
||||
question = "You look like a man who has seen something he can't explain.",
|
||||
answers = {
|
||||
{ label = "I'm hearing things.", next_step = 2 },
|
||||
{ label = "Maybe.", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Keep looking. The world has a way of showing you what you need to see. Talk to people. You're almost there.",
|
||||
answers = {
|
||||
{ label = "Almost where?", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "sumphore_disc_asc_6",
|
||||
on_end = function()
|
||||
Meter.apply_sumphore_discussion_reward()
|
||||
Context.should_ascend = true
|
||||
Day.increase()
|
||||
MysteriousManScreen.start({
|
||||
text = MysteriousManScreen.get_text_for_level(Ascension.get_level())
|
||||
})
|
||||
end,
|
||||
steps = {
|
||||
{
|
||||
question = "You've been seeing the cracks, haven't you? The repetition. The loops. The coworkers who can't quite remember.",
|
||||
answers = {
|
||||
{ label = "How do you know that?", next_step = 2 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Because I was you, once. This isn't a workplace, Norman. It never was. You're in a system. And you've just become aware of it.",
|
||||
answers = {
|
||||
{ label = "That can't be true.", next_step = 3 },
|
||||
{ label = "I knew something was wrong.", next_step = 3 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "It doesn't matter if you believe it. You already know. That's why the cracks are showing. That's why you're still here.",
|
||||
answers = {
|
||||
{ label = "What do I do now?", next_step = 4 },
|
||||
},
|
||||
},
|
||||
{
|
||||
question = "Oh look, is that a squirrel ?",
|
||||
answers = {
|
||||
{ label = "Alcoholic ...", next_step = nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Discussion.register({
|
||||
id = "homeless_guy",
|
||||
on_end = Meter.apply_sumphore_discussion_reward,
|
||||
|
||||
@@ -21,7 +21,7 @@ local FADE_COLORS = nil
|
||||
function Ascension.get_initial()
|
||||
_increased_this_cycle = false
|
||||
return {
|
||||
level = 0,
|
||||
level = 0, -- FYI: change this to test ascension levels without having to play through them
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -53,8 +53,12 @@ function Context.initial_data()
|
||||
toilet_meters_today_evening = false,
|
||||
coworker_discussion_meter_applied_today = false,
|
||||
sumphore_discussion_meter_applied_today = false,
|
||||
glitch_conversation_count = 0,
|
||||
glitch_conversation_done_today = false,
|
||||
should_ascend = false,
|
||||
have_met_sumphore = false,
|
||||
fast_food_approaching = false,
|
||||
fast_food_eaten_today = 0,
|
||||
office_sprites = {},
|
||||
walking_to_office_sprites = {},
|
||||
game = {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
--- @section CodeGenerator
|
||||
|
||||
CodeGenerator = {}
|
||||
|
||||
local SALT = 27471
|
||||
local BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local NAME_LEN = 3
|
||||
|
||||
-- Per-position offsets derived from SALT so each character slot
|
||||
-- maps to a different region of the 2-char base-36 space.
|
||||
local SALTS = {
|
||||
SALT % 36,
|
||||
math.floor(SALT / 36) % 36,
|
||||
math.floor(SALT / 1296) % 36,
|
||||
}
|
||||
|
||||
--- Encodes a number (0–935) as exactly 2 base-36 characters.
|
||||
--- @within CodeGenerator
|
||||
function CodeGenerator.encode_pair(n)
|
||||
return BASE36:sub(math.floor(n / 36) + 1, math.floor(n / 36) + 1)
|
||||
.. BASE36:sub(n % 36 + 1, n % 36 + 1)
|
||||
end
|
||||
|
||||
--- Decodes 2 base-36 characters back to a number.
|
||||
--- @within CodeGenerator
|
||||
function CodeGenerator.decode_pair(s)
|
||||
local d1 = BASE36:find(s:sub(1, 1), 1, true) - 1
|
||||
local d2 = BASE36:find(s:sub(2, 2), 1, true) - 1
|
||||
return d1 * 36 + d2
|
||||
end
|
||||
|
||||
--- Encrypts a player name into a code twice its length.
|
||||
--- Each input character (A-Z, value 0-25) is encoded as
|
||||
--- c + SALTS[i] * 26, producing 2 base-36 output characters.
|
||||
--- @within CodeGenerator
|
||||
--- @param text string NAME_LEN-character uppercase player name.
|
||||
--- @return string Encrypted code (2 * NAME_LEN base-36 characters).
|
||||
function CodeGenerator.encrypt(text)
|
||||
local result = ""
|
||||
for i = 1, NAME_LEN do
|
||||
local c = math.max(0, (string.byte(text, i) or 65) - 65)
|
||||
result = result .. CodeGenerator.encode_pair(c + SALTS[i] * 26)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Decrypts a personal code back to the original player name.
|
||||
--- @within CodeGenerator
|
||||
--- @param encrypted_text string The code to decrypt (2 * NAME_LEN chars).
|
||||
--- @return string Original player name, or "???" if the code is invalid.
|
||||
function CodeGenerator.decrypt(encrypted_text)
|
||||
local t = encrypted_text:upper()
|
||||
if #t ~= NAME_LEN * 2 then return "???" end
|
||||
local result = ""
|
||||
for i = 1, NAME_LEN do
|
||||
local pair = CodeGenerator.decode_pair(t:sub((i - 1) * 2 + 1, i * 2))
|
||||
result = result .. string.char(pair % 26 + 65)
|
||||
end
|
||||
return result
|
||||
end
|
||||
@@ -36,6 +36,8 @@ Day.register_handler(function()
|
||||
Context.toilet_meters_today_evening = false
|
||||
Context.coworker_discussion_meter_applied_today = false
|
||||
Context.sumphore_discussion_meter_applied_today = false
|
||||
Context.glitch_conversation_done_today = false
|
||||
Context.fast_food_eaten_today = 0
|
||||
end)
|
||||
|
||||
Day.register_handler(function()
|
||||
|
||||
@@ -8,6 +8,8 @@ local METER_DECAY_PER_DAY = 20
|
||||
local COMBO_BASE_BONUS = 0.02
|
||||
local COMBO_MAX_BONUS = 0.16
|
||||
local COMBO_TIMEOUT_FRAMES = 600
|
||||
local FLASH_DURATION = 2.0
|
||||
local FLASH_COLOR = 4
|
||||
|
||||
-- Internal meters for tracking game progress and player stats.
|
||||
Meter.COLOR_ISM = Config.colors.orange
|
||||
@@ -16,6 +18,12 @@ Meter.COLOR_BM = Config.colors.red
|
||||
Meter.COLOR_BG = Config.colors.meter_bg
|
||||
Meter.COLOR_CONTOUR = Config.colors.white
|
||||
|
||||
local _flash = {
|
||||
wpm = { timer = 0, delta = 0 },
|
||||
ism = { timer = 0, delta = 0 },
|
||||
bm = { timer = 0, delta = 0 },
|
||||
}
|
||||
|
||||
--- Gets initial meter values.
|
||||
--- @within Meter
|
||||
--- @return result table Initial meter values. </br>
|
||||
@@ -95,6 +103,12 @@ function Meter.update()
|
||||
end
|
||||
end
|
||||
end
|
||||
local dt = Context.delta_time or 0
|
||||
for _, key in ipairs({ "wpm", "ism", "bm" }) do
|
||||
if _flash[key].timer > 0 then
|
||||
_flash[key].timer = _flash[key].timer - dt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Adds amount to a meter.
|
||||
@@ -109,7 +123,18 @@ function Meter.add(key, amount)
|
||||
GameOverWindow.show(key)
|
||||
return
|
||||
end
|
||||
local prev_wpm = (key == "wpm") and m.wpm or nil
|
||||
local old_val = m[key]
|
||||
m[key] = math.max(0, math.min(METER_MAX, m[key] + amount))
|
||||
local actual_delta = m[key] - old_val
|
||||
if actual_delta ~= 0 and _flash[key] then
|
||||
_flash[key].delta = actual_delta
|
||||
_flash[key].timer = FLASH_DURATION
|
||||
end
|
||||
if prev_wpm and prev_wpm > 0 and m.wpm == 0 and Context.game_in_progress
|
||||
and Ascension.get_level() == 5 then
|
||||
Context.should_ascend = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -258,12 +283,32 @@ function Meter.draw()
|
||||
local fill_w = math.max(0, math.floor((m[meter.key] / max) * bar_w))
|
||||
rect(bar_x - 1, bar_y - 1, bar_w + 2, bar_h + 2, Meter.COLOR_CONTOUR)
|
||||
rect(bar_x, bar_y, bar_w, bar_h, Meter.COLOR_BG)
|
||||
if fill_w > 0 then
|
||||
local flash = _flash[meter.key]
|
||||
if flash and flash.timer > 0 then
|
||||
local old_val = m[meter.key] - flash.delta
|
||||
local old_fill_w = math.max(0, math.floor((old_val / max) * bar_w))
|
||||
local stable_w = math.min(fill_w, old_fill_w)
|
||||
if stable_w > 0 then
|
||||
rect(bar_x, bar_y, stable_w, bar_h, meter.color)
|
||||
end
|
||||
if flash.delta > 0 then
|
||||
local hi_w = fill_w - stable_w
|
||||
if hi_w > 0 then
|
||||
rect(bar_x + stable_w, bar_y, hi_w, bar_h, FLASH_COLOR)
|
||||
end
|
||||
else
|
||||
local hi_w = old_fill_w - fill_w
|
||||
if hi_w > 0 then
|
||||
rect(bar_x + fill_w, bar_y, hi_w, bar_h, FLASH_COLOR)
|
||||
end
|
||||
end
|
||||
elseif fill_w > 0 then
|
||||
rect(bar_x, bar_y, fill_w, bar_h, meter.color)
|
||||
end
|
||||
---print(meter.label, label_x, label_y, meter.color, false, 1, true)
|
||||
end
|
||||
|
||||
local ascension_y = start_y + 3 * line_h + 1
|
||||
Ascension.draw(bar_x, ascension_y, { spacing = 8 })
|
||||
Ascension.draw(bar_x - 4, ascension_y, { spacing = 8 })
|
||||
end
|
||||
|
||||
|
||||
@@ -54,12 +54,40 @@ local ASC_45_TEXT = [[
|
||||
|
||||
]]
|
||||
|
||||
local ASC_56_TEXT = [[
|
||||
Norman is not as productive as he should be.
|
||||
|
||||
Can we distract him?
|
||||
|
||||
We need to keep him busy.
|
||||
|
||||
We need
|
||||
|
||||
More
|
||||
|
||||
Time
|
||||
]]
|
||||
|
||||
local ASC_67_TEXT = [[
|
||||
He knows.
|
||||
|
||||
Norman has broken through the first veil.
|
||||
|
||||
The simulation is compromised.
|
||||
|
||||
This was not supposed to happen.
|
||||
|
||||
Not yet.
|
||||
]]
|
||||
|
||||
local ascension_texts = {
|
||||
[1] = ASC_01_TEXT,
|
||||
[2] = ASC_12_TEXT,
|
||||
[3] = ASC_23_TEXT,
|
||||
[4] = ASC_34_TEXT,
|
||||
[5] = ASC_45_TEXT,
|
||||
[6] = ASC_56_TEXT,
|
||||
[7] = ASC_67_TEXT,
|
||||
}
|
||||
|
||||
function MysteriousManScreen.get_text_for_level(level)
|
||||
@@ -146,11 +174,25 @@ function MysteriousManScreen.wake_up()
|
||||
end
|
||||
|
||||
-- Norman chooses to stay in bed, skipping the minigame and flash, and going straight to the next day.
|
||||
-- At ascension level 4, staying in bed triggers 4->5: shows the ascension text then wakes with flash.
|
||||
-- @within MysteriousManScreen
|
||||
function MysteriousManScreen.stay_in_bed()
|
||||
Day.increase()
|
||||
state = STATE_DAY
|
||||
day_timer = day_display_seconds
|
||||
if Ascension.get_level() == 4 then
|
||||
Context.should_ascend = true
|
||||
Day.increase()
|
||||
Ascension.consume_increase()
|
||||
trigger_flash_on_wake = true
|
||||
show_mysterious_screen = true
|
||||
text = MysteriousManScreen.get_text_for_level(Ascension.get_level())
|
||||
text_y = Config.screen.height
|
||||
text_done = false
|
||||
text_done_timer = 0
|
||||
state = STATE_TEXT
|
||||
else
|
||||
Day.increase()
|
||||
state = STATE_DAY
|
||||
day_timer = day_display_seconds
|
||||
end
|
||||
end
|
||||
|
||||
--- Starts the mysterious man screen.
|
||||
|
||||
@@ -4,15 +4,20 @@ Screen.register({
|
||||
decisions = {
|
||||
"go_to_home",
|
||||
"go_to_office",
|
||||
"eating_fast_food",
|
||||
},
|
||||
init = function()
|
||||
Audio.music_play_room_work()
|
||||
end,
|
||||
background = "street",
|
||||
draw = function()
|
||||
if Window.get_current_id() == "game" then
|
||||
Sprite.draw_at("norman", 7 * 8, 3 * 8)
|
||||
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
|
||||
local w = Window.get_current_id()
|
||||
if w == "game" or w == "discussion" then
|
||||
local norman_x = Context.fast_food_approaching and (19 * 8) or (7 * 8)
|
||||
Sprite.draw_at("norman", norman_x, 3 * 8)
|
||||
if Context.fast_food_eaten_today < 3 then
|
||||
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
|
||||
end
|
||||
Sprite.draw_at("dev_guard", 22 * 8, 2 * 8)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
WalkingToOfficeScreen = {}
|
||||
|
||||
Screen.register({
|
||||
id = "walking_to_office",
|
||||
name = "Walking to office",
|
||||
@@ -5,6 +7,7 @@ Screen.register({
|
||||
"go_to_home",
|
||||
"go_to_office",
|
||||
"sumphore_discussion",
|
||||
"eating_fast_food",
|
||||
},
|
||||
init = function()
|
||||
Audio.music_play_room_work()
|
||||
@@ -32,12 +35,18 @@ Screen.register({
|
||||
Context.walking_to_office_sprites = Sprite.list_randomize(possible_sprites, possible_positions)
|
||||
end,
|
||||
background = "street",
|
||||
update = function()
|
||||
end,
|
||||
draw = function()
|
||||
if Window.get_current_id() == "game" then
|
||||
Sprite.draw_at("norman", 7 * 8, 3 * 8)
|
||||
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
|
||||
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
|
||||
Sprite.draw_at("dev_guard", 22 * 8, 2 * 8)
|
||||
local w = Window.get_current_id()
|
||||
if w == "game" or w == "discussion" then
|
||||
local norman_x = Context.fast_food_approaching and (19 * 8) or (7 * 8)
|
||||
Sprite.draw_at("norman", norman_x, 3 * 8)
|
||||
Sprite.draw_at("sumphore", 9 * 8, 2 * 8)
|
||||
if Context.fast_food_eaten_today < 3 then
|
||||
Sprite.draw_at("pizza_vendor", 19 * 8, 1 * 8)
|
||||
end
|
||||
Sprite.draw_at("dev_guard", 22 * 8, 3 * 8)
|
||||
|
||||
Sprite.draw_list(Context.walking_to_office_sprites)
|
||||
end
|
||||
|
||||
@@ -30,9 +30,3 @@ function Input.back() return btnp(INPUT_KEY_B) or keyp(INPUT_KEY_BACKSPACE) end
|
||||
--- Checks if Enter is pressed.
|
||||
--- @within Input
|
||||
function Input.enter() return keyp(INPUT_KEY_ENTER) end
|
||||
--- Checks if Up is pressed or held (with repeat).
|
||||
--- @within Input
|
||||
function Input.up_repeat() return btnp(INPUT_KEY_UP, 20, 4) end
|
||||
--- Checks if Down is pressed or held (with repeat).
|
||||
--- @within Input
|
||||
function Input.down_repeat() return btnp(INPUT_KEY_DOWN, 20, 4) end
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
--- @section TextInput
|
||||
|
||||
TextInput = {}
|
||||
|
||||
local LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local _pos = {}
|
||||
local _cursor = 1
|
||||
local _max_len = 3
|
||||
|
||||
--- Initialises a new text input session.
|
||||
--- @within TextInput
|
||||
--- @param max_len number Maximum character count (default 3).
|
||||
function TextInput.init(max_len)
|
||||
_max_len = max_len or 3
|
||||
_pos = {}
|
||||
for i = 1, _max_len do _pos[i] = 1 end
|
||||
_cursor = 1
|
||||
end
|
||||
|
||||
--- Advances to the next letter at the cursor position (wraps Z→A).
|
||||
--- @within TextInput
|
||||
function TextInput.next_letter()
|
||||
_pos[_cursor] = (_pos[_cursor] % #LETTERS) + 1
|
||||
end
|
||||
|
||||
--- Goes back to the previous letter at the cursor position (wraps A→Z).
|
||||
--- @within TextInput
|
||||
function TextInput.prev_letter()
|
||||
_pos[_cursor] = ((_pos[_cursor] - 2) % #LETTERS) + 1
|
||||
end
|
||||
|
||||
--- Confirms the current letter and advances the cursor to the next position.
|
||||
--- When called on the last position the cursor moves into the done state.
|
||||
--- @within TextInput
|
||||
function TextInput.select_letter()
|
||||
if _cursor <= _max_len then _cursor = _cursor + 1 end
|
||||
end
|
||||
|
||||
--- Moves the cursor one position to the right (stops at last position).
|
||||
--- @within TextInput
|
||||
function TextInput.next_position()
|
||||
if _cursor < _max_len then _cursor = _cursor + 1 end
|
||||
end
|
||||
|
||||
--- Moves the cursor one position to the left (stops at first position).
|
||||
--- Also steps back out of the done state.
|
||||
--- @within TextInput
|
||||
function TextInput.prev_position()
|
||||
if _cursor > 1 then _cursor = _cursor - 1 end
|
||||
end
|
||||
|
||||
--- Returns the assembled name string.
|
||||
--- @within TextInput
|
||||
--- @return string
|
||||
function TextInput.get_name()
|
||||
local s = ""
|
||||
for i = 1, _max_len do s = s .. LETTERS:sub(_pos[i], _pos[i]) end
|
||||
return s
|
||||
end
|
||||
|
||||
--- Returns the current 1-based cursor position.
|
||||
--- @within TextInput
|
||||
--- @return number
|
||||
function TextInput.get_position()
|
||||
return _cursor
|
||||
end
|
||||
|
||||
--- Returns the letter at the given 1-based position.
|
||||
--- @within TextInput
|
||||
--- @param i number
|
||||
--- @return string
|
||||
function TextInput.get_letter(i)
|
||||
return LETTERS:sub(_pos[i], _pos[i])
|
||||
end
|
||||
|
||||
--- Returns true when all positions have been confirmed.
|
||||
--- @within TextInput
|
||||
--- @return boolean
|
||||
function TextInput.is_done()
|
||||
return _cursor > _max_len
|
||||
end
|
||||
@@ -30,15 +30,9 @@ function EndWindow.draw()
|
||||
Print.text(yes_text, centerX - 40, y, yes_color)
|
||||
Print.text(no_text, centerX + 10, y, no_color)
|
||||
elseif Context._end.state == "ending" then
|
||||
local cx = Config.screen.width / 2
|
||||
local name = Context.player_name or "AAA"
|
||||
local code = CodeGenerator.encrypt(name)
|
||||
Print.text_center("Game over -- good ending.", cx, 40, Config.colors.light_blue)
|
||||
Print.text_center("Congrats " .. name .. "!", cx, 54, Config.colors.white)
|
||||
Print.text_center("Your personal code:", cx, 72, Config.colors.light_grey)
|
||||
Print.text_center(code, cx, 84, Config.colors.white, false, 3)
|
||||
Print.text_center("Write it down!", cx, 112, Config.colors.item)
|
||||
Print.text_center("Press Z to return to menu", cx, 126, Config.colors.dark_grey)
|
||||
Print.text_center("Game over -- good ending.", Config.screen.width / 2, 50, Config.colors.light_blue)
|
||||
Print.text_center("Congratulations!", Config.screen.width / 2, 70, Config.colors.white)
|
||||
Print.text_center("Press Z to return to menu", Config.screen.width / 2, 110, Config.colors.light_grey)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -102,13 +102,17 @@ function MenuWindow.update()
|
||||
end
|
||||
end
|
||||
|
||||
--- Opens player name entry then starts a new game.
|
||||
--- Starts a new game from the menu.
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.new_game()
|
||||
PlayerNameWindow.init(function()
|
||||
Context.new_game()
|
||||
end)
|
||||
Window.set_current("player_name")
|
||||
Context.new_game()
|
||||
end
|
||||
|
||||
--- Loads a game from the menu.
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.load_game()
|
||||
Context.load_game()
|
||||
GameWindow.set_state("game")
|
||||
end
|
||||
|
||||
--- Saves the current game from the menu.
|
||||
@@ -135,13 +139,6 @@ function MenuWindow.controls()
|
||||
Window.set_current("controls")
|
||||
end
|
||||
|
||||
--- Opens the player name entry screen (test mode shortcut).
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.player_name()
|
||||
PlayerNameWindow.init()
|
||||
Window.set_current("player_name")
|
||||
end
|
||||
|
||||
--- Opens the credits screen.
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.credits()
|
||||
@@ -163,14 +160,6 @@ function MenuWindow.continued()
|
||||
GameWindow.set_state("continued")
|
||||
end
|
||||
|
||||
--- Opens the end screen for testing.
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.end_screen()
|
||||
Context._end.state = "ending"
|
||||
Context._end.selection = 1
|
||||
GameWindow.set_state("end")
|
||||
end
|
||||
|
||||
--- Opens the DDR minigame test.
|
||||
--- @within MenuWindow
|
||||
function MenuWindow.ddr_test()
|
||||
@@ -189,6 +178,7 @@ function MenuWindow.refresh_menu_items()
|
||||
end
|
||||
|
||||
table.insert(_menu_items, {label = "New Game", decision = MenuWindow.new_game})
|
||||
table.insert(_menu_items, {label = "Load Game", decision = MenuWindow.load_game})
|
||||
table.insert(_menu_items, {label = "Controls", decision = MenuWindow.controls})
|
||||
table.insert(_menu_items, {label = "Credits", decision = MenuWindow.credits})
|
||||
|
||||
@@ -196,8 +186,6 @@ function MenuWindow.refresh_menu_items()
|
||||
table.insert(_menu_items, {label = "Audio Test", decision = MenuWindow.audio_test})
|
||||
table.insert(_menu_items, {label = "To Be Continued...", decision = MenuWindow.continued})
|
||||
table.insert(_menu_items, {label = "DDR Test", decision = MenuWindow.ddr_test})
|
||||
table.insert(_menu_items, {label = "End Screen", decision = MenuWindow.end_screen})
|
||||
table.insert(_menu_items, {label = "Player Name", decision = MenuWindow.player_name})
|
||||
end
|
||||
|
||||
table.insert(_menu_items, {label = "Exit", decision = MenuWindow.exit})
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
--- @section PlayerNameWindow
|
||||
|
||||
local _frame = 0
|
||||
local _on_confirm = nil
|
||||
local MAX_LEN = 3
|
||||
local BOX_W = 24
|
||||
local BOX_H = 24
|
||||
local BOX_GAP = 12
|
||||
local BOX_Y = 50
|
||||
local WARN_Y = 104
|
||||
|
||||
local function box_start_x()
|
||||
return math.floor((Config.screen.width - (MAX_LEN * BOX_W + (MAX_LEN - 1) * BOX_GAP)) / 2)
|
||||
end
|
||||
|
||||
local function box_x(i)
|
||||
return box_start_x() + (i - 1) * (BOX_W + BOX_GAP)
|
||||
end
|
||||
|
||||
--- Initialises the player name window.
|
||||
--- @within PlayerNameWindow
|
||||
--- @param on_confirm function Called with the entered name when the player saves.
|
||||
function PlayerNameWindow.init(on_confirm)
|
||||
_frame = 0
|
||||
_on_confirm = on_confirm
|
||||
TextInput.init(MAX_LEN)
|
||||
end
|
||||
|
||||
local function draw_boxes()
|
||||
local cursor = TextInput.get_position()
|
||||
local blink = math.floor(_frame / 18) % 2 == 0
|
||||
|
||||
for i = 1, MAX_LEN do
|
||||
local x = box_x(i)
|
||||
local is_cur = (i == cursor)
|
||||
local done = TextInput.is_done()
|
||||
|
||||
local fill = (is_cur and not done) and Config.colors.blue or Config.colors.black
|
||||
local border = (is_cur and not done) and Config.colors.white
|
||||
or done and Config.colors.light_blue
|
||||
or Config.colors.dark_grey
|
||||
|
||||
rect (x, BOX_Y, BOX_W, BOX_H, fill)
|
||||
rectb(x, BOX_Y, BOX_W, BOX_H, border)
|
||||
|
||||
local show = not (is_cur and blink and not done)
|
||||
if show then
|
||||
local ch = TextInput.get_letter(i)
|
||||
local cw = print(ch, 0, -100, 0, false, 2)
|
||||
local cx = x + math.floor((BOX_W - cw) / 2)
|
||||
local cy = BOX_Y + math.floor((BOX_H - 11) / 2)
|
||||
local col = (is_cur and not done) and Config.colors.white or Config.colors.light_grey
|
||||
print(ch, cx, cy, col, false, 2)
|
||||
end
|
||||
end
|
||||
|
||||
-- caret arrow below active box
|
||||
if not TextInput.is_done() then
|
||||
local cx = box_x(cursor) + math.floor(BOX_W / 2)
|
||||
local ay = BOX_Y + BOX_H + 4
|
||||
line(cx - 4, ay, cx, ay + 4, Config.colors.white)
|
||||
line(cx + 4, ay, cx, ay + 4, Config.colors.white)
|
||||
end
|
||||
end
|
||||
|
||||
--- Draws the player name window.
|
||||
--- @within PlayerNameWindow
|
||||
function PlayerNameWindow.draw()
|
||||
cls(Config.colors.black)
|
||||
|
||||
Print.text_center("Player Name", Config.screen.width / 2, 14, Config.colors.white, false, 2)
|
||||
|
||||
draw_boxes()
|
||||
|
||||
if TextInput.is_done() then
|
||||
Print.text_center("Z: save name B: edit", Config.screen.width / 2, BOX_Y + BOX_H + 12, Config.colors.light_blue)
|
||||
else
|
||||
Print.text_center("Up/Dn: letter Lft/Rgt: move Z: ok", Config.screen.width / 2, BOX_Y + BOX_H + 12, Config.colors.dark_grey)
|
||||
end
|
||||
|
||||
-- Warning section
|
||||
rect(0, WARN_Y, Config.screen.width, Config.screen.height - WARN_Y, Config.colors.blue)
|
||||
rectb(0, WARN_Y, Config.screen.width, Config.screen.height - WARN_Y, Config.colors.light_blue)
|
||||
Print.text_center("Remember your name!", Config.screen.width / 2, WARN_Y + 8, Config.colors.white)
|
||||
Print.text_center("You will need it to load the game.", Config.screen.width / 2, WARN_Y + 20, Config.colors.light_grey)
|
||||
end
|
||||
|
||||
--- Updates player name window logic.
|
||||
--- @within PlayerNameWindow
|
||||
function PlayerNameWindow.update()
|
||||
_frame = _frame + 1
|
||||
|
||||
if TextInput.is_done() then
|
||||
if Input.select() then
|
||||
Context.player_name = TextInput.get_name()
|
||||
if _on_confirm then _on_confirm() else Window.set_current("menu") end
|
||||
elseif Input.back() then
|
||||
TextInput.prev_position()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if Input.up_repeat() then TextInput.next_letter() end
|
||||
if Input.down_repeat() then TextInput.prev_letter() end
|
||||
if Input.right() then TextInput.next_position() end
|
||||
if Input.select() then TextInput.select_letter() end
|
||||
|
||||
if Input.left() or Input.back() then
|
||||
if TextInput.get_position() > 1 then
|
||||
TextInput.prev_position()
|
||||
else
|
||||
Window.set_current("menu")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -45,6 +45,3 @@ Window.register("continued", ContinuedWindow)
|
||||
|
||||
CreditsWindow = {}
|
||||
Window.register("credits", CreditsWindow)
|
||||
|
||||
PlayerNameWindow = {}
|
||||
Window.register("player_name", PlayerNameWindow)
|
||||
|
||||
Reference in New Issue
Block a user