0,]']±>Sï}WÿÍu§ðp8·d%qy)6o;]ÉA¦ösï÷ôôô”°ÂVl†3ÃR-- title: Mr Anderson's Adventure -- name: mranderson -- author: Zsolt Tasnadi -- desc: Life of a programmer in the Vector -- site: https://games.teletype.hu -- license: MIT License -- version: 0.1 -- script: lua local c={}local d={}local e={}local f={}local g={}local h={}local i={}local j={}local k={}local l={}local m={}local n={}local o={}local p={screen={width=240,height=136},colors={black=0,light_grey=13,dark_grey=14,green=6,npc=8,item=12},player={w=8,h=8,start_x=120,start_y=128,sprite_id=1},physics={gravity=0.5,jump_power=-5,move_speed=1.5,max_jumps=2,interaction_radius_npc=12,interaction_radius_item=8},timing={splash_duration=120}}local q={screen=p.screen,colors=p.colors,player=p.player,physics=p.physics,timing=p.timing,}local r=7 local s=0 local t=1 local u=2 local v=0xDE function q.save()mset(q.physics.move_speed*10,s,r)mset(q.physics.max_jumps,t,r)mset(v,u,r)end function q.load()if mget(u,r)==v then q.physics.move_speed=mget(s,r)/10 q.physics.max_jumps=mget(t,r)else q.restore_defaults()end end function q.restore_defaults()q.physics.move_speed=p.physics.move_speed q.physics.max_jumps=p.physics.max_jumps end q.load()local w=0 local x=1 local y=2 local z=3 local A=4 local B=5 local C=6 local D=7 local E=6 local F=0 local G=0xCA local H=1 local I=2 local J=3 local K=4 local L=5 local M=6 local N=128 local function O(eb)local fb={}for gb,hb in pairs(eb)do if type(hb)=="table"then fb[gb]=O(hb)else fb[gb]=hb end end return fb end local function P()return{active_window=w,inventory={},intro={y=q.screen.height,speed=0.5,text="Mr. Anderson is an average\nprogrammer. His daily life\nrevolves around debugging,\npull requests, and end-of-sprint\nmeetings, all while secretly\ndreaming of being destined\nfor something more."},current_screen=1,splash_timer=q.timing.splash_duration,dialog={text="",menu_items={},selected_menu_item=1,active_entity=nil,showing_description=false,current_node_key=nil},player={x=q.player.start_x,y=q.player.start_y,w=q.player.w,h=q.player.h,vx=0,vy=0,jumps=0,sprite_id=q.player.sprite_id},ground={x=0,y=q.screen.height,w=q.screen.width,h=8},menu_items={},selected_menu_item=1,selected_inventory_item=1,game_in_progress=false,screens=O({{name="Screen 1",platforms={{x=80,y=110,w=40,h=8},{x=160,y=90,w=40,h=8}},npcs={{x=180,y=82,name="Trinity",sprite_id=2,dialog={start={text="Hello, Neo.",options={{label="Who are you?",next_node="who_are_you"},{label="My name is not Neo.",next_node="not_neo"},{label="...",next_node="silent"}}},who_are_you={text="I am Trinity. I've been looking for you.",options={{label="The famous hacker?",next_node="famous_hacker"},{label="Why me?",next_node="why_me"}}},not_neo={text="I know. But you will be.",options={{label="What are you talking about?",next_node="who_are_you"}}},silent={text="You're not much of a talker, are you?",options={{label="I guess not.",next_node="dialog_end"}}},famous_hacker={text="The one and only.",options={{label="Wow.",next_node="dialog_end"}}},why_me={text="Morpheus believes you are The One.",options={{label="The One?",next_node="the_one"}}},the_one={text="The one who will save us all.",options={{label="I'm just a programmer.",next_node="dialog_end"}}},dialog_end={text="We'll talk later.",options={}}}},{x=90,y=102,name="Oracle",sprite_id=3,dialog={start={text="I know what you're thinking. 'Am I in the right place?'",options={{label="Who are you?",next_node="who_are_you"},{label="I guess I am.",next_node="you_are"}}},who_are_are={text="I'm the Oracle. And you're right on time. Want a cookie?",options={{label="Sure.",next_node="cookie"},{label="No, thank you.",next_node="no_cookie"}}},you_are={text="Of course you are. Sooner or later, everyone comes to see me. Want a cookie?",options={{label="Yes, please.",next_node="cookie"},{label="I'm good.",next_node="no_cookie"}}},cookie={text="Here you go. Now, what's really on your mind?",options={{label="Am I The One?",next_node="the_one"},{label="What is the Matrix?",next_node="the_matrix"}}},no_cookie={text="Suit yourself. Now, what's troubling you?",options={{label="Am I The One?",next_node="the_one"},{label="What is the Matrix?",next_node="the_matrix"}}},the_one={text="Being The One is just like being in love. No one can tell you you're in love, you just know it. Through and through. Balls to bones.",options={{label="So I'm not?",next_node="dialog_end"}}},the_matrix={text="The Matrix is a system, Neo. That system is our enemy. But when you're inside, you look around, what do you see? The very minds of the people we are trying to save.",options={{label="I see.",next_node="dialog_end"}}},dialog_end={text="You have to understand, most of these people are not ready to be unplugged.",options={}}}}},items={{x=100,y=128,w=8,h=8,name="Key",sprite_id=4,desc="A rusty old key. It might open something."}}},{name="Screen 2",platforms={{x=30,y=100,w=50,h=8},{x=100,y=80,w=50,h=8},{x=170,y=60,w=50,h=8}},npcs={{x=120,y=72,name="Morpheus",sprite_id=5,dialog={start={text="At last. Welcome, Neo. As you no doubt have guessed, I am Morpheus.",options={{label="It's an honor to meet you.",next_node="honor"},{label="You've been looking for me.",next_node="looking_for_me"}}},honor={text="No, the honor is mine.",options={{label="What is this place?",next_node="what_is_this_place"}}},looking_for_me={text="I have. For some time.",options={{label="What is this place?",next_node="what_is_this_place"}}},what_is_this_place={text="This is the construct. It's our loading program. We can load anything from clothing, to equipment, weapons, training simulations. Anything we need.",options={{label="Right.",next_node="dialog_end"}}},dialog_end={text="I've been waiting for you, Neo. We have much to discuss.",options={}}}},{x=40,y=92,name="Tank",sprite_id=6,dialog={start={text="Hey, Neo! Welcome to the construct. I'm Tank.",options={{label="Good to meet you.",next_node="good_to_meet_you"},{label="This place is incredible.",next_node="incredible"}}},good_to_meet_you={text="You too! We've been waiting for you. Need anything? Training? Weapons?",options={{label="Training?",next_node="training"},{label="I'm good for now.",next_node="dialog_end"}}},incredible={text="Isn't it? The boss's design. We can load anything we need. What do you want to learn?",options={{label="Show me.",next_node="training"}}},training={text="Jujitsu? Kung Fu? How about... all of them?",options={{label="All of them.",next_node="all_of_them"}}},all_of_them={text="Operator, load the combat training program.",options={{label="...",next_node="dialog_end"}}},dialog_end={text="Just holler if you need anything. Anything at all.",options={}}}}},items={{x=180,y=52,w=8,h=8,name="Potion",sprite_id=7,desc="A glowing red potion. It looks potent."}}},{name="Screen 3",platforms={{x=50,y=110,w=30,h=8},{x=100,y=90,w=30,h=8},{x=150,y=70,w=30,h=8},{x=200,y=50,w=30,h=8}},npcs={{x=210,y=42,name="Agent Smith",sprite_id=8,dialog={start={text="Mr. Anderson. We've been expecting you.",options={{label="My name is Neo.",next_node="name_is_neo"},{label="...",next_node="silent"}}},name_is_neo={text="Whatever you say. You're here for a reason.",options={{label="What reason?",next_node="what_reason"}}},silent={text="The silent type. It doesn't matter. You are an anomaly.",options={{label="What do you want?",next_node="what_reason"}}},what_reason={text="To be deleted. The system has no place for your kind.",options={{label="I won't let you.",next_node="wont_let_you"}}},wont_let_you={text="You hear that, Mr. Anderson? That is the sound of inevitability.",options={{label="...",next_node="dialog_end"}}},dialog_end={text="It is purpose that created us. Purpose that connects us. Purpose that pulls us. That guides us. That drives us. It is purpose that defines. Purpose that binds us.",options={}}}},{x=160,y=62,name="Cypher",sprite_id=9,dialog={start={text="Well, well. The new messiah. Welcome to the real world.",options={{label="You don't seem happy.",next_node="not_happy"},{label="...",next_node="silent"}}},not_happy={text="Happy? Ignorance is bliss, Neo. We've been fighting this war for years. For what?",options={{label="For freedom.",next_node="freedom"}}},silent={text="Not a talker, huh? Smart. Less to regret later. Want a drink?",options={{label="Sure.",next_node="drink"},{label="No thanks.",next_node="no_drink"}}},drink={text="Good stuff. The little things you miss, you know? Like a good steak.",options={{label="I guess.",next_node="dialog_end"}}},no_drink={text="Your loss. More for me.",options={{label="...",next_node="dialog_end"}}},freedom={text="Freedom... right. If Morpheus told you you could fly, would you believe him?",options={{label="He's our leader.",next_node="dialog_end"}}},dialog_end={text="Just be careful who you trust.",options={}}}}},items={}}})}end a={}local function Q()local eb=P()for fb in pairs(a)do if type(a[fb])~="function"then a[fb]=nil end end for fb,gb in pairs(eb)do a[fb]=gb end end Q()function a.new_game()Q()a.game_in_progress=true e.refresh_menu_items()end function a.save_game()if not a.game_in_progress then return end mset(G,F,E)mset(a.player.x*10,H,E)mset(a.player.y*10,I,E)mset((a.player.vx*100)+N,J,E)mset((a.player.vy*100)+N,K,E)mset(a.player.jumps,L,E)mset(a.current_screen,M,E)end function a.load_game()if mget(F,E)~=G then a.new_game()return end Q()a.player.x=mget(H,E)/10 a.player.y=mget(I,E)/10 a.player.vx=(mget(J,E)-N)/100 a.player.vy=(mget(K,E)-N)/100 a.player.jumps=mget(L,E)a.current_screen=mget(M,E)a.game_in_progress=true e.refresh_menu_items()end function k.text(eb,fb,gb,hb,ib,jb)local kb=q.colors.black if hb==kb then kb=q.colors.light_grey end jb=jb or 1 print(eb,fb+1,gb+1,kb,ib,jb)print(eb,fb,gb,hb,ib,jb)end function m.talk_to()local eb=a.dialog.active_entity if eb.dialog and eb.dialog.start then g.set_dialog_node("start")else f.set_state(z)end end function m.fight()end function m.go_back()f.set_state(z)end function n.use()k.text("Used item: "..a.dialog.active_entity.name)f.set_state(B)end function n.look_at()g.show_description_dialog(a.dialog.active_entity,a.dialog.active_entity.desc)end function n.put_away()table.insert(a.inventory,a.dialog.active_entity)local eb=a.screens[a.current_screen]for fb,gb in ipairs(eb.items)do if gb==a.dialog.active_entity then table.remove(eb.items,fb)break end end f.set_state(z)end function n.go_back_from_item_dialog()f.set_state(z)end function n.go_back_from_inventory_action()f.set_state(z)end function n.drop()for fb,gb in ipairs(a.inventory)do if gb==a.dialog.active_entity then table.remove(a.inventory,fb)break end end local eb=a.screens[a.current_screen]a.dialog.active_entity.x=a.player.x a.dialog.active_entity.y=a.player.y table.insert(eb.items,a.dialog.active_entity)f.set_state(B)end function o.draw()spr(a.player.sprite_id,a.player.x,a.player.y,0)end function o.update()if l.left()then a.player.vx=-q.physics.move_speed elseif l.right()then a.player.vx=q.physics.move_speed else a.player.vx=0 end if l.player_jump()and a.player.jumpsq.screen.width-a.player.w then if a.current_screen<#a.screens then a.current_screen=a.current_screen+1 a.player.x=0 else a.player.x=q.screen.width-a.player.w end elseif a.player.x<0 then if a.current_screen>1 then a.current_screen=a.current_screen-1 a.player.x=q.screen.width-a.player.w else a.player.x=0 end end a.player.vy=a.player.vy+q.physics.gravity local eb=a.screens[a.current_screen]for fb,gb in ipairs(eb.platforms)do if a.player.vy>0 and a.player.y+a.player.h>=gb.y and a.player.y+a.player.h<=gb.y+gb.h and a.player.x+a.player.w>gb.x and a.player.xa.ground.y then a.player.y=a.ground.y-a.player.h a.player.vy=0 a.player.jumps=0 end if l.player_interact()then local fb=false for gb,hb in ipairs(eb.npcs)do if math.abs(a.player.x-hb.x)",gb-8,kb,q.colors.green)end k.text(jb.label,gb,kb,q.colors.green)end end function j.update_menu(eb,fb)if l.up()then fb=fb-1 if fb<1 then fb=#eb end elseif l.down()then fb=fb+1 if fb>#eb then fb=1 end end return fb end function j.word_wrap(eb,fb)if eb==nil then return{""}end local gb={}for hb in(eb.."\n"):gmatch("(.-)\n")do local ib=""local jb=0 for kb in hb:gmatch("%S+")do jb=jb+1 if#ib==0 then ib=kb elseif#ib+#kb+1<=fb then ib=ib.." "..kb else table.insert(gb,ib)ib=kb end end if jb>0 then table.insert(gb,ib)else table.insert(gb,"")end end if#gb==0 then return{""}end return gb end function j.create_numeric_stepper(eb,fb,gb,hb,ib,jb,kb)return{label=eb,get=fb,set=gb,min=hb,max=ib,step=jb,format=kb or"%.1f",type="numeric_stepper"}end function j.create_action_item(eb,fb)return{label=eb,action=fb,type="action_item"}end function c.draw()k.text("Mr. Anderson's",78,60,q.colors.green)k.text("Addventure",90,70,q.colors.green)end function c.update()a.splash_timer=a.splash_timer-1 if a.splash_timer<=0 or l.menu_confirm()then f.set_state(x)end end function d.draw()local eb=(q.screen.width-132)/2 k.text(a.intro.text,eb,a.intro.y,q.colors.green)end function d.update()a.intro.y=a.intro.y-a.intro.speed local eb=1 for fb in string.gmatch(a.intro.text,"\n")do eb=eb+1 end if a.intro.y<-eb*8 then f.set_state(y)end if l.menu_confirm()then f.set_state(y)end end function e.draw()j.draw_top_bar("Main Menu")j.draw_menu(a.menu_items,a.selected_menu_item,108,70)end function e.update()a.selected_menu_item=j.update_menu(a.menu_items,a.selected_menu_item)if l.menu_confirm()then local eb=a.menu_items[a.selected_menu_item]if eb and eb.action then eb.action()end end end function e.new_game()a.new_game()f.set_state(z)end function e.load_game()a.load_game()f.set_state(z)end function e.save_game()a.save_game()end function e.resume_game()f.set_state(z)end function e.exit()exit()end function e.configuration()i.init()f.set_state(D)end function e.refresh_menu_items()a.menu_items={}if a.game_in_progress then table.insert(a.menu_items,{label="Resume Game",action=e.resume_game})table.insert(a.menu_items,{label="Save Game",action=e.save_game})end table.insert(a.menu_items,{label="New Game",action=e.new_game})table.insert(a.menu_items,{label="Load Game",action=e.load_game})table.insert(a.menu_items,{label="Configuration",action=e.configuration})table.insert(a.menu_items,{label="Exit",action=e.exit})a.selected_menu_item=1 end i={controls={},selected_control=1,}function i.init()i.controls={j.create_numeric_stepper("Move Speed",function()return q.physics.move_speed end,function(eb)q.physics.move_speed=eb end,0.5,3,0.1,"%.1f"),j.create_numeric_stepper("Max Jumps",function()return q.physics.max_jumps end,function(eb)q.physics.max_jumps=eb end,1,5,1,"%d"),j.create_action_item("Save",function()q.save()end),j.create_action_item("Restore Defaults",function()q.restore_defaults()end),}end function i.draw()j.draw_top_bar("Configuration")local eb=10 local fb=40 local gb=q.screen.width-10 local hb=4 for ib,jb in ipairs(i.controls)do local kb=fb+(ib-1)*12 local lb=q.colors.green if jb.type=="numeric_stepper"then local mb=jb.get()local nb=jb.label local ob=string.format(jb.format,mb)local pb=gb-(#ob*hb)if ib==i.selected_control then lb=q.colors.item k.text("<",eb-8,kb,lb)k.text(nb,eb,kb,lb)k.text(ob,pb,kb,lb)k.text(">",gb+4,kb,lb)else k.text(nb,eb,kb,lb)k.text(ob,pb,kb,lb)end elseif jb.type=="action_item"then local mb=jb.label if ib==i.selected_control then lb=q.colors.item k.text("<",eb-8,kb,lb)k.text(mb,eb,kb,lb)k.text(">",eb+8+(#mb*hb)+4,kb,lb)else k.text(mb,eb,kb,lb)end end end k.text("Press B to go back",eb,120,q.colors.light_grey)end function i.update()if l.menu_back()then f.set_state(y)return end if l.up()then i.selected_control=i.selected_control-1 if i.selected_control<1 then i.selected_control=#i.controls end elseif l.down()then i.selected_control=i.selected_control+1 if i.selected_control>#i.controls then i.selected_control=1 end end local eb=i.controls[i.selected_control]if eb then if eb.type=="numeric_stepper"then local fb=eb.get()if btnp(2)then local gb=math.max(eb.min,fb-eb.step)eb.set(gb)elseif btnp(3)then local gb=math.min(eb.max,fb+eb.step)eb.set(gb)end elseif eb.type=="action_item"then if l.menu_confirm()then eb.action()end end end end function g.set_dialog_node(eb)local fb=a.dialog.active_entity local gb=fb.dialog[eb]if not gb then f.set_state(z)return end a.dialog.current_node_key=eb a.dialog.text=gb.text local hb={}if gb.options then for ib,jb in ipairs(gb.options)do table.insert(hb,{label=jb.label,action=function()g.set_dialog_node(jb.next_node)end})end end if#hb==0 then table.insert(hb,{label="Go back",action=function()f.set_state(z)end})end a.dialog.menu_items=hb a.dialog.selected_menu_item=1 a.dialog.showing_description=false f.set_state(A)end function g.update()if a.dialog.showing_description then if l.menu_confirm()or l.menu_back()then a.dialog.showing_description=false a.dialog.text=""end else a.dialog.selected_menu_item=j.update_menu(a.dialog.menu_items,a.dialog.selected_menu_item)if l.menu_confirm()then local eb=a.dialog.menu_items[a.dialog.selected_menu_item]if eb and eb.action then eb.action()end end if l.menu_back()then f.set_state(z)end end end function g.show_menu_dialog(eb,fb,gb)a.dialog.active_entity=eb a.dialog.text=""f.set_state(gb or A)a.dialog.showing_description=false a.dialog.menu_items=fb a.dialog.selected_menu_item=1 end function g.show_description_dialog(eb,fb)a.dialog.active_entity=eb a.dialog.text=fb f.set_state(A)a.dialog.showing_description=true end function g.draw()rect(40,40,160,80,q.colors.black)rectb(40,40,160,80,q.colors.green)if a.dialog.active_entity and a.dialog.active_entity.name then k.text(a.dialog.active_entity.name,120-#a.dialog.active_entity.name*2,45,q.colors.green)end local eb=j.word_wrap(a.dialog.text,25)local fb=55 for gb,hb in ipairs(eb)do k.text(hb,50,fb,q.colors.light_grey)fb=fb+8 end if not a.dialog.showing_description then j.draw_menu(a.dialog.menu_items,a.dialog.selected_menu_item,50,fb+2)else k.text("[A] Go Back",50,fb+10,q.colors.green)end end function h.draw()j.draw_top_bar("Inventory")if#a.inventory==0 then k.text("Inventory is empty.",70,70,q.colors.light_grey)else for eb,fb in ipairs(a.inventory)do local gb=q.colors.light_grey if eb==a.selected_inventory_item then gb=q.colors.green k.text(">",60,20+eb*10,gb)end k.text(fb.name,70,20+eb*10,gb)end end end function h.update()a.selected_inventory_item=j.update_menu(a.inventory,a.selected_inventory_item)if l.menu_confirm()and#a.inventory>0 then local eb=a.inventory[a.selected_inventory_item]g.show_menu_dialog(eb,{{label="Use",action=n.use},{label="Drop",action=n.drop},{label="Look at",action=n.look_at},{label="Go back",action=n.go_back_from_inventory_action}},C)end if l.menu_back()then f.set_state(z)end end function f.draw()local eb=a.screens[a.current_screen]j.draw_top_bar(eb.name)for fb,gb in ipairs(eb.platforms)do rect(gb.x,gb.y,gb.w,gb.h,q.colors.green)end for fb,gb in ipairs(eb.items)do spr(gb.sprite_id,gb.x,gb.y,0)end for fb,gb in ipairs(eb.npcs)do spr(gb.sprite_id,gb.x,gb.y,0)end rect(a.ground.x,a.ground.y,a.ground.w,a.ground.h,q.colors.dark_grey)o.draw()end function f.update()if l.menu_back()then a.active_window=y e.refresh_menu_items()return end o.update()end function f.set_state(eb)a.active_window=eb end local cb={[w]=function()c.update()c.draw()end,[x]=function()d.update()d.draw()end,[y]=function()e.update()e.draw()end,[z]=function()f.update()f.draw()end,[A]=function()f.draw()g.update()g.draw()end,[B]=function()h.update()h.draw()end,[C]=function()h.draw()g.draw()g.update()end,[D]=function()i.update()i.draw()end,}local db=false function b()if db then return end e.refresh_menu_items()db=true end function TIC()b()cls(q.colors.black)local eb=cb[a.active_window]if eb then eb()end end