From 541450bad81c2c67e651996054ccbaa64555053b Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 26 Nov 2023 05:09:56 -0500 Subject: [PATCH] GIT THIS --- .gitignore | 1 + README | 17 + assets/cave_e.map | 40 ++ assets/cave_e.toml | 14 + assets/cave_w.map | 40 ++ assets/cave_w.toml | 13 + assets/cool_cave.map | 40 ++ assets/cool_cave.toml | 34 ++ assets/gate.map | 40 ++ assets/gate.toml | 46 ++ assets/inner_king_palace.map | 40 ++ assets/inner_king_palace.toml | 55 +++ assets/main_menu | 40 ++ assets/main_menu.toml | 6 + assets/mountain_village.map | 40 ++ assets/mountain_village.toml | 14 + assets/new_cheese.map | 40 ++ assets/new_cheese.toml | 54 ++ assets/old_cheese.map | 40 ++ assets/old_cheese.toml | 87 ++++ assets/outer_king_palace.map | 40 ++ assets/outer_king_palace.toml | 41 ++ assets/test.map | 40 ++ assets/witch_village.map | 40 ++ assets/witch_village.toml | 42 ++ assets/worldmap.map | 40 ++ assets/worldmap.toml | 31 ++ game.pl | 906 ++++++++++++++++++++++++++++++++++ 28 files changed, 1881 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 assets/cave_e.map create mode 100644 assets/cave_e.toml create mode 100644 assets/cave_w.map create mode 100644 assets/cave_w.toml create mode 100644 assets/cool_cave.map create mode 100644 assets/cool_cave.toml create mode 100644 assets/gate.map create mode 100644 assets/gate.toml create mode 100644 assets/inner_king_palace.map create mode 100644 assets/inner_king_palace.toml create mode 100644 assets/main_menu create mode 100644 assets/main_menu.toml create mode 100644 assets/mountain_village.map create mode 100644 assets/mountain_village.toml create mode 100644 assets/new_cheese.map create mode 100644 assets/new_cheese.toml create mode 100644 assets/old_cheese.map create mode 100644 assets/old_cheese.toml create mode 100644 assets/outer_king_palace.map create mode 100644 assets/outer_king_palace.toml create mode 100644 assets/test.map create mode 100644 assets/witch_village.map create mode 100644 assets/witch_village.toml create mode 100644 assets/worldmap.map create mode 100644 assets/worldmap.toml create mode 100755 game.pl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..abf729d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +jjakkes-fernando-game.save.toml diff --git a/README b/README new file mode 100644 index 0000000..ec7d189 --- /dev/null +++ b/README @@ -0,0 +1,17 @@ +this game has been written in Perl. It has perl dependencies + +# cpanm TOML File::Slurper Curses +Note that 'Curses dev files' may need to be installed as Curses module relies on to build (it is a 'perl-lish' wrapper around the C one) + +eg some command like +# package-manager install curses-dev +but for your situation and relevent package + +Note: +this game is intended for mature audeninces due to the SHOCKING material found in it e.g. regicide + +also, the code ~base~ is a big hot mess, so if I touch it again, I'll do it proper now that I have an idea of what not to do. spaghetti code everywhere... ... ahahaha.... + +I advise against looking at the 'assets' dir (or the single program) until you beat the game or you'll spoil yourself, if you care about that kind of thing + +does write to a file called 'jjakkes-fernando-game.save.toml' in the current directory when you save. diff --git a/assets/cave_e.map b/assets/cave_e.map new file mode 100644 index 0000000..2cc8391 --- /dev/null +++ b/assets/cave_e.map @@ -0,0 +1,40 @@ + + + + + # +## # ##### +# ######### # #### +e ##### ##### ### ## +# ### ### ## ###### ### +# ### ### ####### +# # +# # +## # + # ## + ### ############################ + # # + # # + # # + # # + # ## + # # + # # ############# + # # # # + # # #### # e + # # ####### ### ### ##### ## ####### + #### # # ##### # ### + ###### # + ## #### # + ## ## ### + ########## ########### + # # + ## ## + # # + # # + # # + # # + ## # + ## + + diff --git a/assets/cave_e.toml b/assets/cave_e.toml new file mode 100644 index 0000000..94a4a55 --- /dev/null +++ b/assets/cave_e.toml @@ -0,0 +1,14 @@ +interact = [ + [23,79, "east_exit"], + [ 7, 1, "mountain_village"] +] +npc = [ +] +map_script = """ +""" +[npc_movements] +none = ['w'] + +[objects] +east_exit = "teleport('worldmap', 15, 34)" +mountain_village = "teleport('mountain_village', 11, 77)" diff --git a/assets/cave_w.map b/assets/cave_w.map new file mode 100644 index 0000000..5fe1cfe --- /dev/null +++ b/assets/cave_w.map @@ -0,0 +1,40 @@ + ##e## + # # + # # + # # + # # + # # + # # + # # + # # ## + ## # # # + # #### # + # # + # ##### # + # # #### + # # + # # + # # + # ## + # # + # # + ### # + # # + # ### # + # # # # + # # # # + # # # # + ## # # + # ## + # # + # ## + # ## + # ### + # # + ###### # + # # + # # + # # + # # + # # + ###e## diff --git a/assets/cave_w.toml b/assets/cave_w.toml new file mode 100644 index 0000000..b53540e --- /dev/null +++ b/assets/cave_w.toml @@ -0,0 +1,13 @@ +interact = [ + [ 0, 15, "worldmap"], + [ 39, 40, "mountain_village"], +] +npc = [] +map_script = """ +""" +[npc_movements] +none = ['w'] + +[objects] +worldmap = "teleport('worldmap', 8, 22)" +mountain_village = "teleport('mountain_village', 10,10)" diff --git a/assets/cool_cave.map b/assets/cool_cave.map new file mode 100644 index 0000000..a2fcab6 --- /dev/null +++ b/assets/cool_cave.map @@ -0,0 +1,40 @@ + + + + + + + + + ______ + / \_ + / \___ + | \ + | | + \ / + \ | + | | + | / + / / + / | + | / + \ | + \ / + \ / + | | + \ | + | | + e + + + + + + + + + + + + + diff --git a/assets/cool_cave.toml b/assets/cool_cave.toml new file mode 100644 index 0000000..66e1ceb --- /dev/null +++ b/assets/cool_cave.toml @@ -0,0 +1,34 @@ +interact = [ + [ 26, 32, "mountain_village" ], +] +npc = [ + [14,33,"S","cool_sword", "none", "magenta_black"] +] +map_script = """ +if (exists $game_vars{cool_sword} and $game_vars{cool_sword} == 1) { + delete $npcs{S}; +} +""" +[npc_movements] +none = ['w'] + +[objects] +mountain_village = "teleport('mountain_village', 8, 44)" +cool_sword = """ +if (exists $game_vars{cool_sword}) { + dialog('',qq(You're not cool enough to pull this sword.\nMaybe the next life? (1/3 chance after all))); +} +else { + my $chance = int rand 3; # maxs at 2; 0, 1, 2 + if ($chance == 2) { + dialog('Cool Sword',qq(Woah. You're Cool. I'll let you wield me)); + item(qq(Cool Sword)); + delete $npcs{S}; + $game_vars{cool_sword} = 1; + } + else { + dialog('Cool Sword',qq(You're not Cool. ... This life. Be gone.)); + $game_vars{cool_sword} = 0; + } +} +""" diff --git a/assets/gate.map b/assets/gate.map new file mode 100644 index 0000000..ce2c79d --- /dev/null +++ b/assets/gate.map @@ -0,0 +1,40 @@ +################################################################################ +# t trt t br rttb t b t rrt b rt t b r tb b ttrrt btb rb tt t # +#bb t b rrr t t t r t t r trr ttbrtb tbr r bb t tb t b r tt # +# b b rtb b ttb bbb btt r rbt tr tt t t r t tbr t br t bt # +# rtt btbr bt tb tttbr rb rttb t b t t b rtrrbrrr rt t rbb bb # +# r tttbbtbb t t rt r rr trtrbr r t rt rr tb brbb b r t# +# t rb r b t tbb b brt rbbrt t r trt rt rb rr r r tbr# +#t rt bb b rbb r ttt rtbtrb rr rtt rtb br r t t r r r r bb r b# +#bt br t tb b trb t bbb r brtt tt t b rrtrrtb t t tb br rbtrtb t # +# brt t r bb bbrb rt r t t bb brb bt rtr trt r tb b b rr # +#r rr t br tt rt r r r brr tbr br r r r b t b b t b t bb b# +# rt r r r t tt trrt rrrr t bbt r rbbtrtr t r b rttr t rt rbbrbr# +# br t b b r b rbr^^^^^^ tbrr^^^^ rb bb rrr t r b b tbb tb t # +#^^^^t b brb r^^^^^^^ ^^^^^^^ ^^^^^^ r r ^^^^^^^b b b ^^^^^^ # +# ^^ ^^^^ ^^^^^ ^^^^t ^^ |^^^^^# +# ^^^^^^ r t t ^^^^^ / # +# b \ r b | r # +# | b r ]| # +# |[ b r r g +g t t t ]| # +# b |[ t b b \ # +# | b b r b | # +# | r b ^^^ # +# / ^^^ ^^ # +#^^ ^^^^ ^^^ ^^ ^^ ^^^^ ^^b ttb r^^^# +# ^^^ bb ^^^^ ^ r ^^^^ ^^^^ ^^^ ^ ^^ ^rb t^^ ^^^ bt bb r rr r# +# b r r tb ^^^^r t b ^^^tb tt b^^^^bbrt ^^^^ rtb ^^^^^b rrt bt rt# +#bb tr r tr ttb ttrrt bb bt r b b b tttb b ttrrr t r b bbrr # +#t bt tbb t b r b tbr t rb t btbrt t rrr r t tb r rr b b# +# t b t bb tb rttt r bt rtb tb b bb b t b t rrt r bb r b t b # +#b b rrr t t t trt rrb tbb rt b t rtb r b b t t b tttb b r # +#t tbt t t b r rbbb t r rr r b b rt b t tt t rbr btt t tbr# +#tr br rtr t btbbtt r t b r br b t rt t b bt b rt rrr r # +#br r trr t r r r tt bt ttt t tb bbr b r bbbrb b rtr t br# +#r rb rrb rtt r t b brtbtrr b btt ttrtr brb t r rr b rt trb b bb b br # +# bb trbtbt b tb r r r btrrt br b bbrbb b trr r t b t b b r rt b# +#btt bbt rt b r t tr b tb rt r rr r brr t tt r bb tbr rr rr t b r # +#b rb b bb t bt rtrbtr t bbb rt btb tb rb rt rtrr r rtr b b t b t # +#tbr t rrb rbb rr b trtb r b r tbr ttrr t t tb r t b b bt b b # +################################################################################ diff --git a/assets/gate.toml b/assets/gate.toml new file mode 100644 index 0000000..e199040 --- /dev/null +++ b/assets/gate.toml @@ -0,0 +1,46 @@ +interact = [ + [ 18, 79, "king_side" ], + [ 19, 0, "other_side" ], +] +npc = [ + [ 15,33, "q", "rat_ni", "random_movement", "yellow_black"], + [ 22,48, "y", "rat_ni", "random_movement", "yellow_black"], + [ 17,62, "p", "rat_ni", "random_movement", "yellow_black"], + [ 22, 6, "d", "rat_ni", "random_movement", "yellow_black"], + [ 19,12, "U", "shrubbery", "none", "cyan_black"], # comment this out +] +map_script = """ +if (exists $game_vars{shrubbery} and $game_vars{shrubbery} == 3) { + $npcs{U}->[3] = 'random_movement'; + $npcs{U}->[0] = 14; + $npcs{U}->[1] = 26; +} +""" +[npc_movements] +none = ['w'] + +[objects] +king_side = "teleport('worldmap', 7, 54)" +other_side = "teleport('worldmap',7, 51)" +rat_ni = "dialog(qq(Rats that say 'Ni!'), qq(Ni!));" +shrubbery = """ +my $quest_string = qq(WE ARE THE RATS THAT SAY 'NI!'\nIF YOU WISH TO GET BY US, YOU MUST PRESENT A\n SHRUBBERY); + +if (not exists $game_vars{shrubbery}) { + quest(qq(Rats that say 'Ni!'), 'Gate to King Palace', qq(Find a shrubbery), qq(The Rats that say 'Ni!' are blocking my way to the King's palace.\nThe leader has demanded that I find and present them a shrubbery.), 'shrubbery'); + dialog(qq(Rats that say 'Ni!'), $quest_string); + $game_vars{shrubbery} = 1; +} +elsif ($game_vars{shrubbery} == 1) { + dialog(qq(Rats that say 'Ni!'), $quest_string); +} +elsif ($game_vars{shrubbery} == 2) { + dialog(qq(Rats that say 'Ni!'), qq(WE ARE THE RATS THAT SAY 'NI!'\nYOU HAVE A SHRUBBERY? VERY WELL, YOU MAY PASS.)); + $game_vars{shrubbery} = 3; + $npcs{U}->[3] = 'random_movement'; + quest_desc_add(qq(\nI have found the shrubbery and presented it to the Rats that say 'Ni!'. They\nhave granted me passage though the only gate on this world.), 'shrubbery'); +} +elsif ($game_vars{shrubbery} == 3) { + dialog(qq(Rats that say 'Ni!'), qq(YOU'VE PRESENTED A SHRUBBERY. YOU MAY PASS.)); +} +""" diff --git a/assets/inner_king_palace.map b/assets/inner_king_palace.map new file mode 100644 index 0000000..a178f2a --- /dev/null +++ b/assets/inner_king_palace.map @@ -0,0 +1,40 @@ +################################################################################ +# # +# # +#______________________________________________________________________________# +# # +# _ " # +# " / \ " # +# / \ # +# | " # +# \ / # +# \_/ " # +# " # +# " " " # +# # +#______________________________| |_________________________________________# +# i\ /i # +# i\ /i # +#______________________________i\ /i_________________________________________# +# i\ /i # +# i\ /i # +# # +# # +# # +# # +# # +# # +# # +###################### ########################### +# # # # +#____________________# #_________________________# +# # # # +# b # # b # +# # # # +# # # +# # # +# # # # +# # # # +# # # # +# # # # +#######################################e######################################## diff --git a/assets/inner_king_palace.toml b/assets/inner_king_palace.toml new file mode 100644 index 0000000..9f48579 --- /dev/null +++ b/assets/inner_king_palace.toml @@ -0,0 +1,55 @@ +interact = [ + [ 39, 39, "outer_king_palace" ] +] +npc = [ + [8,66,"j","jjakke", "none", "green_black"] +] +map_script = """ +if (exists $game_vars{jjakke} and $game_vars{jjakke} == 4) { + $npcs{j}->[4] = 'black_red'; +} +""" +[npc_movements] +none = ['w'] + +[objects] +outer_king_palace = "teleport('outer_king_palace', 9, 35) " +jjakke = """ +if (not exists $game_vars{jjakke}) { + $game_vars{jjakke} = 1; + dialog('King J.J. Akke', qq(Ah, yes a visitor to the crown, how quaint.)); +} +elsif ($game_vars{jjakke} == 1) { + $game_vars{jjakke} = 2; + dialog('King J.J. Akke', qq(Hmm? You're still here? Ah yes, of course.\nHere take this HP Potion.\nNow go away, PEASENT. I mean, valued fellow rat.)); + item('hp potion'); +} +elsif ($game_vars{jjakke} == 2) { + if (exists $game_vars{mom} and $game_vars{mom} == 4) { # ie, mom tasks you to kill the king + if ($hands eq 'lil grabbies') { + $game_vars{jjakke} = 3; + dialog('King J.J. Akke', qq(What's that? You intend to kill me? ... Hahaha!\nYou can't hurt me with your lil grabbies!\nYou better *SAVE* your life!)); # fernando *will* die + } else { + $game_vars{jjakke} = 3; + dialog('King J.J. Akke', qq(What's that? You intend to kill me? ...\nWell, bring it ON!!)); + } + } + else { + dialog('King J.J. Akke', qq(Go home little rat; I've given you items.\nGo tell mommy what a great King I am.)); + } +} +elsif ($game_vars{jjakke} == 3) { + battle('King J.J. Akke'); + quest_desc_add(qq(I killed the king for mom. I should go talk to her.), 'mom_quest'); + if ($hands eq 'Cool Sword') { + dialog('King J.J. Akke', qq(Dang... that's a cool sword.\nx_x)); + } + else { + dialog('King J.J. Akke', qq(I can't believe I died to such a... lame sword!\nx_x)); + } + $npcs{j}->[4] = 'black_red'; + $game_vars{jjakke} = 4; + $game_vars{mom} = 5; +} + +""" diff --git a/assets/main_menu b/assets/main_menu new file mode 100644 index 0000000..20bf845 --- /dev/null +++ b/assets/main_menu @@ -0,0 +1,40 @@ + + + + ████ ███ ████ █ █ ███ █ █ ███ ███ ██ ███ + █ █ █ █ ██ █ █ █ ██ █ █ █ █ █ ██ █ + ████ ██ ████ █ █ █ █████ █ █ █ █ █ █ █ ███ + █ █ █ █ █ ██ █ █ █ ██ █ █ █ █ █ + █ ███ █ █ █ █ █ █ █ █ ███ ███ ███ + + + grand adenture! + + + + + + (n)ew game + (q)uit + + + + + + + + + + + arrow keys to move + jjakke's fernando game! space to interact! + m key to open the menu! + + (game needs a 80x40 resulotion, sorry! big 'X' shows corner) + (don't resize window while in game, it breaks it big time and + I don't know how to fix it, right now if you do resize, + save -> exit -> restart) + + + + X diff --git a/assets/main_menu.toml b/assets/main_menu.toml new file mode 100644 index 0000000..ad5877c --- /dev/null +++ b/assets/main_menu.toml @@ -0,0 +1,6 @@ +do_this = """ +if (-e $save_file and -r $save_file) { + move(15,11); + printw("(l)oad game"); +} +""" diff --git a/assets/mountain_village.map b/assets/mountain_village.map new file mode 100644 index 0000000..419047e --- /dev/null +++ b/assets/mountain_village.map @@ -0,0 +1,40 @@ +################################################################################ +# | | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | | # +# | | | | | | | | | | |__| |___| | | # +# | | | | | | | | |_C_| / \ / \ | | # +# | |___| | | | | |_____/ \___/ \_____/ \__| | # +# |__C_/ \__| | | |__/ \___| # +#______/ \__| |______/ \__C_# +# \___/ ___ # +# / \ # +# / \ # +# ####### # +# ___ #=====# # +# / \ #=====# # +# / \ #### ## ___ # +# ####### V / \ # +# #=====# / \ # +# #=====# ####### # +# ## #### #=====# # +# V #=====# # +# ### ### # +# V # +#\ # +# \____ ______ # +# | \__ / \___ _____ # +# | | \____/ | \______ _____ /| \___ # +# | | | | | | \ /| \_____| | | \___ # +# | | | | | | \________/ | | | | | | \______# +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +# | | | | | | | | | | | | | | | # +################################################################################ diff --git a/assets/mountain_village.toml b/assets/mountain_village.toml new file mode 100644 index 0000000..44f7edc --- /dev/null +++ b/assets/mountain_village.toml @@ -0,0 +1,14 @@ +interact = [ + [10,77,'cave_e'], + [7,44,'cool_cave'], + [9,10,'cave_w'] +] +npc = [] +map_script = """ +""" +[npc_movements] +none = ['w'] +[objects] +cave_e = "teleport('cave_e', 7, 1) " +cave_w = "teleport('cave_w', 38, 40) " +cool_cave = "teleport('cool_cave', 25, 32)" diff --git a/assets/new_cheese.map b/assets/new_cheese.map new file mode 100644 index 0000000..b886616 --- /dev/null +++ b/assets/new_cheese.map @@ -0,0 +1,40 @@ +####################################g########################################### +# ] [ # +# \ / t # +# #################### | | i--------------i # +# #)()()()()()()()()(# | | | ####### | # +# #)()()()()()()()()(# / / | # # # # | # +# ################ ###-------/ / | | t # +# V| | | | # +# \_______________ _____________/ | | # +# | | t # +# i------- ------i # +# V V # +# # +# # +# ###### # +# t #@@@@# t # +# #@@@@# ###### # +# ### ## #&&&&# # +# ###### t V #&&&&# # +# #|\|\# t ## ### # +# #|/|/# V # +# ### ## # +# V # +# ] [V # +# i------------------i---------- -------------------------------i # +# | " " " " " " " " "| | # +# | | t | # +# | " " " " " " " " "| ######################### | t # +# | |V #/\ /\ /\ /\ /\ /\ /\ /\# | # +# | " " " " " " " " "| #\/ \/ \/ \/ \/ \/ \/ \/# | t # +# | | ######################### | # +# | " " " " " " " " #_-?_-?_-?_-?_-?_-?_-?_-# | # +# | " " " " " " " " | #?_-?_-?_-?_-?_-?_-?_-?_# | # +# | | #### #################### | # +# | " " " " " " " " "| t t t | tt # +# | | t t | # +# | " " " " " " " " "| | t # +# i------------------i-------------------------------------------i # +# # +################################################################################ diff --git a/assets/new_cheese.toml b/assets/new_cheese.toml new file mode 100644 index 0000000..25822c4 --- /dev/null +++ b/assets/new_cheese.toml @@ -0,0 +1,54 @@ +interact = [ + [0, 36, "worldmap"], + [28, 23, "cheese_farm"], +] +npc = [ + [35,7,'F','frog','random_movement','green_black'], + [27,30,'l', 'landlord', 'random_movement', 'cyan_black'], + [ 34, 39, "S", "bush", "none", "red_green"], +] +map_script = """ +if (exists $game_vars{witch} and $game_vars{witch} >= 3) { + delete $npcs{F}; +} +if (exists $game_vars{shrubbery} and $game_vars{shrubbery} >= 2) { + delete $npcs{S}; +} +""" + +[npc_movements] +none=['w'] +[objects] +worldmap = "teleport('worldmap', 30,9)" +frog = """ +if (exists $game_vars{witch} and $game_vars{witch} == 2) { + $game_vars{witch} = 3; + addch($npcs{F}->[0], $npcs{F}->[1], ' '); # put whitespace over + delete $npcs{F}; + dialog('',qq(You've acquired: "Frog"\n(this creature won't stop yelling))); + item(qq(Frog)); + quest_desc_add(qq(\nI've aquired a frog, perhaps this creature will be suitable as a familiar.), 'witch_quest'); +} else { + dialog('Frog', 'Ribbit.'); +} +""" +landlord = """ +if (not exists $game_vars{witch} or (exists $game_vars{witch} and $game_vars{witch} <= 1)) { + dialog(qq(New Cheese's Landlord), qq(AAA! AAA! FROG FROG! SOMEONE TAKE CARE OF IT!\nBut humanely!!)); +} elsif (exists $game_vars{witch} and $game_vars{witch} >= 2) { + dialog(qq(New Cheese's Landlord), qq(You're taking the Frog to the witch? Well...\nShe is nice to animals...)); +} +""" +bush = """ +if (not exists $game_vars{shrubbery}) { + dialog('', qq(Some shrubbery bush. Probably not important.)); +} +elsif ($game_vars{shrubbery} == 1) { + $game_vars{shrubbery} = 2; + addch($npcs{S}->[0], $npcs{S}->[1], ' '); # put whitespace over shrubbery + delete $npcs{S}; + dialog('',qq(You've acquired: "Shrubbery")); + item(qq(Shrubbery)); +} +""" +cheese_farm = "dialog('Sign', qq(Cheese Farm: New Cheese's only source of income!\nIt would be BAD if some frog ate all our cheese!))" diff --git a/assets/old_cheese.map b/assets/old_cheese.map new file mode 100644 index 0000000..d3547a2 --- /dev/null +++ b/assets/old_cheese.map @@ -0,0 +1,40 @@ +################################################################################ +# | # +# | # +# " " " " " " " " " " " " " " | t t # +# " " " " " " " " " " " " " " | ################ # +# " " " " " " " " " " " " " " | #+|+|+|+|+|+|+|# t # +# " " " " " " " " " " " " " " | #|+|+|+|+|+|+|+# # +# " " " " " " " " " " " " " " | ################ # +# " " " " " " " " " " " " " " | #+|+|+|+|+|+|+|# t # +# " " " " " " " " " " " " " " | #|+|+|+|+|+|+|+# # +# " " " " " " " " " " " " " " | ####### ####### h # +# | # +# V | ]\ /[ # +# | | | t # +#------------] [ | | | # +# \------------------------ \ / -----------------# +# \---------] [----/ # +# ####### V / # +# #+++++# / # +# #+++++# | # +# #+++++# ####### | # +# ### ### #+++++# | " " " " " " " " # +# V #+++++# | " " " " " " " " # +# ### ### | " " " " " " " " # +# V rrr " " " " " " " " # +# rfr | " " " " " " " " # +# rrr |V" " " " " " " " # +# | " " " " " " " " # +# | " " " " " " " " # +# /--------\ V | " " " " " " " " # +# / TTTTTTT \ / " " " " " " " " # +# ####### / T T T T V\ ###################/ " " " " " " " " # +# #+++++# \ \ #@@ @@@ @@@ @@@ @ # " " " " " " " " # +# #+++++# \ h | | #@@ @@@ @@@ @@@ @ # " " " " " " " " # +# ### ### \ | | ############# ##### " " " " " " " " # +# V \ h | | | V | # +# | | | | | # +# | h | \ / t | # +# | | | # +#####################################g########################################## diff --git a/assets/old_cheese.toml b/assets/old_cheese.toml new file mode 100644 index 0000000..6ba3880 --- /dev/null +++ b/assets/old_cheese.toml @@ -0,0 +1,87 @@ +interact = [ + [ 29, 38, "village_sign" ], + [ 24, 29, "fernandos_hut" ], + [ 22, 13, "bobs_hut" ], + [ 35, 13, "joes_hut" ], + [ 17, 52, "landlord_house" ], + [ 31, 34, "stables" ], + [ 12, 18, "cheese_farm" ], + [ 26, 60, "cheese_farm" ], + [ 35, 50, "guard_barracks" ], + [ 33, 28, "horse" ], + [ 35, 30, "horse" ], + [ 37, 30, "horse" ], + [ 10, 66, "r_horse" ], + [ 39, 37, "exit" ], + [ 2, 54, "landlord_sword"], +] +npc = [ + [ 26, 26, "m", "mom", "random_movement", "green_black"], + [ 23, 13, "b", "bob", "bob1", "white_black"], + [ 36, 14, "j", "joe", "none", "white_black"], + [ 2, 54, "S", "landlord_sword", "none", "red_black" ], +] +map_script = """ +if (exists $game_vars{mom} and $game_vars{mom} >= 2) { + delete $npcs{S}; +} +""" +[npc_movements] +bob1 = ['l','l','l','l','l','l','u','u','u','u','u','u','u','r','r','r','r','r','r','r','r','r','d','d','d','d','d','d','d','l','l','l'] # walking around his hut +none = ['w'] + +[objects] +"village_sign" = " dialog('Sign', 'Welcome to Old Cheese!') " +"fernandos_hut" = " dialog('Sign', \"Fernando's Hut\") " +"bobs_hut" = " dialog('Sign', \"Bob's Hut\") " +"joes_hut" = " dialog('Sign', \"Joe's Hut\") " +"landlords_house" = " dialog('Sign', \"House of the Landlord. Do not steal anything.\") " +stables = "dialog('Sign', 'Stables')" +"cheese_farm" = "dialog('Sign', 'Cheese Farm; our only source of income. It would\nbe BAD if something happened to it.')" +horse = "dialog('Horse','Neigh.')" +r_horse = "dialog('Regal Horse', 'Neeeeeigh.')" +exit = "teleport('worldmap', 14, 14)" +"guard_barracks" = "dialog('Sign', qq(Guards provided by the King))" +mom = """ +if (not exists $game_vars{mom}) { + $game_vars{mom} = 1; + dialog('mom',qq(oh dear, if only someone would\nKILL the king for mommy...)); +} +elsif ($game_vars{mom} == 1) { + dialog('mom',qq(You should start by stealing a sword (S) from the\nlandlord... but take care!\nDon't let him see you...)); + quest('mom', 'Old Cheese', qq(Steal Landlord's Sword), 'Mom has tasked me with killing the King, to do so, I must procure a sword\nfrom the Landlord.', 'mom_quest'); +} +elsif ($game_vars{mom} == 2) { + dialog('mom',qq(What's that you STOLE THE SWORD from the\nlandlord? I thought I raised you better than\nthis... *cry*)); + $game_vars{mom} = 3; + quest_desc_add('Mom is distraught that I stole the sword at her behest.', 'mom_quest'); +} elsif ($game_vars{mom} == 3) { + dialog('mom', qq(Hah! Just kidding!\nFernando... listen closely... YOU must put an end\nto that wretched king, King J.J. Akke.)); + quest('mom', 'Old Cheese', qq(Kill the King for mom), "$journal{mom_quest}->[3]\nMom was just joking about being distraught.\n\nI should kill the King for mom.", 'mom_quest'); + $game_vars{mom} = 4; +} elsif ($game_vars{mom} == 4) { + dialog('mom', qq(Remember! Kill that horrible King J.J. Akke!!\nHis guards steal all our cheese!)); +} elsif ($game_vars{mom} == 5) { + dialog('mom', qq(You did it! You killed that horrible king,\nKing J.J Akke! You're the best son any mother could ask for <3\n(you beat the game! talk to mom one last time to end the game))); + $game_vars{mom} = 6; +} elsif ($game_vars{mom} == 6) { + goodend("Thank you for playing my humble game, my 'second' entry for the\nfirst ever (and probably last) #sqt gamejam!\n\nRules were\n1) no game engine\n2) a WEEK\n3) features Fernando ~~,=,^>\n\nI had to do a lot of really nasty hacks just to make this thing... and leave\nnearly location basicly uneventful and without interactions... :'(\n\nBut you know what? It IS my first ever actual 'game' (even if it is 'low \ntech') after all these years of 'wanting to be a game dev.'\nKinda embarrassing actually.\n\nRegardless, I learned a lot such as 'OOP would've been really useful\nhere' and a bit about curses. I do need to learn how to use a 'window'\n(this game uses none besides the one you get with 'initscr()')\n\nANYWAY, thanks for playing nerd\n\nKing J.J Akke out\n\n(space for the game to exit)"); +} +""" +landlord_sword = """ +if (not exists $game_vars{mom}){ + dialog('',qq(Some smelly old sword)); +} +elsif ($game_vars{mom} == 1) { + delete $npcs{S}; + $game_vars{mom} = 2; + addch(2, 54, ' '); # put whitespace over sword + dialog('',qq(You've acquired: "Landlord's sword")); + item(qq(Landlord's Sword)); +} +""" + +bob = """ +dialog('Bob', qq(Hey chief.)); +""" +joe = "dialog('Joe', qq(I like Cheese.) )" diff --git a/assets/outer_king_palace.map b/assets/outer_king_palace.map new file mode 100644 index 0000000..b6448b2 --- /dev/null +++ b/assets/outer_king_palace.map @@ -0,0 +1,40 @@ +################################################################################ +# | | # +# | | # +# | | # +#_____________________| |____________________________# +| | | | +| | | | +| | ___ | | +| | / e \ | | +| | | | | | +| |__________| |____________| | +| | +| | +| | +| | +| | +| | +| | +| | +| | +| __ i i __ | +| |i\ /i\ /i| | +| |i\ /i\ /i| | +| |i\ /i\ /i| | +| |i\ /i\ /i| | +| |i\ /i\ /i| | +|_____________________________|i\ /i\ /i|______________________________________| +# | |i\ /i| | # +# | |i\ /i| | # +# | |i\ /i| | # +# | |i\ /i| | # +# | |i\ /i| | # +# | |i\ /i| | # +#_________________|__________|i\ /i|___________|_________________________# +# # +# # +# # +# # +# # +##################################g############################################# diff --git a/assets/outer_king_palace.toml b/assets/outer_king_palace.toml new file mode 100644 index 0000000..aefab91 --- /dev/null +++ b/assets/outer_king_palace.toml @@ -0,0 +1,41 @@ +interact = [ + [39, 34, "worldmap"], + [ 8, 35, "inner_king_palace"] +] +npc = [ + [18,24, 'g', 'guardl1', 'guardl_no_caffine', 'yellow_black'], + [18,46, 'p', 'guardr1', 'guardr_no_caffine', 'yellow_black'], + [15,54, 'C', 'chainmail', 'none', 'cyan_white'], +] +map_script = """ +if (exists $game_vars{coffee_consumed}) { + $npcs{g}->[2] = 'guardl2'; + $npcs{g}->[3] = 'guardl_yes_caffine'; + $npcs{p}->[2] = 'guardr2'; + $npcs{p}->[3] = 'guardr_yes_caffine'; +} +if (exists $game_vars{chainmail}) { + delete $npcs{C}; +} +""" +[npc_movements] +none = ['w'] +# taking advantage of 'is_move_ok' where if move is NOT ok, nothing happens +guardl_no_caffine = ['r','r','r','r','r','r','r','r','r','d','d','d'] +guardr_no_caffine = ['l','l','l','l','l','l','l','l','l','d','d','d'] +guardl_yes_caffine = ['r','w','r','w','r','w','r','w','r','w','r','w','r','w','r','w','r','w','d','w','d','w','d'] +guardr_yes_caffine = ['l','w','l','w','l','w','l','w','l','w','l','w','l','w','l','w','l','w','d','w','d','w','d'] + +[objects] +worldmap = "teleport('worldmap', 6, 60)" +inner_king_palace = "teleport('inner_king_palace', 38, 39)" +guardl1 = "dialog('Guard (left)', qq(Ha, nice try loser. The Honorable, most amazing\nKing, King J.J. Akke is not seeing visitors at\nthis moment. Now shove off.))" +guardr1 = "dialog('Guard (right)', qq(Just face it pal; unless you somehow go REAL\nfast, you ain't gettin' past us.\nNow go drink some coffee like a good little rat.))" +guardl2 = "dialog('Guard (left)', qq(W-o-a-h w-h-e-r-e d-i-d y-o-u g-e-t\nt-h-a-t s-p-e-e-d-?))" +guardr2 = "dialog('Guard (right)', qq(O-h n-o, w-e c-a-n-'t k-e-e-p u--p\nw-i-t-h y-o-u-!))" +chainmail = """ +item(qq(Chainmail Armor)); dialog('', qq(You've acquired 'Chainmail Armor')); +addch($npcs{C}->[0], $npcs{C}->[1], ' '); # no more chainmail. +delete $npcs{C}; +$game_vars{chainmail} = 1; +""" diff --git a/assets/test.map b/assets/test.map new file mode 100644 index 0000000..2288bec --- /dev/null +++ b/assets/test.map @@ -0,0 +1,40 @@ +################################################################################ +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +################################################################################ diff --git a/assets/witch_village.map b/assets/witch_village.map new file mode 100644 index 0000000..b7da2b8 --- /dev/null +++ b/assets/witch_village.map @@ -0,0 +1,40 @@ +######################################e######################################### +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~i i~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~i-------i i--i~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~/ \~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~/ /\ \~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~/ / \ \~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~/ ###### \~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~i #++++# i~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| #++++# |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| ### ## |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| +----+ |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |c c|V |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| | |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |c c| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| +----+ |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~i i~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~|\ /|~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~||\ /~|~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~|~i i~|~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~----------------------------- ~|~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~|~~~~|~~~~|~~~~|~~~~|~~~|~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~|~~~~|~~~~|~~~~|~~~~|~~~|~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +################################################################################ diff --git a/assets/witch_village.toml b/assets/witch_village.toml new file mode 100644 index 0000000..b50a78a --- /dev/null +++ b/assets/witch_village.toml @@ -0,0 +1,42 @@ +interact = [ + [0, 38, "worldmap"], + [19,30, "coffee_sign"], +] +npc = [ + [16,39, "W", "witch", "random_movement", "red_black"] +] +map_script = """ +if (exists $game_vars{witch} and $game_vars{witch} == 4) { + $npcs{F} = [15, 36, "frog", "random_movement", "green_black"]; + move(15,36); + addstr("F"); +} +""" +[npc_movements] +none=['w'] +[objects] +worldmap = "teleport('worldmap', 35, 39)" +witch = """ +if (not exists $game_vars{witch}) { + $game_vars{witch} = 1; + dialog('Witch Rat', qq(Oh, my precious familiar... Oh how I wish you\ndidn't pass on... If only I had another\njust as powerful as you...)); +} +elsif ($game_vars{witch} == 1 or $game_vars{witch} == 2) { + dialog('Witch Rat', qq(Hmm? You wish to aid me in my time of need?\nWhy thank you Sir Fernando.\nA familiar can be a cat... frog... dog (maybe)...)); + quest('Witch Rat', 'Witch Hut', qq(Aquire familiar), qq(The Witch Rat's old familiar has passed on and she has tasked me with finding\nher another.), 'witch_quest'); + $game_vars{witch} = 2; +} +elsif ($game_vars{witch} == 3) { + dialog('Witch Rat', qq(Is that FROG? For me??\nThank you thank yoy thank you thank you thank you thank you thank you thank you thank you thank you thank you thank you thank you thank you thank you\nFor being my knight, I shall brew you a coffee!)); + item(qq(Cup of Coffee)); + item_remove(qq(Frog)); + $game_vars{witch} = 4; + quest_desc_add(qq(\nThe Witch Rat is happy with the frog I gave her and gave me some coffee in\nturn.), 'witch_quest'); + $npcs{F} = [15, 36, "frog", "random_movement", "green_black"]; +} +elsif ($game_vars{witch} == 4) { + dialog('Witch Rat', qq(Thank you again for bringing me Sir Ribbet.)); +} +""" +coffee_sign = "dialog('Sign', qq(Coffee Trees.\nSelf sufficiency is hard work!))" +frog = "dialog('Sir Ribbet', 'Ribbit');" diff --git a/assets/worldmap.map b/assets/worldmap.map new file mode 100644 index 0000000..adc621a --- /dev/null +++ b/assets/worldmap.map @@ -0,0 +1,40 @@ +################################################################################ +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # +# www ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # +# wwwww ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ttt # +# www ^^^^^^^^^^^^^^^^^^^^^^^^^^^^tttttt KK # +# ^^^^^^^^^^^^^^^^^^^^^^tttttttttt KK # +# ^^^^^^^^^^^^^^^^^^^^^ ttttt gg # +# tt C^^^^^^^^^^^^^^^^ tttttt # +# tt tt ^^^c ^^^^^^^^^ ttttttttt ^^^^^^ # +# ttt ^^^^^^ V ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^c^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ # +# V ^^^^^^^^^^^^^^^ ^^^^^^^^www^^^^^^^^^^^^^^^ # +# tt ^^^^^^^^^^^^^^ ^^^^^^^www^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^C ^^^^^^w^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^^ tt^^^^^w^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^ tttwwwwww^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^ tttwww^^^^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^ tttwwwwt^^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^ tttwwwwttt ^^^^^^^^^^^^^^^ # +# ^^^^^^^^ tttttttt ^^^^^^^^^^^^^^^ # +# ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^# +# ^^^^^^ ^^^^^^^^^^^^tt^^^^^^^^^# +# ^^^ ^^^^^^^^^^^^^ # +# ^ ^^^^^^^ ^^ ^^ ^^# +# ^^^^^ ^^^^^^^# +# ^^^ ^^ # +# ttttt # +# ttttttttttt # +# ^^ttttttttttt^^^^^ # +# V ^^^^ttttttttt^^^^^^^ ^^^^^^^^^^ # +# ^^^^^^tttttt^^t^^^^^ ^^^^wwwww^^^^ # +# ^^^^^^^^tttt^^^^^^^ ^^^^wwwwww^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^^^^t^^^ ^^^^^wwwwwww^^^^^^^^^^^^^^^^^ # +# ^^^^^^^^^^^^^^^^^^^^^^^ ^^^wwwwwwwwwwwwwwwwwww^^^^^^^^^^^^^^^^^# +# ^^^^^^^^^^^^^^^^^^^^^^^wVwwwwwwwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# +# ^^^^^^^^^^^^^^^^^^^^^^^wwwwwwwwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# +# ^^^^^^^^^^^^^^^^^^^^^^^wwwwwwwwww^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# +################################################################################ diff --git a/assets/worldmap.toml b/assets/worldmap.toml new file mode 100644 index 0000000..cfe92bc --- /dev/null +++ b/assets/worldmap.toml @@ -0,0 +1,31 @@ +interact = [ + [ 7, 52, "gate1" ], + [ 7, 53, "gate2" ], + [ 13, 14, "old_cheese" ], + [ 8, 23, "cave_w" ], + [ 15, 33, "cave_e" ], + [ 31, 9, "new_cheese" ], + [ 36, 39, "witch_village" ], + [ 6, 61, "king" ], + [ 6, 62, "king" ], + [ 5, 61, "king" ], + [ 6, 62, "king" ], +] +npc = [ + [ 25, 13, "r", "mr_rat", "none", "white_black" ], +] +map_script = """ +""" +[npc_movements] +none = ['w'] + +[objects] +gate1 = " teleport('gate', 19, 1) " +gate2 = " teleport('gate', 18, 78) " +"mr_rat" = ' dialog("Mr. Rat" ,"Hello there young whipper snapper. Off to kill \nthe King I see.\nWell, I wish you luck!" );' +old_cheese = "teleport('old_cheese', 38, 37)" +cave_w = "teleport('cave_w', 2, 16)" +cave_e = "teleport('cave_e', 23, 78)" +new_cheese = "teleport('new_cheese', 1, 36)" +witch_village = "teleport('witch_village', 1, 38)" +king = "teleport('outer_king_palace', 38, 34)" diff --git a/game.pl b/game.pl new file mode 100755 index 0000000..4b16a28 --- /dev/null +++ b/game.pl @@ -0,0 +1,906 @@ +#!/usr/bin/env perl +# This 'game.pl' is licensed under the GPL3 + +use strict; +use warnings; +use 5.010; + +# wants, desires: +# utf8 compatible -> would require an array of the map rather +# than relying on curses knowledge of what is on the screen +# as utf8 'chars' are like 3+ bytes +# (which would make fixing the resize bug easier) + +use Curses; +use TOML; +use File::Slurper qw(read_text write_text); + +my $save_file = './jjakkes-fernando-game.save.toml'; + +initscr(); +cbreak(); +noecho(); +keypad(stdscr, 1); +start_color(); + +my %colors; +# init color pairs +{ + my %term_colors = ( + black => COLOR_BLACK, + blue => COLOR_BLUE, + cyan => COLOR_CYAN, + green => COLOR_GREEN, + magenta => COLOR_MAGENTA, + red => COLOR_RED, + white => COLOR_WHITE, + yellow => COLOR_YELLOW + ); + my $i = 1; + for my $fore (sort keys %term_colors) { + for my $back (sort keys %term_colors) { + init_pair($i, $term_colors{$fore}, $term_colors{$back}); + $colors{$fore . '_' . $back} = $i; + $i++; + } + } +} + +attron( COLOR_PAIR( $colors{white_black} ) ); + +my $size_changed; +$SIG{'WINCH'} = sub { $size_changed = 1; }; + +my $max_y = 40; # Curses inits at top left, down is y+1, right is x+1 ( y,x NOT x,y ) +my $max_x = 80; + + +my $grass = ' '; +my $empty = '.'; +my $fernando = 'f'; +my $f_y = 25; +my $f_x = 15; +my $dialog_mode = 0; +my $menu_mode = 0; +my $battle_mode = 0; +my $hp = 100; +my $max_hp = 100; +my $atk = 5; +my $def = 5; +my $lvl = 1; +my $class = 'rat'; +my $hands = 'lil grabbies'; +my $armor = 'fur'; + +my $curr_map = 'old_cheese'; +my $scripts; +my %npcs; # coming in ( 25, 23, 'r', "mr. rat", $movement_pattern, color ); actual hash %{ y, x, $object, $movement_pattern, color } +my %npc_movements; # %{ 'bob1' => 0 } # 0 == array index and 'bob1' refers to $scripts->{npc_movements}->{bob1} +my %game_vars; +my %game_items = ( + 'lil grabbies' => ['hands', 'lil grabbies (not paws) that CAN grab and grip!', 5], + 'fur' => ['armor', 'Fernando\'s coat!',5], + 'Landlord\'s Sword' => ['hands', qq(Old Cheese Landlord's smelly sword.), 10], + 'Cup of Coffee' => ['potion', 'A cup of perfectly blended and brewed coffee made by the Witch Rat.', + sub { + $game_vars{coffee_consumed} = 1; + dialog('Fernando', qq(Mmm... coffee...)); + item_remove('Cup of Coffee'); + status_add('Caffinated'); + } + ], + 'hp potion' => ['potion', 'this potion heals Fernando to full health! great stuff!', + sub { $hp = $max_hp; item_remove('hp potion'); } + ], + 'Cool Sword' => ['hands', qq(This sword radiates coolness.), 15], + 'Chainmail Armor' => ['armor', qq(Naturally, rats can wear chainmail.\nRead a book or something.), 7], + Shrubbery => ['key item', qq(Some bush that Rats that say 'Ni!' want.)], + Frog => ['key item', qq(Frog that probably is good enough to be a familiar. Maybe.)], +); +my @inventory = ('lil grabbies', 'fur'); +my @status = (); +my %journal; +my %battlers = ( + 'King J.J. Akke' => { + hp => 100, + hands => 'big grabbies', + armor => 'regal fur', + atk => 13, + def => 6, + art => '<^===,=,~~', + }, +); +my %battle_sess; + +sub draw_map { + my $map = shift; + $map = "$map.map"; + open my $fh, '<', './assets/'. $map + or endwin() and die "$map not accessible"; + move(0,0); + while (<$fh>) { + printw($_); + } + move($f_y, $f_x); +} + +sub is_move_ok { + my ($y, $x) = @_; + move($y,$x); + my $testchar = substr instring(), 0, 1; + my %ok_movement = ( + $grass => 1, + $empty => 1, + ); + exists $ok_movement{$testchar} ? return 1 : return 0; +}; + +sub get_scripts { + my $file = shift; + $file = "$file.toml"; + $scripts = from_toml(read_text('./assets/' . $file)); + create_npcs($scripts); +}; + +sub create_npcs { + undef %npcs; + undef %npc_movements; + my $scripts = shift; + # ( 25, 23, 'r', "mr. rat", $behavior, color ); + for my $npc (@{ $scripts->{npc} }) { + # %{ 'r' => [25, 23, "mr. rat", $behavior, color] } + $npcs{ $npc->[2] } = [$npc->[0], $npc->[1], $npc->[3], $npc->[4], $npc->[5]]; + }; +}; + +sub draw_npcs { + for my $npc (keys %npcs ) { + move($npcs{$npc}->[0], $npcs{$npc}->[1]); + if ($npcs{$npc}->[4] ) { + attroff( COLOR_PAIR( $colors{white_black} )); + attron( COLOR_PAIR( $colors{ $npcs{$npc}->[4] } )); + addstr($npc); + attroff( COLOR_PAIR( $colors{ $npcs{$npc}->[4] } )); + attron( COLOR_PAIR( $colors{white_black} )); + } + else { + addstr($npc); + } + } + refresh(); +}; + +# TODO BUGFIX where NPC/Player can collide with one another and 'stack' +# a fix would be to check each npc position after thier movement +# I don't feel like doing this rn. +sub engage_npcs { + for my $npc (keys %npcs) { + if($npcs{$npc}->[3]) { + my $behavior = $npcs{$npc}->[3]; + if ($behavior eq 'random_movement') { + random_movement_npc($npc); + } + elsif (exists $npc_movements{$behavior}) { + custom_movement_npc($npc, $behavior); + } + elsif (not exists $npc_movements{$behavior}) { + $npc_movements{$behavior} = 0; + custom_movement_npc($npc, $behavior); + } + } + } + draw_npcs(); +}; + +sub random_movement_npc { + my $npc = shift; + my $move = int rand 8; + if ($move == 1) { # move up + if (is_move_ok($npcs{$npc}->[0] - 1, $npcs{$npc}->[1])) { + addch($npcs{$npc}->[0], $npcs{$npc}->[1], ' '); + $npcs{$npc}->[0] -= 1 + } + } elsif ($move == 2) { # move down + if (is_move_ok($npcs{$npc}->[0] + 1, $npcs{$npc}->[1])) { + addch($npcs{$npc}->[0], $npcs{$npc}->[1], ' '); + $npcs{$npc}->[0] += 1 + } + } elsif ($move == 3) { # move right + if (is_move_ok($npcs{$npc}->[0], $npcs{$npc}->[1] + 1)) { + addch($npcs{$npc}->[0], $npcs{$npc}->[1], ' '); + $npcs{$npc}->[1] += 1 + } + } elsif ($move == 4) { # move left + if (is_move_ok($npcs{$npc}->[0], $npcs{$npc}->[1] - 1)) { + addch($npcs{$npc}->[0], $npcs{$npc}->[1], ' '); + $npcs{$npc}->[1] -= 1; + } + } + else { }; # don't move +} + +sub custom_movement_npc { + my ($npc, $behavior) = @_; + my %movement_cmds = ( + l => sub { my $npc = shift; return $npc->[0], $npc->[1] - 1; }, + r => sub { my $npc = shift; return $npc->[0], $npc->[1] + 1; }, + u => sub { my $npc = shift; return $npc->[0] - 1, $npc->[1]; }, + d => sub { my $npc = shift; return $npc->[0] + 1, $npc->[1]; }, + w => sub { my $npc = shift; return -1, -1; }, + ); + + # enjoy, future me. :^) + # ... okok just remember: + # %npcs, %npc_movements, and $scripts are global. $scripts is the TOML file + # $behavior is a key for a value (char array) that $scripts->{npc_movements} has + # %npc_movements: %{ $behavior => 0, ... } where 0 is array index + # %movement_cmds: keys are chars that the array can return; values are code blocks + my ($y, $x) = $movement_cmds{ + $scripts->{npc_movements}->{$behavior}->[ $npc_movements{$behavior} ] + }( $npcs{$npc} ); + + if ($y == -1 and $x == -1) { + my $new_index = ($npc_movements{$behavior} + 1) % scalar @{ $scripts->{npc_movements}->{$behavior} }; + $npc_movements{$behavior} = $new_index; + return; + } + + if (is_move_ok($y, $x)) { + addch($npcs{$npc}->[0], $npcs{$npc}->[1], ' '); # put whitespace on char + ($npcs{$npc}->[0], $npcs{$npc}->[1]) = ($y, $x); # new coords, so it can be drawn + my $new_index = ($npc_movements{$behavior} + 1) % scalar @{ $scripts->{npc_movements}->{$behavior} }; + $npc_movements{$behavior} = $new_index; + } +} + +sub run_map_script { + ## no critic (eval) + if ($scripts->{map_script}) { + eval $scripts->{map_script}; + if ($@) { + warn $@; + } + } +}; + +sub interact { + # I'll just use 'dumb' logic here. + + my ($y, $x) = @_; + # get list of interactables + my $object; + for my $interact_list ( @{ $scripts->{interact} } ) { + if ($interact_list->[0] == $y and $interact_list->[1] == $x + 1) { # to the right + $object = $interact_list->[2]; + goto INTERACT_GOTO; + } elsif ($interact_list->[0] == $y and $interact_list->[1] == $x - 1) { # to the left + $object = $interact_list->[2]; + goto INTERACT_GOTO; + } elsif (($interact_list->[0] == $y + 1) and $interact_list->[1] == $x) { # to the down + $object = $interact_list->[2]; + goto INTERACT_GOTO; + } elsif (($interact_list->[0] == $y - 1) and $interact_list->[1] == $x) { # to the up + $object = $interact_list->[2]; + goto INTERACT_GOTO; + } elsif ($interact_list->[0] == $y and $interact_list->[1] == $x) { # right on top of it + $object = $interact_list->[2]; + goto INTERACT_GOTO; + } + } + # check each and all npcs + for my $npc (keys %npcs) { + if ($npcs{$npc}->[0] == $y and $npcs{$npc}->[1] == $x + 1) { # to the right + $object = $npcs{$npc}->[2]; + goto INTERACT_GOTO; + } elsif ($npcs{$npc}->[0] == $y and $npcs{$npc}->[1] == $x - 1) { # to the left + $object = $npcs{$npc}->[2]; + goto INTERACT_GOTO; + } elsif (($npcs{$npc}->[0] == $y + 1) and $npcs{$npc}->[1] == $x) { # to the down + $object = $npcs{$npc}->[2]; + goto INTERACT_GOTO; + } elsif (($npcs{$npc}->[0] == $y - 1) and $npcs{$npc}->[1] == $x) { # to the up + $object = $npcs{$npc}->[2]; + goto INTERACT_GOTO; + } elsif ($npcs{$npc}->[0] == $y and $npcs{$npc}->[1] == $x) { # right on top of it + $object = $npcs{$npc}->[2]; + goto INTERACT_GOTO; + } + } + + INTERACT_GOTO: + if ($object) { + # hopefully no one wrote anything naughty here + ## no critic (eval) + eval $scripts->{objects}->{$object}; + if ($@) { + warn $@; + } + refresh(); + } +} + +sub dialog { + my ($name, $dialog) = @_; + $dialog_mode = 1; + dialog_box(); + dialog_wrap($name, $dialog); + + move($f_y, $f_x); +}; + +sub dialog_box { + my ($name) = @_; + hline(30, 14, ACS_HLINE, 50); + for (31..34) { + hline($_, 14, ' ', 50); + } + hline(35, 14, ACS_HLINE, 50); + vline(30,14, ACS_VLINE, 6); + vline(30,64, ACS_VLINE, 6); + addch(30,14, ACS_ULCORNER); # top left + addch(30,64, ACS_URCORNER); # top right + addch(35,14, ACS_LLCORNER); # bottom left + addch(35,64, ACS_LRCORNER); # bottom right + move(35,40); + printw("SPACE TO CONTINUE"); +} + +sub dialog_wrap { + my ($name, $dialog) = @_; + move(31,15); + printw($name); + # 31 -> 34 on y axis (so 3 lines of dialog) + # 14 -> 64 on x axis (but really 15 to 63 so about 48 chars per line) + my @lines = split "\n", $dialog; + my $y = 32; + for (@lines) { + move($y, 15); + printw($_); + $y++; + } +} + +sub teleport { + my ($map, $y, $x) = @_; + $curr_map = $map; + endwin(); + get_scripts($map); + draw_map($map); + run_map_script(); + draw_npcs(); + $f_y = $y; + $f_x = $x; + move($f_x, $f_x); + refresh(); +} + +sub game_loop { + my ($new_game) = @_; + get_scripts($curr_map) if $new_game; + draw_map($curr_map); + draw_npcs(); + run_map_script(); + + # main gameplay loop + my $input; + while () { + if ($size_changed) { + endwin(); + refresh(); + draw_map($curr_map); + } + + if ($dialog_mode) { + $input = getchar(); + if ($input eq ' ' or $input eq KEY_ENTER) { + endwin(); + draw_map($curr_map); + draw_npcs(); + addch($f_y, $f_x, $fernando); + move($f_y, $f_x); + refresh(); + $dialog_mode = 0; + } + next; + } + if ($menu_mode) { + $input = getchar(); + if ($input eq 's') { + my $save = { + hp => $hp, + max_hp => $max_hp, + atk => $atk, + def => $def, + lvl => $lvl, + class => $class, + f_y => $f_y, + f_x => $f_x, + hands => $hands, + armor => $armor, + inventory => \@inventory, + status => \@status, + curr_map => $curr_map, + scripts => $scripts, # this will be awkward I suspect + }; + %journal and $save->{journal} = \%journal; + %game_vars and $save->{game_vars} = \%game_vars; + %npc_movements and $save->{npc_movements} = \%npc_movements; + %npcs and $save->{npcs} = \%npcs; + + write_text($save_file, to_toml($save)); + move(1,1); + printw("saved"); + } elsif ($input eq 'i') { + inventory_menu(); + } elsif ($input eq 'j') { + journal_menu(); + } elsif ($input eq '~') { + # quit game + endwin(); + exit 0; + } elsif ($input eq 'q') { + $menu_mode = 0; + draw_map($curr_map); + draw_npcs(); + } + next; + } + + move($f_y, $f_x); + addstr($fernando); + move($f_y, $f_x); + refresh(); + $input = getchar(); + if ($input eq 'w' or $input eq KEY_UP) { + if ($f_y > 0 and is_move_ok($f_y-1, $f_x)) { + addch($f_y, $f_x, $empty); + $f_y = $f_y - 1; + } + } elsif ($input eq 's' or $input eq KEY_DOWN) { + if ($f_y < $max_y - 1 and is_move_ok($f_y+1, $f_x)) { + addch($f_y, $f_x, $empty); + $f_y = $f_y + 1; + } + } elsif ($input eq 'a' or $input eq KEY_LEFT) { + if ($f_x > 0 and is_move_ok($f_y, $f_x-1)) { + addch($f_y, $f_x, $empty); + $f_x = $f_x - 1; + } + } elsif ($input eq 'd' or $input eq KEY_RIGHT) { + if ($f_x < $max_x - 1 and is_move_ok($f_y, $f_x+1)) { + addch($f_y, $f_x, $empty); + $f_x = $f_x + 1; + } + } elsif ($input eq ' ' or $input eq KEY_ENTER) { + interact($f_y, $f_x); + next; + } elsif ($input eq 'm') { + $menu_mode = 1; + start_menu(); + next; + } + else { }; + engage_npcs(); + move($f_y, $f_x); + } +} + +# TODO +# curses website advises against this sort of thing but I don't feel like I +# have enough time to learn the proper way with windows and such +sub menu_box { + my ($starty, $startx, $endy, $endx) = (0,0,39,80); # roughly the size of the game + + hline($starty, $startx, ACS_HLINE, $endx); + for ($starty+1..$endy) { + hline($_, $startx+1, ' ', $endx-1); + } + hline($endy, $startx, ACS_HLINE, $endx); + vline($starty,$startx, ACS_VLINE, $endy); + vline($starty,$endx, ACS_VLINE, $endy); + addch($starty,$startx, ACS_ULCORNER); # top left + addch($starty,$endx, ACS_URCORNER); # top right + addch($endy,$startx, ACS_LLCORNER); # bottom left + addch($endy,$endx, ACS_LRCORNER); # bottom right +} + +sub start_menu { + menu_box(); + my $r = 2; # row + my $lc = 6; # left column + my $rc = 50; # right column + move(++$r, $lc); + printw("Game Menu"); + move(++$r, $lc); + printw("(s)ave"); + move(++$r, $lc); + printw("(i)nventory"); + move(++$r, $lc); + printw("(j)ournal"); + move(++$r, $lc); + printw("~ to quit game"); + move(++$r, $lc); + printw("q to leave menu"); + + $r = 2; + move(++$r, $rc); + printw("Fernando"); + move(++$r, $rc); + printw("~~,=,^> $hp/$max_hp"); + move(++$r, $rc); + printw("class: $class"); + move(++$r, $rc); + printw("atk ($hands) $atk"); + move(++$r, $rc); + printw("def ($armor) $def"); + move(++$r, $rc); + if (scalar @status == 0) { + printw("Statuses: none"); + } else { + printw("Statuses: "); + for my $status (@status) { + printw($status); + } + } +} + +sub journal_menu { + while () { + menu_box(); + my $r = 2; # row + move(1,1); + printw("Journal: q -> back, number -> more details"); # atm this means no more than 10 quests + my $i = 0; + for my $entry (sort keys %journal) { + move(++$r,6); + printw("$i> $journal{$entry}->[0]: $journal{$entry}->[2]"); + $i++; + } + + my $input = getchar(); + if ($input eq 'q') { + start_menu(); + last; + } + else { + # single digit + if ($input =~ m/^\d$/ and $input <= (scalar keys %journal) - 1 ) { + my @ids = sort keys %journal; + journal_entry_menu( $ids[$input] ); + } + else { + next; + } + } + } +} + +sub journal_entry_menu { + my $id = shift; + menu_box(); + my $r = 2; # row + move(1, 1); + printw("q -> back"); + move(++$r, 1); + printw("$journal{$id}->[0]: $journal{$id}->[2] (given at $journal{$id}->[1])"); + $r++; + for my $line (split "\n", $journal{$id}->[3]) { + move(++$r, 1); + printw($line); + } + + while () { + my $je_input = getchar(); + if ($je_input eq 'q') { + last; + } + } + +} + +sub quest { + my ($quest_giver, $where_given, $title, $description, $id) = @_; + $journal{$id} = [$quest_giver, $where_given, $title, $description]; +} + +sub quest_desc_add { + my ($desc, $id) = @_; + $journal{$id}->[3] .= "\n$desc"; +} + +sub main_menu { + # should contain basic instructions + # eg. arrow keys to move, space to interact, m for in game menu + # if saved game detected: { + # (l)oad + # } + # (n)ew + # load should read in a TOML file and replace the current global values + open my $fh, '<:utf8','./assets/main_menu' + or endwin() and die "$!"; + move(0,0); + while (<$fh>) { + printw($_); + } + close $fh; + my $script = from_toml(read_text('./assets/main_menu.toml')); + ## no critic (eval) + eval $script->{do_this}; + while () { + my $input = getchar(); + if ($input eq 'n') { + game_loop(1); + last; + } elsif ($input eq 'l') { + my $save = from_toml(read_text($save_file)); + $f_y = $save->{f_y}; + $f_x = $save->{f_x}; + $hp = $save->{hp}; + $max_hp = $save->{max_hp}; + $lvl = $save->{lvl}; + $atk = $save->{atk}; + $def = $save->{def}; + $class = $save->{class}; + $hands = $save->{hands}; + $armor = $save->{armor}; + $curr_map = $save->{curr_map}; + $scripts = $save->{scripts}; + %npcs = %{ $save->{npcs} } if $save->{npcs}; + %npc_movements = %{ $save->{npc_movements} } if $save->{npc_movements}; + %game_vars = %{ $save->{game_vars} } if $save->{game_vars}; + %journal = %{ $save->{journal} } if $save->{journal}; + @inventory = @{ $save->{inventory} }; + $curr_map = $save->{curr_map}; + undef $save; + game_loop(); + last; + } + elsif ($input eq 'b') { + $hands = qq(Cool Sword); + $atk = 15; + item('hp potion'); + battle('King J.J. Akke'); + } + } +} + +sub inventory_menu { + while() { + menu_box(); + my $r = 2; + move(1,1); + printw("Inventory: q -> back, numb -> more details"); # atm this means no more than 10 items + my $i = 0; + for my $item (@inventory) { + move(++$r, 6); + printw("$i> $item"); + $i++; + } + + my $input = getchar(); + if ($input eq 'q') { + start_menu(); + last; + } + else { + if ($input =~ m/^\d$/ and $input <= (scalar @inventory) - 1 ) { + inventory_item_menu( $inventory[$input] ); + } + else { + next; + } + } + } +} + +sub inventory_item_menu { + my ($id) = @_; + menu_box(); + my $r = 2; #row + move(1,1); + printw('Inventory Item: q -> back, e -> equip/use '); + if ($hands eq $id or $armor eq $id) { + printw("(equiped)"); + } + + my ($type, $desc, $behavior) = @{ $game_items{$id} }; + move(++$r, 2); + printw("$id: $type "); + if ($type eq 'hands') { + printw("atk $behavior"); + } elsif ($type eq 'armor') { + printw("def $behavior"); + } elsif ($type eq 'potion') { + printw("(consumable)"); + } elsif ($type eq 'key item') { + printw("(key item -> cannot be used)"); + } + for my $line ( split "\n", $desc ) { + move(++$r, 2); + printw($line); + } + + while () { + my $input = getchar(); + if ($input eq 'q') { + last; + } + elsif ($input eq 'e') { + if ($type eq 'hands') { + $hands = $id; + $atk = $behavior; + # not happy with this below but it'll do for now + move(1,1); + printw('Inventory Item: q -> back, e -> equip (equiped)'); + } elsif ($type eq 'armor') { + $armor = $id; + $def = $behavior; + move(1,1); + printw('Inventory Item: q -> back, e -> equip (equiped)'); + } elsif ($type eq 'potion') { + ## no critic (eval) + eval &$behavior; + # disgusting hack + if ($dialog_mode) { + my $d_input = getchar(); + if ($d_input eq ' ') { + $dialog_mode = 0; + last; + } + } + } elsif ($type eq 'key item') { + ; # nothing + } + } + } +} + +sub item { + my $item = shift; + exists $game_items{$item} + and push (@inventory, $item) + or dialog('system', "Sorry player, this item\n`$item'\ndoesn't exist in the game system."); +} + +sub item_remove { + my $item = shift; + exists $game_items{$item} + or dialog('system', "Sorry player, this item \n`$item`\n cannot be remove as it doesn't exist in game system.") + and return; + + my $i = 0; + for my $test (@inventory) { + if ($test eq $item) { + splice @inventory, $i, 1; + } + $i++; + } +} + +sub item_has { + my $item = shift; + my $i = 0; + for my $test (@inventory) { + if ($test eq $item) { + return 1; + } + $i++; + } + return 0; +} + + +sub status_add { + my $status = shift; + push @status, $status; +} + +sub battle { + my $battler = shift; + exists $battlers{$battler} + or return; + undef %battle_sess; + $battle_sess{hp} = $battlers{$battler}{hp}; + $battle_sess{battler} = $battler; + $battle_sess{atk} = $battlers{$battler}{atk}; + $battle_sess{hands} = $battlers{$battler}{hands}; + $battle_sess{def} = $battlers{$battler}{def}; + $battle_sess{armor} = $battlers{$battler}{armor}; + $battle_sess{art} = $battlers{$battler}{art}; + $battle_mode = 1; + while () { + battle_screen_update(); + if ($hp < 0) { + dialog('', qq(You died.\nGame over.)); + refresh(); + sleep 5; + endwin(); + exit; + } + elsif ($battle_sess{hp} < 0) { + last; + } + + my $input = getchar(); + if ($input eq 'a') { + my $f_dmg = $atk - $battle_sess{def}; + my $b_dmg = $battle_sess{atk} - $def; + $battle_sess{hp} -= $f_dmg; + $hp -= $b_dmg; + $battle_sess{last_event} = "Fernando dealt $f_dmg damage. $battler dealt $b_dmg."; + } + elsif ($input eq 'h') { + if (item_has('hp potion')) { + $hp = $max_hp; + my $b_dmg = $battle_sess{atk} - $def; + $hp -= $b_dmg; + $battle_sess{last_event} = "Fernando healed up!. $battler dealt $b_dmg."; + item_remove('hp potion'); + } + else { + $battle_sess{last_event} = "No hp potions left."; + } + } + } + draw_map($curr_map); + draw_npcs(); + return 1; +} + +sub battle_screen_update { + menu_box(); + my $r = 2; #row + my $lc = 6; # left column + my $rc = 46; # right column + move(++$r,$lc); + printw("Fernando"); + move(++$r,$lc); + printw('~~,=,^>'); + move(++$r,$lc); + printw("hp: $hp"); + move(++$r,$lc); + printw("atk: $hands ($atk)"); + move(++$r,$lc); + printw("def: $armor ($def)"); + + $r = 2; + move(++$r,$rc); + printw($battle_sess{battler}); + move(++$r,$rc); + printw($battle_sess{art}); + move(++$r,$rc); + printw("hp: $battle_sess{hp}"); + move(++$r,$rc); + printw("atk: $battle_sess{hands} ($battle_sess{atk})"); + move(++$r,$rc); + printw("def: $battle_sess{armor} ($battle_sess{def})"); + + $r = 10; + move(++$r,$lc); + printw("a -> attack, h -> use potion"); + move(++$r,$lc); + move(++$r,$lc); + printw($battle_sess{last_event}) if $battle_sess{last_event}; +} + +sub goodend { + my ($lines) = @_; + menu_box(); + my $r = 2; #row + my $lc = 2; #left column + for my $line (split "\n", $lines) { + move(++$r,$lc); + printw($line); + } + while () { + my $input = getchar(); + if ($input == ' ') { + endwin(); + exit; + } + } +} + +main_menu(); + +endwin();