C#: Text Adventure
Xythan's Castle is a text adventure written in C#. Players journey through a dark wizard's castle to save Alyssa, their friend and lover. The "Main Quest" has multiple solutions for different player types, and gives the option to explore and learn the deeper lore behind the world, or immediately jump to important interactions.
Drug a hapless guard to slip by, or impersonate him yourself. Strong-arm the gatekeeper to let you past, or slip him a bribe to buy his good will. Steal magical artifacts to become a wizard, or sound the alarm to spread enemy forces thin. These are but a few of the many possibilities in Xythan's Castle.
Drug a hapless guard to slip by, or impersonate him yourself. Strong-arm the gatekeeper to let you past, or slip him a bribe to buy his good will. Steal magical artifacts to become a wizard, or sound the alarm to spread enemy forces thin. These are but a few of the many possibilities in Xythan's Castle.
Design Highlights
- Dynamic NPC states: NPCs react to your attire, knowledge, items, and past interactions. Some NPCs allow chance-based bribery or intimidation options, letting players balance risk vs. rewards
- Color coded text generator: Gives immediate feedback about interactive objects and how they can be used
- Modular Room Constructors: Allows for a scalable game and easy room updates
- Modular Text Parser and Input Handler: Allows for easily expanded exploration, interaction, and items
Dynamic NPC States
The Gatekeeper: Dynamic States and Interactions
The player's first NPC encounter is with the Gatekeeper outside the castle. This Gatekeeper is apparently quite the scholar, and happy to educate inquiring players about the castle's history and current affairs, giving away critical information that can help the player trick his way into the castle. Less patient players can resort to intimidation or bribery, but this locks out certain options for completing the main quest. Depending how players interact with him, he may change states, altering his attitude and changing the player's options.
//new function - talk to gatekeeper static void Talk_Gatekeeper(out bool gate_locked, string Disposition) { string input; int selection; switch (Disposition) //potential states for Gatekeeper { case "cowering": Write_Text("The GATEKEEPER shies away at your approach."); Console.ReadKey(); Display_Room(); gate_locked = false; return; case "bribed": Write_Text("GATEKEEPER: \"Go on inside! I can't be seen talking with you or the guards will get suspicious."); Console.ReadKey(); Display_Room(); gate_locked = false; return; case "gone": Write_Text("The GATEKEEPER has left to go to the tavern."); Console.ReadKey(); Display_Room(); gate_locked = false; return; case "haughty": Console.Clear(); Write_Text("The GATEKEEPER casts you an insolent gaze as you approach."); Write_Text("GATEKEEPER: \"What do you want?\""); break; case "acquainted": Console.Clear(); Write_Text("The GATEKEEPER stands shivering in the cold, his cloak pulled close."); Write_Text("GATEKEEPER: \"You again? How can I help you?\""); break; case "first": { Outside.Description = "You stand outside a towering black CASTLE, the wind chill on your face. The GATE EAST to the COURTYARD stands shut, guarded by a GATEKEEPER."; Current_Room.Description = Outside.Description; Gatekeeper_Disposition = "acquainted"; Console.Clear(); Write_Text("The GATEKEEPER pulls his cloak close, huddling against the cold. Noticing your approach, he squints up at you from beneath a dirty brown hood."); Write_Text("GATEKEEPER: \"Few come by this way these days. What is your business here, traveler?\""); break; } }
Intimidation and Bribery Systems
- Intimidation is a one-time chance to cow the guard into submission. Depending on the player's information, his intimidation may be more successful. If the intimidation fails, the Gatekeeper becomes hostile and refuses to share any more information, forcing the player to bribe his way past.
- Bribery has a % chance to be successful depending on how much gold the player offers, bounded by upper and lower limits for 100% success or failure, respectively. The player may attempt a new bribe if he fails, but the threshold for success raises each time, up to a hard cap.
case "4": //intimidate int chance = RNG.Next(1, 4 + 1); // 1/4 chance if (chance == 3) //intimidation successful { Console.Clear(); Write_Text("[Intimidate Success!] The GATEKEEPER cowers in fear."); Write_Text("GATEKEEPER: \"Please don't hurt me! Here, I'll open the gate for you. Just don't tell them I let you in!\""); Write_Text("The GATEKEEPER quickly unlocks the GATE and dashes back to his post, keeping a good distance away from you."); Gatekeeper_Disposition = "cowering"; gate_locked = false; Outside.Description = "You stand outside a towering black CASTLE, the wind chill on your face. The GATE EAST to the COURTYARD is now open, and the GATEKEEPER shies away at your approach."; Outside.Objects["COURTYARD"] = "The GATE stands open. You can now go EAST to COURTYARD."; Outside.Objects["GATE"] = "The GATE is unlocked. You may now pass through."; Outside.NPCs["GATEKEEPER"] = "The GATEKEEPER cowers in fear. He refuses to come near enough to talk."; Console.ReadKey(); Current_Room.Name = "OUTSIDE"; Switch_Room(Current_Room.Name); Build_Room(); Display_Room(); return; } else // intimidation failed { Console.Clear(); Write_Text("[Intimidate Failure!] The GATEKEEPER snorts in derision. "); Write_Text("GATEKEEPER: \"Oh really? You're unarmed and I have a CASTLE full of guards at my back. I'd think twice about that choice if I were you.\""); Gatekeeper_Disposition = "haughty"; gate_locked = true; Outside.NPCs["GATEKEEPER"] = "The GATEKEEPER scowls at you in contempt."; Console.ReadKey(); Current_Room.Name = "OUTSIDE"; Switch_Room(Current_Room.Name); Build_Room(); Talk_Gatekeeper(out gate_locked, Gatekeeper_Disposition); return; } case "3": //see xythan do { Console.Clear(); Write_Text("The GATEKEEPER narrows his eyes."); Write_Text("GATEKEEPER: \"What's your business with XYTHAN?\""); Write_Text("1) \"Nevermind.\""); Write_Text("2) [Bribe] \"It's a private matter. Here are some coins for your trouble.\""); if (General_Objects.Keys.Contains("ALYSSA") && General_Objects.Keys.Contains("RECRUIT")) { Write_Text("3) [Lie] \"I'm the new RECRUIT, reporting for duty.\""); } if (General_Objects.Keys.Contains("XYTHAN") && General_Objects.Keys.Contains("CERULEAN ESSENCE") && General_Objects["XYTHAN"].Contains("From GATEKEEPER: 120") && General_Objects["CERULEAN ESSENCE"].Contains("From GATEKEEPER:")) { Write_Text("4) [Lie] \"Ah, I see you don't recognize me. It has been many decades since I aided XYTHAN in taking this CASTLE. I'm here to share my new discoveries about CERULEAN ESSENCE with your lord.\""); } // need CERULEAN ESSENCE LONG VERSION, and XYTHAN 120 YEARS do { Console.Write("> "); input = Console.ReadLine().Trim(); switch (input) { case "1": //nevermind gate_locked = true; Talk_Gatekeeper(out gate_locked, Gatekeeper_Disposition); return; case "2": //Offer Bribe Console.Clear(); Write_Text("Current GOLD COINS: " + Player_Coins); Console.WriteLine("Note: higher bribes have a greater chance of success."); do { Console.Write("How many GOLD COINS would you like to offer? "); input = Console.ReadLine().Trim(); if (!int.TryParse(input, out selection) || selection < 1 || selection > Player_Coins) { Write_Text("You must enter an amount between 1 and your total GOLD COINS."); continue; } else { Write_Text("You offer " + selection + " GOLD COINS."); Console.ReadKey(); if (selection <= Gatekeeper_Min_Bribe) //does not meet past bribe { Console.Clear(); Write_Text("GATEKEEPER: \"Pah! You'll have to do better than that!\""); Gatekeeper_Bribe_Penalty += 5; } else { int success = RNG.Next(10 + Gatekeeper_Bribe_Penalty, 30 + 1 + Gatekeeper_Bribe_Penalty); if (selection >= success || selection >= 90) //bribe successful { Console.Clear(); Write_Text("GATEKEEPER: \"Thank'ee kindly sir. I'll open the GATE for you. Just don't let the guards see us talking. Mum's the word!\""); Gatekeeper_Disposition = "bribed"; gate_locked = false; Outside.Description = "You stand outside a towering black CASTLE, the wind chill on your face. The GATE EAST to the COURTYARD is now open, and the GATEKEEPER secretively counts his GOLD COINS while avoiding your gaze."; Outside.Objects["COURTYARD"] = "The GATE stands open. You can now go EAST to COURTYARD."; Outside.Objects["GATE"] = "The GATE is unlocked. You may now pass through."; Outside.NPCs["GATEKEEPER"] = "The GATEKEEPER smiles contentedly, counting his GOLD COINS. He hums a short ditty to himself and deliberately averts his eyes from you, trying not to draw attention to himself."; Player_Coins -= selection; if (Player_Coins == 0) { Inventory.Remove("GOLD COINS"); Compound_Terms.Remove("GOLD COINS"); Compound_Words.Remove("GOLD"); } Console.ReadKey(); Current_Room.Name = "OUTSIDE"; Switch_Room(Current_Room.Name); Build_Room(); Display_Room(); return; } else // bribe failed { Console.Clear(); Write_Text("GATEKEEPER: \"You don't expect me to accept that paltry offer do you?\""); Gatekeeper_Bribe_Penalty += 5; Gatekeeper_Min_Bribe = selection; } } } Console.WriteLine("1) Continue Bribing"); Console.WriteLine("2) Stop Bribing"); do { Console.Write("> "); input = Console.ReadLine(); switch (input) { case "1": break; case "2": break; default: Console.WriteLine("Enter a number to select your response."); break; } } while (!int.TryParse(input, out selection) || selection < 1 || selection > 2); if (selection == 1) { input = "0"; } else if (selection == 2) { input = "2"; //this goes all the way back to the "see xythan" conversation topic } } while (!int.TryParse(input, out selection) || selection < 1 || selection > Player_Coins); break;
The Guard: Multiple Quest Solutions
A guard stands watch in the Northern Tower, a run-down and seemingly abandoned part of the castle. However, he is guarding three potential means of gaining access to Alyssa's prison cell:
- A wizard's scepter, hidden inside a loose brick. This completes the vestements of Rathmir, allowing you to command the Captain to grant access to the cell.
- The Bell. Ringing the bell sounds the alarm, and pulls the sentries away from Alyssa's prison.
- The guard's coin purse. This (usually) provides enough gold to bribe the captain to let you into the prison tower.
Likewise, the player has multiple options to remove the guard from the tower: using a disguise to deceive him into leaving, or bringing him enough food and wine to make him pass out. These options in turn are made available depending on the player's previous actions in the level, creating a host of potential paths to save Alyssa.
static Room North_Tower = new Room ( "NORTHERN TOWER", "The WALLS of the NORTHERN TOWER are chill and damp, and a steady breeze comes from the BELL tower overhead. A bored looking GUARD watches as you explore the room.", new Dictionary<string, string> //directions { {"WEST", "BEDROOM"}, {"SOUTHEAST", "DINING ROOM"} }, new Dictionary<string, string> { }, //items new Dictionary<string, string> //objects { {"BEDROOM", "To the WEST, a heavy door opens into the BEDROOM."}, {"DINING ROOM", "To the SOUTHEAST, an iron banded door opens into the DINING ROOM."}, {"WALLS", "The WALLS of the NORTHERN TOWER are stained from the rain and grime of the centuries. Several areas along the WALLS look cracked and decayed."}, {"BELL", "The walls at the top of the BELL tower have partially collapsed, opening the tower to the elements. A massive iron BELL hangs overhead, swaying in the wind."}, {"BELL TOWER", "The walls at the top of the BELL tower have partially collapsed, opening the tower to the elements. A massive iron BELL hangs overhead, swaying in the wind."}, }, new Dictionary<string, string> //npcs { {"GUARD", "The GUARD leans against one of the WALLS, looking miserably cold and bored."}, } ); static string Tower_Guard_Disposition = "first"; static void Talk_Tower_Guard() { if (Player_Identity == "MAGUS") { Tower_Guard_Disposition = "gone"; } if (Tower_Guard_Disposition == "gone") { Console.Clear(); Write_Text("Seeing you arrayed in WIZARDS ROBES, the GUARD snaps to attention."); Console.WriteLine(); Write_Text("GUARD: \"You must be the magus I was waiting for. I'll leave the room to you now.\""); Console.WriteLine(); Write_Text("After bowing repeatedly, the GUARD dashes away. You can't tell whether it was from fear of you or just eagerness to leave the freezing tower.\""); North_Tower.Description = "The WALLS of the NORTHERN TOWER are chill and damp, and a steady breeze comes from the BELL tower overhead. The GUARD who was here left very quickly when he saw you enter wearing WIZARDS ROBES."; North_Tower.NPCs["GUARD"] = "The GUARD left when you spoke to him wearing WIZARDS ROBES. He must have mistaken you for a magus."; North_Tower.Objects["WALLS"] = "The WALLS of the NORTHERN TOWER are stained from the rain and grime of the centuries. Several areas along the WALLS look cracked and decayed. The spot where the GUARD was standing looks particularly weird, as a large BRICK juts out."; Console.ReadKey(); Switch_Room(Current_Room.Name); return; } if (Tower_Guard_Disposition == "asleep") { Console.Clear(); Console.WriteLine("The GUARD is passed out."); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } Console.Clear(); if (Tower_Guard_Disposition == "first") { Write_Text("GUARD: \"What are you doing in here?\""); } else if (Tower_Guard_Disposition == "hungry") { Write_Text("GUARD: \"Ahh.. I'm so hungry... What can I help you with?\""); } else if (Tower_Guard_Disposition == "thirsty") { Write_Text("The GUARD looks a little green in the face."); Write_Text("GUARD: \"Oy... that stew... Oh hello there. What is it?\""); } Console.WriteLine(); Console.WriteLine("1) \"Just passing through.\""); Write_Text("2) \"What can you tell me about this CASTLE?\""); switch (Tower_Guard_Disposition) { case "first": Console.WriteLine("3) \"This tower seems so battered and empty. What are you guarding in here?\""); break; case "hungry": Console.WriteLine("3) \"Why don't you go get some food?\""); if (Inventory.Keys.Contains("STEW")) { Write_Text("4) Give the GUARD your STEW."); } break; case "thirsty": Console.WriteLine("3) \"You don't look so good after that stew...\""); if (Inventory.Keys.Contains("WINE BOTTLE")) { Write_Text("4) Give the GUARD your WINE BOTTLE."); } break; } Console.WriteLine(); string input; do { Console.Write("> "); input = Console.ReadLine(); switch (input) { case "1": Switch_Room(Current_Room.Name); return; case "2": Console.Clear(); Write_Text("GUARD: \"This CASTLE is a pretty interesting place with all the magical stuff here. Too bad it's ruled by an insane magus who stations all his poor guards out in the cold to freeze or starve to death.\""); Console.ReadKey(); Talk_Tower_Guard(); return; case "3": if (Tower_Guard_Disposition == "first") { if (!General_Objects.Keys.Contains("XYTHAN")) { General_Objects.Add("XYTHAN", ""); } if (!General_Objects["XYTHAN"].Contains("From GUARD:")) { General_Objects["XYTHAN"] += "\n\nFrom GUARD: XYTHAN has posted a GUARD in the NORTHERN TOWER until a wizard arrives."; } Console.Clear(); Write_Text("GUARD: \"Hell if I know. XYTHAN has me guarding this forsaken tower until some wizard friend of his arrives. If you ask me, he's gone completely insane. Even so, it's better to do what he says than to be caught disobeying orders... So I'm stuck here, cold and starving, until I'm relieved of duty.\""); North_Tower.NPCs["GUARD"] = "The GUARD leans against one of the WALLS. He looks starving."; Tower_Guard_Disposition = "hungry"; Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else if (Tower_Guard_Disposition == "hungry") { Console.Clear(); Write_Text("GUARD: \"I wish I could, but XYTHAN would kill me, or worse, if he found out I deserted my post... Say... If you're not too busy, maybe you could take pity on a poor soul and bring me some food? I'd be forever in your debt.\""); Console.WriteLine(); Console.WriteLine("1) \"I'll see what I can find for you.\""); Console.WriteLine("2) \"Sorry, I've got more important things to do.\""); Console.WriteLine(); do { Console.Write("> "); input = Console.ReadLine(); if (input.Trim() == "1" || input.Trim() == "2") { Switch_Room(Current_Room.Name); return; } else { Console.WriteLine("Enter a number to select your response."); input = "invalid"; } }while (input == "invalid"); } else if (Tower_Guard_Disposition == "thirsty") { Console.Clear(); Write_Text("GUARD: \"Aye that blasted COOK always adds too much salt to his stew. I've a raging thirst now. Please, if you could get me something to drink, I'd be eternally grateful.\""); Console.WriteLine(); Console.WriteLine("1) \"I'll see what I can find for you.\""); Console.WriteLine("2) \"Sorry, I've got more important things to do.\""); Console.WriteLine(); do { Console.Write("> "); input = Console.ReadLine(); if (input.Trim() == "1" || input.Trim() == "2") { Switch_Room(Current_Room.Name); return; } else { Console.WriteLine("Enter a number to select your response."); input = "invalid"; } } while (input == "invalid"); } break; case "4": if (Tower_Guard_Disposition == "hungry") { Console.Clear(); Write_Text("You give the GUARD your STEW."); Inventory.Remove("STEW"); Console.WriteLine(); Write_Text("He devours the foul stuff with a ravenous appetite. At last, when he's done, he looks up at you, seeming slightly sick."); Tower_Guard_Disposition = "thirsty"; North_Tower.NPCs["GUARD"] = "The GUARD leans against one of the WALLS. He looks sick with thirst."; Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else if (Tower_Guard_Disposition == "thirsty") { Console.Clear(); Write_Text("You give the GUARD your WINE BOTTLE."); Inventory.Remove("WINE BOTTLE"); Console.WriteLine(); Write_Text("He glugs down the stuff at a frantic pace. After about 15 minutes of drinking, he looks up at you. He sways back and forth slightly."); Write_Text("GUARD: \"Hankz schfur deh frin.. mferr deh.. thangz drin schfur.. shrubrn durgn summch... ahhhh..\""); Compound_Words.Add("COIN"); Compound_Terms.Add("COIN PURSE"); North_Tower.Items.Add("COIN PURSE", "A heavy purse full of GOLD COINS, it belongs to the GUARD in the NORTHERN TOWER."); North_Tower.Objects["WALLS"] = "The WALLS of the NORTHERN TOWER are stained from the rain and grime of the centuries. Several areas along the WALLS look cracked and decayed. The spot where the GUARD was standing looks particularly weird, as a large BRICK juts out."; North_Tower.Description = "The WALLS of the NORTHERN TOWER are chill and damp, and a steady breeze comes from the BELL tower overhead. The GUARD lies slumped on the floor in a drunken stupor."; Write_Text("The GUARD slumps to the ground in a drunken stupor."); Console.Write("His "); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("COIN PURSE "); Console.ForegroundColor = ConsoleColor.White; Console.Write("jungles loudly as it hits the floor."); Tower_Guard_Disposition = "asleep"; Console.ReadKey(); Switch_Room(Current_Room.Name); return; } break; default: Console.WriteLine("Enter a number to select your response."); input = "invalid"; break; } } while (input != "valid"); }
Color Coded Text Generator
Color coded text helps players spot important interactive items or parts of the environment, and informs them how to interact with these items.
|
|
//new function static void Write_Text(string input) { int space_remaining = 80; string [] word_array = input.Split(new char[] {' '}); List<string> word_list = word_array.ToList(); // join compound words together here for (int i = 0; i < word_list.Count(); i++) { //remove punctuation when checking the last word of compound term if (Compound_Words.Contains(word_list[i]) && word_list.Count > (i + 1)) //potential compound word { string[] no_punc_w2 = word_list[i+1].Trim().Split(new char[] { ',', ':', '.', '?', '!', '-', ';', '\t', }); // '\n', if (Compound_Terms.Contains(word_list[i] + " " + no_punc_w2[0])) //first 2 words form a complete compound term { word_list[i] += " " + word_list[i + 1]; //concatenate the words and remove the second from the list word_list.RemoveAt(i + 1); } else if (Compound_Words.Contains(word_list[i] + " " + word_list[i + 1]) && word_list.Count > (i + 2)) //first 2 words form a potential but incomplete compound word { string[] no_punc_w3 = word_list[i+2].Trim().Split(new char[] { ',', ':', '.', '?', '!', '-', ';' }); //'\t', '\n', if (Compound_Terms.Contains(word_list[i] + " " + word_list[i + 1] + " " + no_punc_w3[0])) //first 3 words form a complete compound term { word_list[i] += " " + word_list[i + 1] + " " + word_list[i + 2]; //concatenate the 3 words and remove the second and third from the list word_list.RemoveAt(i + 2); word_list.RemoveAt(i + 1); } } } } foreach (string word in word_list) { string[] no_punc = word.Trim().Split(new char[] {',', ':', '.', '?', '!', '-', ';'}); //'\t', '\n', //add colors to every word type: directions, npcs, rooms, items, objects, general objects if (Directions.Contains(no_punc[0])) { Console.ForegroundColor = ConsoleColor.Red; } else if (NPCs.Keys.Contains(no_punc[0])) { Console.ForegroundColor = ConsoleColor.DarkCyan; } else if (Rooms.Keys.Contains(no_punc[0]) || Current_Room.Name == no_punc[0]) { Console.ForegroundColor = ConsoleColor.Blue; } else if (Items.Keys.Contains(no_punc[0]) || Inventory.Keys.Contains(no_punc[0])) { Console.ForegroundColor = ConsoleColor.Yellow; } else if (Objects.Keys.Contains(no_punc[0])) { Console.ForegroundColor = ConsoleColor.Cyan; } else if (General_Objects.Keys.Contains(no_punc[0])) { Console.ForegroundColor = ConsoleColor.Green; } else { Console.ForegroundColor = ConsoleColor.White; } if (word.Contains("\n")) { space_remaining = 80; } if (word.Length >= 80) { Console.WriteLine("Error: word exceeds console size"); return; } else if (word.Length + 1 >= space_remaining) { Console.WriteLine(); Console.Write(word + " "); space_remaining = 80 - (word.Length + 1); } else { space_remaining -= word.Length + 1; Console.Write(word + " "); } } Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(); } } }
Modular Room Constructors
Room Constructors
Each room is a struct containing five lists: Area Description, Movement Options (Directions), Items, Objects, and NPCs. This system provides strong modularity and flexibility in adding or altering these archetypes, making it easy to build and expand (or contract) each room.
//new function - display room static void Display_Room() { Console.Clear(); Write_Text(Current_Room.Name); Write_Text(Current_Room.Description); Console.WriteLine(); foreach (var item in Current_Room.Directions) { Write_Text("To the " + item.Key + " is the " + Current_Room.Directions[item.Key]); } Console.WriteLine(); Console.WriteLine(); if (Current_Room.Name == "BARRACKS" && Captain_Disposition == "first") { Talk_Captain(); } Get_Command(Valid_Input()); return; } static void Build_Room() // constructs usable terms dictionaries from Current Room { //items objects npcs foreach (var element in Current_Room.Items) { if (!Items.Keys.Contains(element.Key)) //if the item is not in Items, add it from Current Room { Items.Add(element.Key, element.Value); } else //if the item is already in Items, replace the definition with the one from Current Room { Items[element.Key] = element.Value; } } foreach (var element in Current_Room.Objects) { if (!Objects.Keys.Contains(element.Key)) //if the object is not in Objects, add it from Current Room { Objects.Add(element.Key, element.Value); } else //if the object is already in Objects, replace the definition with the one from Current Room { Objects[element.Key] = element.Value; } } foreach (var element in Current_Room.NPCs) { if (!NPCs.Keys.Contains(element.Key)) //if the npc is not in NPCs, add it from Current Room { NPCs.Add(element.Key, element.Value); } else //if the npc is already in NPCs, replace the definition with the one from Current Room { NPCs[element.Key] = element.Value; } } foreach (var element in Current_Room.Directions) { if (!Rooms.Keys.Contains(element.Value)) //if the room is not in Rooms, add it from Current Room { Rooms.Add(element.Value, Current_Room.Objects[element.Value]); } else //if the room is already in Rooms, replace the definition with the one from Current Room { Rooms[element.Value] = Current_Room.Objects[element.Value]; } } if (!Rooms.Keys.Contains(Current_Room.Name)) { Rooms.Add(Current_Room.Name, Current_Room.Description); } else { Rooms[Current_Room.Name] = Current_Room.Description; } }
Initialization
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Media; namespace ConsoleApplication1 { class Program { //Player Stats static string Player_Identity = "TRAVELER"; //possible: traveler, magus, recruit, archmagus static int Player_Coins = 100; // multiple possible values //NOTE: "General_Objects" is the 3rd player stat. It acts as a knowledge base for the character. static Random RNG = new Random(); static System.Media.SoundPlayer WIND = new System.Media.SoundPlayer(); static System.Media.SoundPlayer INSIDE = new System.Media.SoundPlayer(); static System.Media.SoundPlayer VICTORY = new System.Media.SoundPlayer(); static System.Media.SoundPlayer CASTLE = new System.Media.SoundPlayer(); struct Room { public string Name; public string Description; public Dictionary<string, string> Directions; public Dictionary<string, string> Items; public Dictionary<string, string> Objects; public Dictionary<string, string> NPCs; public Room(string name, string description, Dictionary<string, string> directions, Dictionary<string, string> items, Dictionary<string, string> objects, Dictionary<string, string> npcs) { Name = name; Description = description; Directions = directions; Items = items; Objects = objects; NPCs = npcs; } } static Dictionary<string, string> General_Objects = new Dictionary<string, string>() { {"CASTLE", "The CASTLE looms over the landscape like a vast black spectre. Its walls are strong and tall: too steep to climb."}, {"KING", "KING Halfas has ruled the nation for nearly three decades. Aggressively expansionist during his first years of rule, Halfas conquered several independant baronies to expand the kingdom before retiring from war. Facing increasing dissension from the wizards within his own kingdom, Halfas has since turned his attention to politics within the capital. He now pays little attention to the satellite baronies at the far borders of his kingdom."}, }; static Dictionary<string, string> Objects = new Dictionary<string, string>(); static Dictionary<string, string> Items = new Dictionary<string, string>(); static Dictionary<string, string> NPCs = new Dictionary<string, string>(); static Dictionary<string, string> Rooms = new Dictionary<string, string>(); static Room Current_Room = new Room(); //============== //Main Function //============== static void Main(string[] args) { WIND.SoundLocation = @"wind.wav"; INSIDE.SoundLocation = @"inside.wav"; VICTORY.SoundLocation = @"win.wav"; CASTLE.SoundLocation = @"castle.wav"; WIND.Play(); Current_Room.Name = "OUTSIDE"; Help(); Switch_Room(Current_Room.Name); } //new function - switch room - fills in the information of the room based on room name - assumes the room name has already been changed static void Switch_Room(string Room_name) { switch (Room_name) { case "OUTSIDE": Current_Room.Description = Outside.Description; Current_Room.Directions = Outside.Directions; Current_Room.Items = Outside.Items; Current_Room.Objects = Outside.Objects; Current_Room.NPCs = Outside.NPCs; break; case "COURTYARD": Current_Room.Description = Courtyard.Description; Current_Room.Directions = Courtyard.Directions; Current_Room.Items = Courtyard.Items; Current_Room.Objects = Courtyard.Objects; Current_Room.NPCs = Courtyard.NPCs; break; case "STUDY": Current_Room.Description = Study.Description; Current_Room.Directions = Study.Directions; Current_Room.Items = Study.Items; Current_Room.Objects = Study.Objects; Current_Room.NPCs = Study.NPCs; break; case "BEDROOM": Current_Room.Description = Bedroom.Description; Current_Room.Directions = Bedroom.Directions; Current_Room.Items = Bedroom.Items; Current_Room.Objects = Bedroom.Objects; Current_Room.NPCs = Bedroom.NPCs; break; case "NORTHERN TOWER": Current_Room.Description = North_Tower.Description; Current_Room.Directions = North_Tower.Directions; Current_Room.Items = North_Tower.Items; Current_Room.Objects = North_Tower.Objects; Current_Room.NPCs = North_Tower.NPCs; break; case "DINING ROOM": Current_Room.Description = Dining.Description; Current_Room.Directions = Dining.Directions; Current_Room.Items = Dining.Items; Current_Room.Objects = Dining.Objects; Current_Room.NPCs = Dining.NPCs; break; case "KITCHEN": Current_Room.Description = Kitchen.Description; Current_Room.Directions = Kitchen.Directions; Current_Room.Items = Kitchen.Items; Current_Room.Objects = Kitchen.Objects; Current_Room.NPCs = Kitchen.NPCs; break; case "HALL OF STATUES": Current_Room.Description = Statues.Description; Current_Room.Directions = Statues.Directions; Current_Room.Items = Statues.Items; Current_Room.Objects = Statues.Objects; Current_Room.NPCs = Statues.NPCs; break; case "LIBRARY": Current_Room.Description = Library.Description; Current_Room.Directions = Library.Directions; Current_Room.Items = Library.Items; Current_Room.Objects = Library.Objects; Current_Room.NPCs = Library.NPCs; break; case "THRONE ROOM": Current_Room.Description = Throne.Description; Current_Room.Directions = Throne.Directions; Current_Room.Items = Throne.Items; Current_Room.Objects = Throne.Objects; Current_Room.NPCs = Throne.NPCs; break; case "BARRACKS": Current_Room.Description = Barracks.Description; Current_Room.Directions = Barracks.Directions; Current_Room.Items = Barracks.Items; Current_Room.Objects = Barracks.Objects; Current_Room.NPCs = Barracks.NPCs; break; case "SOUTHERN TOWER": Current_Room.Description = South_Tower.Description; Current_Room.Directions = South_Tower.Directions; Current_Room.Items = South_Tower.Items; Current_Room.Objects = South_Tower.Objects; Current_Room.NPCs = South_Tower.NPCs; break; default: Console.WriteLine("You broke the game by going to a nonexistant room. Teleporting to OUTSIDE."); Console.ReadKey(); Current_Room.Name = "OUTSIDE"; Switch_Room(Current_Room.Name); break; } Build_Room(); Display_Room(); return; }
Modular Text Parser and Input Handler
Text Bank, Parser, and Input Validator
The text parser checks for key words from word banks for each archetype. It is not case sensitive, and responds to compound inputs and multiple variants for each input. For instance: "GO EAST", "East", "go e", and "E" all yield the same result.
static List<string> Directions = new List<string> { "GO", "W", "WEST", "NW", "NORTHWEST", "N", "NORTH", "NE", "NORTHEAST", "E", "EAST", "SE", "SOUTHEAST", "S", "SOUTH", "SW", "SOUTHWEST" }; static List<string> Actions = new List<string> { "X", "EXAMINE", "LOOK", "L", "EXPLORE", "TAKE", "TALK", "EQUIP", "UNEQUIP", "I", "INVENTORY", "STATS", "OPEN", "EAT", "DRINK", "IMBIBE", "DEVOUR", }; static List<string> Quit = new List<string> { "Q", "QUIT", "EXIT" }; static Dictionary<string, string> Inventory = new Dictionary<string, string> { {"TRAVELLING CLOTHES", "A long brown cloak with a hood. Typical traveler's attire."}, {"GOLD COINS", "This standard currency bears the mark of the KING."}, {"NOTE", "A crumpled NOTE from your brother. (To read, exit the Inventory menu and examine the note)"}, }; static List<string> Exceptions = new List<string> { "", "TO", "ON", "IN", "OVER", "UNDER", "OFF", "UP", "DOWN", "WITH", "THE", //, "OF" - removed to allow "HALL OF STATUES" , "GO" - removed to parse the command more strictly }; static List<string> Compound_Words = new List<string> // lists the first word(s) of compound terms { "CERULEAN", "HALL", "HALL OF", "DINING", "THRONE", "SOUTHERN", "NORTHERN", "WINE", "BOOK", "WRITING", "PRISON", "TRAVELLING", "GOLD", "BELL", }; static List<string> Compound_Terms = new List<string> // lists complete compound terms { "CERULEAN ESSENCE", "HALL OF STATUES", "DINING ROOM", "THRONE ROOM", "SOUTHERN TOWER", "NORTHERN TOWER", "WINE BOTTLE", "BOOK CASES", "BOOK CASE", "WRITING DESKS", "WRITING DESK", "PRISON CELLS", "PRISON CELL", "TRAVELLING CLOTHES", "GOLD COINS", "BELL TOWER", }; //new function - makes sure the input uses valid words - stores input as trimmed, upper case string, ignoring 'exception' words static List<string> Valid_Input() { Console.Write("> "); string input = Console.ReadLine().ToUpper(); string[] input_string_array = input.Split(' ', '\t'); List<string> input_trimmed = new List<string>(); foreach (var item in input_string_array) { string item_tr = item.Trim(); if (!Exceptions.Contains(item_tr)) { input_trimmed.Add(item_tr); } } if (input_trimmed.Count < 1) // if user enters nothing or whitespace { return Valid_Input(); } for (int i = 0; i < input_trimmed.Count; i++) // (var item in input_trimmed) { if (input_trimmed[i] == "HELP") { Help(); return Valid_Input(); } if (!Directions.Contains(input_trimmed[i]) && !Actions.Contains(input_trimmed[i]) && !Quit.Contains(input_trimmed[i]) && !Items.Keys.Contains(input_trimmed[i]) && !NPCs.Keys.Contains(input_trimmed[i]) && !Objects.Keys.Contains(input_trimmed[i]) && !General_Objects.Keys.Contains(input_trimmed[i]) && !Rooms.Keys.Contains(input_trimmed[i]) && !Exceptions.Contains(input_trimmed[i]) && !Inventory.Keys.Contains(input_trimmed[i])) { while (Compound_Words.Contains(input_trimmed[i]) && input_trimmed.Count > (i + 1)) { input_trimmed[i] += " " + input_trimmed[i+1]; input_trimmed.RemoveAt(i + 1); } if (!Compound_Terms.Contains(input_trimmed[i])) { Console.WriteLine("\"" + input_trimmed[i] + "\" is not a valid word."); return Valid_Input(); } if (!Directions.Contains(input_trimmed[i]) && !Actions.Contains(input_trimmed[i]) && !Quit.Contains(input_trimmed[i]) && !Items.Keys.Contains(input_trimmed[i]) && !NPCs.Keys.Contains(input_trimmed[i]) && !Objects.Keys.Contains(input_trimmed[i]) && !General_Objects.Keys.Contains(input_trimmed[i]) && !Rooms.Keys.Contains(input_trimmed[i]) && !Exceptions.Contains(input_trimmed[i]) && !Inventory.Keys.Contains(input_trimmed[i])) { Console.WriteLine("\"" + input_trimmed[i] + "\" is not a valid word."); return Valid_Input(); } } } return input_trimmed; } //new function chooses action submenu based on input static void Get_Command(List<string> input_list) { //direction input if (Directions.Contains(input_list[0])) { Direction_Input(input_list); return; } // action input else if (Actions.Contains(input_list[0])) { // if input takes an action but has no object if (input_list.Count < 2 && input_list[0] != "I" && input_list[0] != "INVENTORY" && input_list[0] != "STATS") { Console.WriteLine("You must enter an object to interact with."); Console.WriteLine("Correct format is: " + input_list[0] + " <OBJECT>"); Get_Command(Valid_Input()); return; } switch (input_list[0]) { case "X": case "EXAMINE": case "LOOK": case "L": case "EXPLORE": Examine(input_list[1]); return; case "TAKE": Take_Item(input_list[1]); return; case "TALK": Talk_To(input_list); return; case "EQUIP": if (input_list[1] == "WIZARDS ROBES") { if (Player_Identity != "ARCHMAGUS") { Player_Identity = "MAGUS"; Write_Text("You equip the WIZARDS ROBES and are now recognized as a magus"); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } } else if (input_list[1] == "WIZARDS SCEPTER") { if (Player_Identity != "MAGUS") { Write_Text("You must first be wearing wizard's robes or you will attract attention as a thief."); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else { Player_Identity = "ARCHMAGUS"; Write_Text("You equip the WIZARDS SCEPTER and are now recognized as an Archmagus."); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } } else { Write_Text("You cannot equip " + input_list[1]); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } //do EQUIP action return; case "I": case "INVENTORY": View_Inventory(); return; case "STATS": View_Stats(); return; case "OPEN": Open(input_list[1]); return; case "EAT": case "DEVOUR": Console.WriteLine("You're not hungry right now."); Get_Command(Valid_Input()); return; case "DRINK": case "IMBIBE": Console.WriteLine("You're not thirsty right now."); Get_Command(Valid_Input()); return; } } // quit input else if (Quit.Contains(input_list[0])) { Quit_Program(); return; } // item input else if (Items.Keys.Contains(input_list[0])) { Console.Clear(); Examine(input_list[0]); return; } //NPC input else if (NPCs.Keys.Contains(input_list[0])) { Console.Clear(); Examine(input_list[0]); return; } //object input else if (Objects.Keys.Contains(input_list[0])) { Console.Clear(); Examine(input_list[0]); return; } //room input else if (Rooms.Keys.Contains(input_list[0])) { Console.Clear(); Display_Room_Description(input_list[0]); return; } //general object input else if (General_Objects.Keys.Contains(input_list[0])) { Console.Clear(); Display_General_Object(input_list[0]); return; } else if (Inventory.Keys.Contains(input_list[0])) { Console.Clear(); Examine(input_list[0]); return; } }
Player Action Functions
The player has numerous interactive actions he can take, and the parser must recognize and correctly respond to all of them (examining, taking, opening, talking, equipping, etc.). Beyond this, the player may access his inventory, journal, or the help menu at any time.
//new function - direction input static void Direction_Input(List<string> input_list) { switch (input_list[0]) { case "W": input_list[0] = "WEST"; break; case "NW": input_list[0] = "NORTHWEST"; break; case "N": input_list[0] = "NORTH"; break; case "NE": input_list[0] = "NORTHEAST"; break; case "E": input_list[0] = "EAST"; break; case "SE": input_list[0] = "SOUTHEAST"; break; case "S": input_list[0] = "SOUTH"; break; case "SW": input_list[0] = "SOUTHWEST"; break; case "GO": if (input_list.Count > 1 && Directions.Contains(input_list[1])) { input_list.RemoveAt(0); Direction_Input(input_list); } else //doesn't know where to go { Write_Text("You must enter a direction to go."); Write_Text("Correct format is: <DIRECTION> or GO <DIRECTION>"); Console.ReadKey(); Display_Room(); return; } break; } if (Current_Room.Directions.Keys.Contains(input_list[0])) // if the room allows you to go in that direction { if (Current_Room.Name == "OUTSIDE" && Gate_IsLocked == true) //can't go through the locked gate from OUTSIDE to COURTYARD { Console.Clear(); Write_Text("The GATE is locked."); Console.ReadKey(); Display_Room(); return; } if (Current_Room.Name == "THRONE ROOM" && !Throne.Objects["DOOR"].Contains("unlock") && Current_Room.Directions[input_list[0]] == "SOUTHERN TOWER") //can't go through the locked door from THRONE to SOUTH TOWER { Console.Clear(); Write_Text("The DOOR is locked."); Console.ReadKey(); Display_Room(); return; } if (Current_Room.Name == "SOUTHERN TOWER" && !South_Tower.Objects["DOOR"].Contains("unlock") && Current_Room.Directions[input_list[0]] == "THRONE ROOM") //can't go through the locked door from SOUTH TOWER to THRONE { Console.Clear(); Write_Text("The DOOR is locked."); Console.ReadKey(); Display_Room(); return; } Current_Room.Name = Current_Room.Directions[input_list[0]]; // sets the current room name to value tied to the direction if (Current_Room.Name == "COURTYARD") { WIND.Play(); } else if (Current_Room.Name == "THRONE ROOM") { INSIDE.Play(); } else if (Current_Room.Name == "STUDY" || Current_Room.Name == "HALL OF STATUES" || Current_Room.Name == "LIBRARY") { CASTLE.Play(); } Switch_Room(Current_Room.Name); return; } else // user entered a valid directional command but the room has no links in that direction { Console.WriteLine("You cannot go that way."); Console.ReadKey(); Display_Room(); return; } } //new function - examine static void Examine(string input) { Console.Clear(); if (Current_Room.Name == input) { Display_Room(); return; } else if (Current_Room.Objects.Keys.Contains(input)) { if (Current_Room.Name == "COURTYARD") { if (General_Objects.Keys.Contains("CERULEAN ESSENCE") && !General_Objects["CERULEAN ESSENCE"].Contains("You don't know")) { Courtyard.Objects["FOUNTAIN"] = "In the center of the COURTYARD, a large stone FOUNTAIN radiates a mysterious blue light. The particles of light seem to splash and hang in the air, like an ephemeral, viscous substance. This must be the CERULEAN ESSENCE you've heard about."; } } if (Current_Room.Name == "STUDY") { //BURNT LETTER, GOLDEN KEY if (input == "DESK") { if (!Study.Items.Keys.Contains("GOLDEN KEY")) { Study.Items.Add("GOLDEN KEY", "A glimmering key with the likeness of a gargoyle subtly worked into the metal."); Compound_Words.Add("GOLDEN"); Compound_Terms.Add("GOLDEN KEY"); } } if (input == "FIREPLACE") { if (!Study.Items.Keys.Contains("BURNT LETTER")) { Study.Items.Add("BURNT LETTER", "A letter that was hastily thrown into the fire, its contents are only partially destroyed."); Compound_Words.Add("BURNT"); Compound_Terms.Add("BURNT LETTER"); } } } if (Current_Room.Name == "DINING ROOM") { //WINE BOTTLE if (input == "TABLE") { if (!Dining.Items.Keys.Contains("WINE BOTTLE")) { Dining.Items.Add("WINE BOTTLE", "A large bottle of potent wine. The bottle seems to be enchanted, constantly refilling itself so it never empties."); Compound_Words.Add("WINE"); Compound_Terms.Add("WINE BOTTLE"); } } } if (Current_Room.Name == "SOUTHERN TOWER") { //WINE BOTTLE if (input == "PRISON CELLS" || input == "PRISON" || input == "CELLS" || input == "CELL" || input == "PRISON CELL") { if (!South_Tower.NPCs.Keys.Contains("ALYSSA")) { South_Tower.NPCs.Add("ALYSSA", "ALYSSA is in one of the cells. She's lying face down, not moving."); } } } if (Current_Room.Name == "NORTHERN TOWER") { //BRICK if (input == "WALLS" || input == "WALL") { if (North_Tower.Objects["WALLS"].Contains("BRICK") && !North_Tower.Objects.Keys.Contains("BRICK")) { North_Tower.Objects.Add("BRICK", "On closer inspection you see the BRICK is loose. Behind it, in a hidden recess, you see a WIZARDS SCEPTER."); } } //WIZARDS SCEPTER if (input == "BRICK") { if (!Compound_Words.Contains("WIZARDS")) { Compound_Words.Add("WIZARDS"); } if (!Compound_Terms.Contains("WIZARDS SCEPTER")) { Compound_Terms.Add("WIZARDS SCEPTER"); } if (!North_Tower.Items.Keys.Contains("WIZARDS SCEPTER") && !Inventory.Keys.Contains("WIZARDS SCEPTER")) { North_Tower.Items.Add("WIZARDS SCEPTER", "A glowing rod of onyx, this arcane artifact radiates magical energy."); } } } if (Current_Room.Name == "THRONE ROOM") { //SIGNET RING if (input == "THRONE") { if (!Throne.Items.Keys.Contains("SIGNET RING")) { Throne.Items.Add("SIGNET RING", "A shimmering golden ring studded with small saphires. It bears an elaborate seal."); Compound_Words.Add("SIGNET"); Compound_Terms.Add("SIGNET RING"); } } } Write_Text(Current_Room.Objects[input]); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else if (Current_Room.Items.Keys.Contains(input)) { Write_Text(Current_Room.Items[input]); } else if (Current_Room.NPCs.Keys.Contains(input)) { Write_Text(Current_Room.NPCs[input]); } else if (General_Objects.Keys.Contains(input)) { Write_Text(General_Objects[input]); } else if (Inventory.Keys.Contains(input)) { if (input == "NOTE") { Outside.Description = "You stand outside a towering black CASTLE, the wind chill on your face. The GATE EAST to the COURTYARD stands shut, guarded by a GATEKEEPER."; if (!General_Objects.Keys.Contains("XYTHAN")) { General_Objects.Add("XYTHAN", ""); } if (!General_Objects.Keys.Contains("ALYSSA")) { General_Objects.Add("ALYSSA", ""); } if (!General_Objects.Keys.Contains("CASTLE")) { General_Objects.Add("CASTLE", ""); } if (!General_Objects["XYTHAN"].Contains("From NOTE:")) { General_Objects["XYTHAN"] += "\n\nFrom NOTE: XYTHAN is the ruler of the domain encompassing your village. Apparently, when ALYSSA confronted him over his excessive tribute demands, he imprisoned her within his CASTLE. According to your brother, XYTHAN recently left the CASTLE to visit the capital, leaving few guards to defend it."; } General_Objects["ALYSSA"] = "\n\nYour dearest friend since childhood, ALYSSA and you grew up together. Very brave and acutely empathetic with the people around her, ALYSSA's courage can get her in trouble when she stands up to higher authorities on the behalf of the oppressed."; if (!General_Objects["CASTLE"].Contains("From NOTE:")) { General_Objects["CASTLE"] += "\n\nFrom NOTE: XYTHAN holds ALYSSA prisoner within his CASTLE."; } Write_Text("Brother,"); Write_Text("I have dire news for you. While you were gone, XYTHAN came to the village, demanding extra tribute. When that impoverished old crone Thyla couldn't pay, XYTHAN grew engraged. Just as he was about to strike her down, ALYSSA intervened, offering to pay on Thyla's behalf and remonstrating XYTHAN for his unreasonable demands. The wizard was visibly seething after this outburst, and ordered his guard to take ALYSSA. She's been imprisoned in his CASTLE."); Console.WriteLine(); Write_Text("I know I can't stop you from trying to rescue her, but please, don't be a fool and get yourself killed in the attempt. XYTHAN is rumored to be leaving for the capital within a few days, and will doubtless take most of his guards with him. If you want to rescue her, that will be your chance. I only hope you're not too late."); Console.WriteLine(); Write_Text("Good luck and godspeed."); } else if (input == "BURNT LETTER") { Write_Text("Many patches of the letter's contents are burned away. You can make out the following: \n"); Write_Text("Xyt.... \n"); Write_Text("..................... coming to ........... prisoner. I want to test................................................... ence. The results should prove interesting in furthering our knowledge of this phenomenon................................................................... sts, the prisoner will most likely die under the effects of th................................... there soon. If you cannot meet me, leave my..................................................rthern tower. \nSigned,\n-Ra......"); } else if (input == "CONSCRIPTION CONTRACT") { if (!General_Objects.Keys.Contains("RECRUIT")) { General_Objects.Add("RECRUIT", ""); } General_Objects["RECRUIT"] += "\n\nFrom CONSCRIPTION CONTRACT: You have a document detailing the hiring of the new RECRUIT for the CASTLE. All his expenses have been waived."; Write_Text("The CONSCRIPTION CONTRACT lays out the terms for hiring a new RECRUIT for the CASTLE, detailing specifics of duties, pay, and a stern list of expectations and requirements. Among other things it lists that all equipment expenses have been waived."); } else { Write_Text(Inventory[input]); } } Console.ReadKey(); Switch_Room(Current_Room.Name); return; } //new function - take static void Take_Item(string item_name) { if (Current_Room.Items.Keys.Contains(item_name)) { //STUDY - GOLDEN KEY, BURNT LETTER, if (Current_Room.Name == "STUDY") { if (item_name == "BURNT LETTER") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Study.Items[item_name]); } Study.Items.Remove(item_name); Study.Objects["FIREPLACE"] = "Smoldering coals still glow red hot in the FIREPLACE, warming the room."; Write_Text("You take the " + item_name + " from the hot embers of the FIREPLACE."); } if (item_name == "GOLDEN KEY") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Study.Items[item_name]); } Study.Items.Remove(item_name); Study.Objects["DESK"] = "A massive oak DESK dominates the center of the room. The woodwork exhibits very intricate sculpture and ornate decoration."; Write_Text("You grab the " + item_name + " from under the DESK."); } } //BEDROOM - WIZARDS ROBES if (Current_Room.Name == "BEDROOM") { if (item_name == "WIZARDS ROBES") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Bedroom.Items[item_name]); } Bedroom.Items.Remove(item_name); Write_Text("You take the " + item_name + " from out of the WARDROBE."); } if (item_name == "CONSCRIPTION CONTRACT") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Bedroom.Items[item_name]); } Bedroom.Items.Remove(item_name); Write_Text("You take the " + item_name + " from out of the CHEST."); } } //NORTHERN TOWER - COIN PURSE if (Current_Room.Name == "NORTHERN TOWER") { if (item_name == "COIN PURSE") { Write_Text("You find 200 GOLD COINS in the " + item_name); North_Tower.Items.Remove(item_name); Player_Coins += 200; } if (item_name == "WIZARDS SCEPTER") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, North_Tower.Items[item_name]); } North_Tower.Items.Remove(item_name); Write_Text("You grab the " + item_name + " from the CHEST."); } } //THRONE ROOM - SIGNET RING if (Current_Room.Name == "THRONE ROOM") { if (item_name == "SIGNET RING") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Throne.Items[item_name]); } Throne.Items.Remove(item_name); Throne.Objects["THRONE"] = "A massive stone THRONE sits on an elevated dais. Lion's head carvings make up the two armrests, and the seat and back are padded with large purple cushions of fine silk."; Write_Text("You grab the " + item_name + " from the THRONE cushions."); } } //KITCHEN - INGREDIENTS if (Current_Room.Name == "KITCHEN") { if (item_name == "INGREDIENTS") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Kitchen.Items[item_name]); } Kitchen.Items.Remove(item_name); Kitchen.Description = "A fat COOK scowls at you briefly as wander around them room, then returns to stirring a huge bubbling CAULDRON of stew."; Write_Text("You take the " + item_name + " from the counter."); } } //DINING ROOM - WINE BOTTLE if (Current_Room.Name == "DINING ROOM") { if (item_name == "WINE BOTTLE") { if (!Inventory.Keys.Contains(item_name)) { Inventory.Add(item_name, Dining.Items[item_name]); } Dining.Items.Remove(item_name); Dining.Objects["TABLE"] = "A long mahogany TABLE fills the room, lined with luxurious chairs. Unlit candelabras span its length from end to end. It looks very empty with no dishes or silverware laid out."; Write_Text("You take the " + item_name + " from the TABLE."); } } } else { Write_Text("You can't take the " + item_name); } Console.ReadKey(); Switch_Room(Current_Room.Name); return; } //new function - talk static void Talk_To(List<string> input_list) { // if the first word is not a valid npc, item, object, general object, or room, concatenate it with next word - zero'th word is "talk" while (!NPCs.Keys.Contains(input_list[1]) && !Items.Keys.Contains(input_list[1]) && !Objects.Keys.Contains(input_list[1]) && !General_Objects.Keys.Contains(input_list[1]) && !Rooms.Keys.Contains(input_list[1]) && input_list.Count > 2) { if (input_list.Count > 2) // adds word 2 to word 1 and removes word 2 from the list { input_list[1] += input_list[2]; input_list.Remove(input_list[2]); } else // we're down to an action + object, but the object is unspecified { Console.WriteLine("You must specify something to talk to."); Console.WriteLine("Correct format is: TALK <OBJECT> or TALK TO <OBJECT>"); Get_Command(Valid_Input()); return; } } if (Current_Room.NPCs.Keys.Contains(input_list[1])) { //do a talk function based on NPC switch (input_list[1]) { case "GATEKEEPER": Talk_Gatekeeper(out Gate_IsLocked, Gatekeeper_Disposition); break; case "COOK": Talk_Cook(); break; case "GUARD": if (Current_Room.Name == "NORTHERN TOWER") { Talk_Tower_Guard(); return; } else if (Current_Room.Name == "BARRACKS") { Talk_Captain(); } break; case "CAPTAIN": case "OFFICER": Talk_Captain(); break; case "ALYSSA": Talk_Alyssa(); break; } return; } //if the target is an item/object/room that is in range else if (Current_Room.Items.Keys.Contains(input_list[1]) || Current_Room.Objects.Keys.Contains(input_list[1]) || Current_Room.Directions.Values.Contains(input_list[1]) || Inventory.Keys.Contains(input_list[1])) { Console.WriteLine("You talk to the " + input_list[1] + ", but there is no response."); Get_Command(Valid_Input()); return; } else // the target is not nearby { Console.WriteLine(input_list[1] + " is nowhere to be found."); Get_Command(Valid_Input()); } return; } //new function - view inventory static void View_Inventory() { Console.Clear(); Write_Text("To view an item description, type the item's name. To continue playing, press 'Enter'"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine("INVENTORY:"); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("-------------------------------------------------------------------------------"); foreach (var item in Inventory) { Write_Text(item.Key); } Console.WriteLine(); string input = "do at least once once"; while (input != "") { Console.Write("> "); input = Console.ReadLine().Trim().ToUpper(); if (Inventory.Keys.Contains(input)) { Write_Text(input + ": " + Inventory[input]); Console.ReadKey(); View_Inventory(); return; } else if (input != "") { Console.WriteLine("You do not have that in your inventory."); Console.ReadKey(); View_Inventory(); return; } } Switch_Room(Current_Room.Name); return; } //new function - view stats static void View_Stats() { Console.Clear(); Write_Text("To view your inventory, exit the stats menu and enter 'I' or 'Inventory'."); Console.WriteLine("To continue playing, press 'Enter'"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine("PLAYER STATS:"); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("-------------------------------------------------------------------------------"); Write_Text("GOLD COINS: " + Player_Coins); Console.WriteLine(); Write_Text("Current garb/identity: " + Player_Identity); Console.WriteLine(); Write_Text("Current knowledge base: "); foreach (var item in General_Objects) { Write_Text(item.Key); } Console.ReadKey(); Display_Room(); } //new function - open object static void Open(string input) { Console.Clear(); if (Current_Room.Name == "OUTSIDE") { if (input == "GATE") { Write_Text("The GATE is locked and you have no way to open it yourself."); } else { Write_Text("You cannot open the " + input); } } else if (Current_Room.Name == "BEDROOM") { if (input == "CHEST") { if (Inventory.Keys.Contains("GOLDEN KEY")) { if (!Inventory.Keys.Contains("CONSCRIPTION CONTRACT")) { if (!Compound_Words.Contains("CONSCRIPTION")) { Compound_Words.Add("CONSCRIPTION"); } if (!Compound_Terms.Contains("CONSCRIPTION CONTRACT")) { Compound_Terms.Add("CONSCRIPTION CONTRACT"); } if (!Bedroom.Items.Keys.Contains("CONSCRIPTION CONTRACT")) { Bedroom.Items.Add("CONSCRIPTION CONTRACT", "A document officially recognizing the enlistment of a new guard."); } Write_Text("You open the CHEST with the GOLDEN KEY."); Console.Write("Inside you see a "); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("CONSCRIPTION CONTRACT"); Console.ForegroundColor = ConsoleColor.White; Console.Write(" for a new guard."); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else { Write_Text("The CHEST is open."); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } } else { Write_Text("The CHEST is locked and you do not have the key."); } } else if (input == "WARDROBE") { if (!Inventory.Keys.Contains("WIZARDS ROBES")) { if (!Compound_Words.Contains("WIZARDS")) { Compound_Words.Add("WIZARDS"); } if (!Compound_Terms.Contains("WIZARDS ROBES")) { Compound_Terms.Add("WIZARDS ROBES"); } if (!Bedroom.Items.Keys.Contains("WIZARDS ROBES")) { Bedroom.Items.Add("WIZARDS ROBES", "These robes are made of a fine cloth you cannot identify. Woven into the midnight black fabric are silver runes and insignias that seem to subtly glow with magic."); } Console.Write("Hanging inside the massive WARDROBE you see a set of "); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("WIZARDS ROBES."); Console.ForegroundColor = ConsoleColor.White; Console.ReadKey(); Switch_Room(Current_Room.Name); return; } else { Write_Text("The WARDROBE is open."); } } else { Write_Text("You cannot open the " + input); } } else if (Current_Room.Name == "THRONE ROOM") { if (input == "DOOR") { if (!Inventory.Keys.Contains("KEYRING")) { Write_Text("The DOOR is locked and you do not have the keys."); Console.ReadKey(); Switch_Room(Current_Room.Name); } else { Write_Text("You open the DOOR with the keys on the KEYRING."); Throne.Objects["DOOR"] = "The DOOR is unlocked."; South_Tower.Objects["DOOR"] = "The DOOR is unlocked."; Console.ReadKey(); } } } Console.ReadKey(); Switch_Room(Current_Room.Name); } //new function - quit game static void Quit_Program() { Console.WriteLine("Are you sure you want to quit? (Y/N)"); Console.Write("> "); string confirm = Console.ReadLine().Trim().ToUpper(); if (confirm == "YES" || confirm == "Y" || confirm == "CONFIRM" || confirm == "") { Environment.Exit(0); return; } else { Get_Command(Valid_Input()); return; } } //new function - help menu static void Help() { Console.Clear(); Write_Text("Welcome! \nGenerally, things you can interact with are capitalized. Often they will also be colored. Green is for knowledge, Yellow is for items, Cyan is for objects, Red is for directions, Blue is for room names, Dark Green is for NPCs, etc."); Console.ReadKey(); Write_Text("Commands: \nstats to view stats\ni or inventory to view inventory\nGo <direction> or just <direction> to move \nTalk to <NPC> or Talk <NPC> to converse\nOpen <object> to open locked objects\nTake <item> to add items to inventory\nEquip <item> to equip certain items to change your perceived identity\nExamine <X> to more closely inspect things. This is also how you read.\nQuit to exit game\nHelp to view this menu\nThere are some other things you can do as well! Experiment and have fun! GOOD LUCK!"); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } //new function - room input static void Display_Room_Description(string room_name) { Write_Text(room_name + ": " + Rooms[room_name]); Console.ReadKey(); Switch_Room(Current_Room.Name); return; } //new function - general object input static void Display_General_Object(string object_name) { Write_Text(object_name + ": " + General_Objects[object_name]); Console.ReadKey(); Switch_Room(Current_Room.Name); return; }