Project Overview
Mini Dungeon is a text-based RPG built as a C# console application, inspired by traditional dungeon crawler games. This project demonstrates my programming fundamentals, object-oriented design principles, and ability to create engaging gameplay mechanics through code alone.
Development Time: 2 months
Team Size: Solo project
Role: Programmer & Game Designer
Platform: Console Application (Windows)
Screenshots & Console Outputs
Key Features
📜 Handcrafted Dungeon Map and City Map
The map is based on my idea to integrate my university life in Cambridge City in a small fantasy RPG including my teachers as NPCs, mini-Bosses and final Boss. I created one single map that included several interconnected location:
- Home
- Compass House (Town)
- The White Horse Tavern
- The Nerd Shrine (Shop)
- Ian, The Alchemist House
- The Red Forest
- The Cabin in The Woods
- The Red Lake
- The Mini Dungeon
- Entrance
- Three Dungeon Rooms
- Throne Room

Figure 1: Mini Dungeon Map prototype

Figure 2: Mini Dungeon Map prototype with the final dungeon map
⚔️ Turn-based Combat System
The combat system is the ancestor of every primordial RPGs: it is a strategic turn-based mechanics that requires a small tactical thinking. The damage calculation is a mathematical combat system with attack, defense, and health system.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static void PlayerTurn()
{
int damageToMonster;
int monsterDefense;
int effectiveDamageToMonster;
monsterDefense = RandomNumberGenerator.NumberBetween(_currentMonster.MinimumProtection,
_currentMonster.MaximumProtection);
if (_character.newPlayer.MinimumDamage != 0 && _character.newPlayer.MaximumDamage != 0)
{
damageToMonster = RandomNumberGenerator.NumberBetween(_character.newPlayer.MinimumDamage,
_character.newPlayer.MaximumDamage);
effectiveDamageToMonster = PlayerTurnReport(damageToMonster, monsterDefense);
}
else
{
damageToMonster = RandomNumberGenerator.NumberBetween(_character.newPlayer.ClassMinimumDamage,
_character.newPlayer.ClassMaximumDamage);
effectiveDamageToMonster = PlayerTurnReport(damageToMonster, monsterDefense);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
private static int CalcEffectiveDamageToMonster(int damageToMonster, int monsterDefense)
{
int effectiveDamageToMonster = damageToMonster - monsterDefense;
if (effectiveDamageToMonster <= 0)
{
effectiveDamageToMonster = 0;
}
else
{
_currentMonster.CurrentHitPoints -= effectiveDamageToMonster;
}
return effectiveDamageToMonster;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// <summary>
/// Report of the player turn
/// </summary>
/// <param name="damageToMonster">Player attack power</param>
/// <param name="monsterDefense">Monster defense power</param>
/// <returns></returns>
private static int PlayerTurnReport(int damageToMonster, int monsterDefense)
{
int effectiveDamageToMonster;
Console.Clear();
Console.WriteLine(fightText);
Console.WriteLine();
Console.WriteLine("** Your turn ***");
Console.WriteLine();
Console.WriteLine("You attacking with " + damageToMonster + " hit point/s.");
Console.WriteLine(_currentMonster.Name + " is defending with " + monsterDefense + " armor points.");
effectiveDamageToMonster = CalcEffectiveDamageToMonster(damageToMonster, monsterDefense);
Console.WriteLine("You hit " + _currentMonster.Name + " for " + effectiveDamageToMonster.ToString() + " point/s.");
Console.WriteLine("The " + _currentMonster.Name + " has " + _currentMonster.CurrentHitPoints + " HP left.");
Console.Write("[Enter] to continue...");
Console.ReadKey();
Console.WriteLine();
return effectiveDamageToMonster;
}
🎮 RPG Mechanics
Class
The player can chose different classes at the start of the game with different modifiers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void BardClassModifier()
{
newPlayer.CurrentHitPoints = 10;
newPlayer.MaximumHitPoints = 10;
newPlayer.ClassMinimumDamage = 0;
newPlayer.ClassMaximumDamage = 1;
newPlayer.ClassMinimumProtection = 0;
newPlayer.ClassMaximumProtection = 2;
}
private void PaladinClassModifier()
{
newPlayer.CurrentHitPoints = 20;
newPlayer.MaximumHitPoints = 20;
newPlayer.ClassMinimumDamage = 0;
newPlayer.ClassMaximumDamage = 2;
newPlayer.ClassMinimumProtection = 1;
newPlayer.ClassMaximumProtection = 3;
}
private void MageClassModifier()
{
newPlayer.CurrentHitPoints = 15;
newPlayer.MaximumHitPoints = 15;
newPlayer.ClassMinimumDamage = 0;
newPlayer.ClassMaximumDamage = 2;
newPlayer.ClassMinimumProtection = 0;
newPlayer.ClassMaximumProtection = 2;
}
private void WarriorClassModifier()
{
newPlayer.CurrentHitPoints = 20;
newPlayer.MaximumHitPoints = 20;
newPlayer.ClassMinimumDamage = 0;
newPlayer.ClassMaximumDamage = 2;
newPlayer.ClassMinimumProtection = 1;
newPlayer.ClassMaximumProtection = 3;
}
Character Progression
For the progression of the character I chose to base it on armor and weapon equipped.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Weapon : Item
{
public int MinimumDamage { get; set; }
public int MaximumDamage { get; set; }
/// <summary>
/// Constructor of Weapon class
/// </summary>
/// <param name="id">ID of the weapon</param>
/// <param name="name">Name of the weapon</param>
/// <param name="namePlural">Name plural of the weapon</param>
/// <param name="price">Price of the weapon</param>
/// <param name="minimumDamage">Minimum damage of the weapon</param>
/// <param name="maximumDamage">Maximum Damage of the weapon</param>
/// <param name="isWeapon">True</param>
/// <param name="isArmor">False</param>
/// <param name="isHealingPotion">False</param>
public Weapon(int id,
string name,
string namePlural,
float price,
int minimumDamage,
int maximumDamage,
bool isWeapon = true,
bool isArmor = false,
bool isHealingPotion = false) : base(id, name, namePlural, price, isWeapon, isArmor, isHealingPotion)
{
MinimumDamage = minimumDamage;
MaximumDamage = maximumDamage;
IsArmor = isArmor;
IsWeapon = isWeapon;
IsHealingPotion = isHealingPotion;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Armor : Item
{
public int MinimumProtection { get; set; }
public int MaximumProtection { get; set; }
/// <summary>
/// Armor class constructor
/// </summary>
/// <param name="id">Armor ID</param>
/// <param name="name">Armor Name</param>
/// <param name="namePlural">Armor Name Plural</param>
/// <param name="price">Armor Price</param>
/// <param name="minimumProtection">Armor Minimum Protection</param>
/// <param name="maximumProtection">Armor Maximum Protection</param>
/// <param name="isWeapon">Bool set to false</param>
/// <param name="isArmor">Bool set to true</param>
/// <param name="isHealingPotion">Bool set to false</param>
public Armor(int id,
string name,
string namePlural,
float price,
int minimumProtection,
int maximumProtection,
bool isWeapon = false,
bool isArmor = true,
bool isHealingPotion = false) : base(id, name, namePlural, price, isWeapon, isArmor, isHealingPotion)
{
MinimumProtection = minimumProtection;
MaximumProtection = maximumProtection;
IsWeapon = isWeapon;
IsArmor = isArmor;
}
}
Inventory Management
At the beginning of the game all the weapons, armors, and healing potions are created and added to the player inventory inside of the main class creating new lists.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private static void NewGame()
{
_weapons = new List<Weapon>();
_armors = new List<Armor>();
_healingPotions = new List<HealingPotion>();
_character = new Character();
Console.Clear();
Console.WriteLine(miniDungeonText);
//Set the player to the initial location
_character.newPlayer.CurrentLocation = World.LocationByID(World.LOCATION_ID_HOME);
//Add to player inventory the first 2 basic piece: a weapon and an armor
_character.newPlayer.Inventory.Add(new InventoryItem(World.ItemByID(World.ITEM_ID_THE_MIGHTY_WOODEN_STICK), 1));
_character.newPlayer.Inventory.Add(new InventoryItem(World.ItemByID(World.ITEM_ID_THE_MIGHTY_USELESS_CLOTHES), 1));
_character.newPlayer.Inventory.Add(new InventoryItem(World.ItemByID(World.ITEM_ID_HEALING_POTION), 1));
Intro();
AddWeaponsToWeaponList(_weapons);
AddArmorsToArmorList(_armors);
AddHealingPotionsToHealingPotionList(_healingPotions);
RemoveWeaponsFromPlayerInventory();
RemoveArmorsFromPlayerInventory();
RemoveHealingPotionsFromPlayerInventory();
_ = new MainM();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/// <summary>
/// Check if the inventory item is a weapon and it's adding the weapon to the weapon list
/// </summary>
/// <param name="_weapons">List of weapons</param>
private static void AddWeaponsToWeaponList(List<Weapon> _weapons)
{
foreach (InventoryItem ii in _character.newPlayer.Inventory)
{
if (ii.Details.IsWeapon)
{
_weapons.Add((Weapon)ii.Details);
}
}
}
/// <summary>
/// Check if the inventory item is an armor and it's adding the armor to the armor list
/// </summary>
/// <param name="_armors">List of armors</param>
private static void AddArmorsToArmorList(List<Armor> _armors)
{
foreach (InventoryItem ii in _character.newPlayer.Inventory)
{
if (ii.Details.IsArmor)
{
_armors.Add((Armor)ii.Details);
}
}
}
/// <summary>
/// Check if the inventory item is a healing potion and it's adding the healing potion to
/// the healing potions list
/// </summary>
/// <param name="_healingPotions">List of healing potions</param>
private static void AddHealingPotionsToHealingPotionList(List<HealingPotion> _healingPotions)
{
foreach (InventoryItem ii in _character.newPlayer.Inventory)
{
if (ii.Details.IsHealingPotion)
{
_healingPotions.Add((HealingPotion)ii.Details);
}
}
}
Inventory Management
The inventory is displayed on screen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
private static void ShowInventory()
{
Console.WriteLine("*** WEAPONS ***");
Console.WriteLine();
ShowWeapons();
Console.WriteLine();
Console.WriteLine("*** ARMORS ***");
Console.WriteLine();
ShowArmors();
Console.WriteLine();
Console.WriteLine("*** POTIONS ***");
Console.WriteLine();
ShowHealingPotions();
Console.WriteLine();
Console.WriteLine("*** ITEMS ****");
Console.WriteLine();
ShowInventoryItem();
}
/// <summary>
/// Show the stats of every weapon in weapon list
/// </summary>
private static void ShowWeapons()
{
if (_weapons.Count != 0)
{
foreach (Weapon w in _weapons)
{
Console.WriteLine("==================");
Console.WriteLine("Name: " + w.Name);
Console.WriteLine("ID: " + w.ID.ToString());
Console.WriteLine("Min Damage: " + w.MinimumDamage);
Console.WriteLine("Max Damage: " + w.MaximumDamage);
Console.WriteLine("Value: " + w.Price + " gold");
Console.WriteLine("==================");
Console.WriteLine();
}
}
else
{
Console.WriteLine("There are no weapons in the inventory!");
}
}
/// <summary>
/// Shows stats of every armor in armor list
/// </summary>
private static void ShowArmors()
{
if (_armors.Count != 0)
{
foreach (Armor a in _armors)
{
Console.WriteLine("==================");
Console.WriteLine("Name: " + a.Name);
Console.WriteLine("ID: " + a.ID.ToString());
Console.WriteLine("Min Protection: " + a.MinimumProtection);
Console.WriteLine("Max Protection: " + a.MaximumProtection);
Console.WriteLine("Value: " + a.Price + " gold");
Console.WriteLine("==================");
Console.WriteLine();
}
}
else
{
Console.WriteLine("There are no armors in the inventory!");
}
}
/// <summary>
/// Shows healing potion in healing potion list
/// </summary>
private static void ShowHealingPotions()
{
if (_healingPotions.Count != 0)
{
foreach (HealingPotion hp in _healingPotions)
{
Console.WriteLine("=================");
Console.WriteLine("Name: " + hp.Name);
Console.WriteLine("ID:" + hp.ID);
Console.WriteLine("Heals " + hp.AmountToHeal + " HP");
Console.WriteLine("Value: " + hp.Price);
Console.WriteLine("=================");
}
}
else
{
Console.WriteLine("There are no potions in your inventory!");
}
}
/// <summary>
/// Show normal inventory items (quest items, keys)
/// </summary>
private static void ShowInventoryItem()
{
if (_character.newPlayer.Inventory.Count != 0)
{
foreach (InventoryItem ii in _character.newPlayer.Inventory)
{
Console.WriteLine("=================");
Console.WriteLine("Name: " + ii.Details.Name);
Console.WriteLine("Quantity: " + ii.Quantity);
Console.WriteLine("Value: " + ii.Details.Price);
Console.WriteLine("=================");
}
}
else
{
Console.WriteLine("There no keys or regular items in your inventory!");
}
}
Inside of the Inventory Menu, the player can equip a weapon, armor or drink a healing potion. The dropping items logic it is not completed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
private static void InventoryMenu()
{
string playerInput;
Console.Clear();
Console.WriteLine(miniDungeonText);
Console.WriteLine(inventoryText);
Console.WriteLine();
ShowInventory();
Console.WriteLine();
Console.WriteLine("========================================");
Console.WriteLine("| Equip (W)eapon | Equip (A)rmor|");
Console.WriteLine("| (D)rop (W)eapon | (D)rop (A)rmor|");
Console.WriteLine("| Drink (H)eling Potion|");
Console.WriteLine("=======================================");
Console.WriteLine("| (B)ack | ");
Console.Write(":> ");
playerInput = Console.ReadLine().ToLower();
if (playerInput == "w" || playerInput == "equip weapon" || playerInput == "weapon")
{
EquipWeapon();
}
else if (playerInput == "a" || playerInput == "equip armor" || playerInput == "armor")
{
EquipArmor();
}
else if (playerInput == "dw" || playerInput == "drop weapon")
{
// This is the method to drop the weapons from the inventory. Still work in process
//DropWeapon();
}
else if (playerInput == "da" || playerInput == "drop armor")
{
// This is the method to drop the armors from the inventory. Still work in process
//DropArmor();
}
else if (playerInput == "h" || playerInput == "drink" || playerInput == "drink healing potion")
{
DrinkHealingPotion();
}
else if (playerInput == "b" || playerInput == "back")
{
return;
}
else
{
InvalidInput();
}
}
Equip weapon is adding to the character the modifiers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static void EquipWeapon()
{
int playerInput = 0;
Console.WriteLine("Insert Weapon ID:");
Console.Write(":> ");
while (!int.TryParse(Console.ReadLine(), out playerInput))
{
Console.WriteLine("Invalid input! Try again!");
Console.WriteLine("Insert Weapon ID:");
Console.Write(":> ");
}
foreach (Weapon w in _weapons)
{
if (playerInput == w.ID)
{
_character.newPlayer.MaximumDamage = _character.newPlayer.ClassMaximumDamage + w.MaximumDamage;
_character.newPlayer.MinimumDamage = _character.newPlayer.ClassMinimumDamage + w.MinimumDamage;
_character.newPlayer.CurrentWeapon = w.Name;
}
}
}
📝 Text-based Interface
Immersive Descriptions
The interface, as said before, is text-based creating a classic fantasy atmosphere.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static void Intro()
{
Console.WriteLine();
Console.WriteLine("Narrator: A long time ago Compass house was an happy Town where people came just ");
Console.WriteLine(" to spend their holidays and enjoy their life out of the busy life in Cambridge City.");
Console.Write("[ENTER] to continue...");
Console.ReadKey();
Console.WriteLine("Narrator: Our Mini Dungeon, made by our Ancient Lords, was attracting also a lot of people.");
Console.ReadKey();
Console.WriteLine("Narrator: You could explore the darkest parts of the dungeon without being bothered to any creature.");
Console.ReadKey();
Console.WriteLine("Narrator: Yes, we used for centuries the Mini dungeon as a Museum.");
Console.ReadKey();
Console.WriteLine("Narrator: We were rich and we were living the best of our life with everybody.");
Console.ReadKey();
Console.WriteLine("Narrator: One day a Lord, Viktor One Foot, from The Red Castle, came in Compass House ");
Console.WriteLine(" with his guards: Oliver The Chosen One and Luke The Gentle Giant.");
Console.ReadKey();
Console.WriteLine("Narrator: They were really friendly and they offered improvment for our Mini Dungeon with amazing technologies.");
Console.ReadKey();
Console.WriteLine("Narrator: They were talking about blueprints, textures and a graphic engine that supposed ");
Console.WriteLine(" to increase the income of our small town.");
Console.ReadKey();
Console.WriteLine("Narrator: I have no idea what they were talking about, to be honest.");
Console.ReadKey();
Console.WriteLine("Narrator: We were desperate because Cambridge Town was taking over all the land ");
Console.WriteLine(" that surrounded us and we needed help.");
Console.ReadKey();
Console.WriteLine("Narrator: What a mistake. We trusted them and we gave them the Mini Dungeon for refurbishment.");
Console.ReadKey();
Console.WriteLine("Narrator: Yes, they refurbished but they call their troupes for help: a bunch of ");
Console.WriteLine(" goblin and troll that now infesting the forest and the Ancient Lake.....");
Console.ReadKey();
Console.WriteLine("Narrator: Ah wait, Viktor changed even the name of our sacred places.");
Console.WriteLine(" It was Ancient Lake now is called Red Lake.");
Console.ReadKey();
Console.WriteLine("Narrator: Now Viktor is sitting on the throne inside of the Mini Dungeon.");
Console.ReadKey();
Console.WriteLine("Narrator: Some rumors saying that he's locked inside, eating and playing a game: someone said that is called New World ");
Console.WriteLine(" other people talking about Dyson Sphere Program! What the heck is going on in the Mini Dungeon?");
Console.WriteLine("Narrator: Take this weapon and this clothes, maybe they will help you during your journey.");
Console.WriteLine(" Don't forget to equip them from your inventory!");
Console.ReadKey();
Console.Clear();
Console.WriteLine(miniDungeonText);
Console.WriteLine();
Console.WriteLine("Weapon acquired: The Mighty Wooden Stick");
Console.WriteLine("Armor acquired: The Mighty Useless Clothes");
Console.WriteLine();
Console.Write("[ENTER] to continue...");
Console.ReadKey();
Console.Clear();
}
Clear Command System
Intuitive text commands for player interactions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private static void ExploreMenu()
{
string playerInput;
bool exploreMenuLoop = true;
while (exploreMenuLoop)
{
Console.Clear();
Console.WriteLine(miniDungeonText);
Console.WriteLine();
Console.WriteLine("*** CURRENT LOCATION ***");
Console.WriteLine(" =>" + _character.newPlayer.CurrentLocation.Name.ToString());
Console.WriteLine();
Console.WriteLine("*** INFO LOCATION ***");
Console.WriteLine("<-------------------------------------------------->");
NPCLivingHereChecker(_character.newPlayer.CurrentLocation);
Console.WriteLine();
MonsterLivingHereChecker(_character.newPlayer.CurrentLocation);
Console.WriteLine("<-------------------------------------------------->");
Console.WriteLine();
Console.WriteLine("===================================");
Console.WriteLine("| (T)alk | Show (P)layer|");
Console.WriteLine("| (E)ngage fight | (I)nventory |");
Console.WriteLine("===================================");
Console.WriteLine("| (B)ack | | (O)ptions |");
Console.WriteLine();
Console.WriteLine();
Console.Write(":> ");
playerInput = Console.ReadLine();
if (playerInput == "t" || playerInput == "talk")
{
QuestChecker(_character.newPlayer.CurrentLocation);
exploreMenuLoop = false;
}
else if (playerInput == "e" || playerInput == "engage" || playerInput == "fight" || playerInput == "engage fight")
{
if (_character.newPlayer.CurrentLocation.MonsterLivingHere != null)
{
_ = new EngageFightM();
}
else
{
return;
}
}
else if (playerInput == "i" || playerInput == "inventory")
{
_ = new InventoryM();
}
else if (playerInput == "p" || playerInput == "player" || playerInput == "show player")
{
ShowPlayer();
}
else if (playerInput == "o" || playerInput == "options")
{
//OptionsMenu();
}
else if (playerInput == "b" || playerInput == "back")
{
return;
}
else
{
InvalidInput();
}
}
}
ASCII Art Elements
Visual Visual enhancements using text characters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private static string welcomeText = @"
▒█░░▒█ █▀▀ █░░ █▀▀ █▀▀█ █▀▄▀█ █▀▀ ▀▀█▀▀ █▀▀█
▒█▒█▒█ █▀▀ █░░ █░░ █░░█ █░▀░█ █▀▀ ░░█░░ █░░█
▒█▄▀▄█ ▀▀▀ ▀▀▀ ▀▀▀ ▀▀▀▀ ▀░░░▀ ▀▀▀ ░░▀░░ ▀▀▀▀";
private static string miniDungeonText = @"
███╗░░░███╗██╗███╗░░██╗██╗ ██████╗░██╗░░░██╗███╗░░██╗░██████╗░███████╗░█████╗░███╗░░██╗
████╗░████║██║████╗░██║██║ ██╔══██╗██║░░░██║████╗░██║██╔════╝░██╔════╝██╔══██╗████╗░██║
██╔████╔██║██║██╔██╗██║██║ ██║░░██║██║░░░██║██╔██╗██║██║░░██╗░█████╗░░██║░░██║██╔██╗██║
██║╚██╔╝██║██║██║╚████║██║ ██║░░██║██║░░░██║██║╚████║██║░░╚██╗██╔══╝░░██║░░██║██║╚████║
██║░╚═╝░██║██║██║░╚███║██║ ██████╔╝╚██████╔╝██║░╚███║╚██████╔╝███████╗╚█████╔╝██║░╚███║
╚═╝░░░░░╚═╝╚═╝╚═╝░░╚══╝╚═╝ ╚═════╝░░╚═════╝░╚═╝░░╚══╝░╚═════╝░╚══════╝░╚════╝░╚═╝░░╚══╝";
private static string fightText = @"
███████████████▀███████████
█▄─▄▄─█▄─▄█─▄▄▄▄█─█─█─▄─▄─█
██─▄████─██─██▄─█─▄─███─███
▀▄▄▄▀▀▀▄▄▄▀▄▄▄▄▄▀▄▀▄▀▀▄▄▄▀▀";
private static string victoryText = @"
██╗░░░██╗██╗░█████╗░████████╗░█████╗░██████╗░██╗░░░██╗
██║░░░██║██║██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗╚██╗░██╔╝
╚██╗░██╔╝██║██║░░╚═╝░░░██║░░░██║░░██║██████╔╝░╚████╔╝░
░╚████╔╝░██║██║░░██╗░░░██║░░░██║░░██║██╔══██╗░░╚██╔╝░░
░░╚██╔╝░░██║╚█████╔╝░░░██║░░░╚█████╔╝██║░░██║░░░██║░░░
░░░╚═╝░░░╚═╝░╚════╝░░░░╚═╝░░░░╚════╝░╚═╝░░╚═╝░░░╚═╝░░░";
private static string deadText = @"
██╗░░░██╗░█████╗░██╗░░░██╗ ░█████╗░██████╗░███████╗ ██████╗░███████╗░█████╗░██████╗░
╚██╗░██╔╝██╔══██╗██║░░░██║ ██╔══██╗██╔══██╗██╔════╝ ██╔══██╗██╔════╝██╔══██╗██╔══██╗
░╚████╔╝░██║░░██║██║░░░██║ ███████║██████╔╝█████╗░░ ██║░░██║█████╗░░███████║██║░░██║
░░╚██╔╝░░██║░░██║██║░░░██║ ██╔══██║██╔══██╗██╔══╝░░ ██║░░██║██╔══╝░░██╔══██║██║░░██║
░░░██║░░░╚█████╔╝╚██████╔╝ ██║░░██║██║░░██║███████╗ ██████╔╝███████╗██║░░██║██████╔╝
░░░╚═╝░░░░╚════╝░░╚═════╝░ ╚═╝░░╚═╝╚═╝░░╚═╝╚══════╝ ╚═════╝░╚══════╝╚═╝░░╚═╝╚═════╝░";
private static string questBookText = @"
█▀█ █░█ █▀▀ █▀ ▀█▀ █▄▄ █▀█ █▀█ █▄▀
▀▀█ █▄█ ██▄ ▄█ ░█░ █▄█ █▄█ █▄█ █░█";
private static string inventoryText = @"
█ █▄░█ █░█ █▀▀ ▀█▀ █▀█ █▀█ █▄█
█ █░▀█ ▀▄▀ ██▄ ░█░ █▄█ █▀▄ ░█░";
private static string gameOverText = @"████████╗██╗░░██╗░█████╗░███╗░░██╗██╗░░██╗ ██╗░░░██╗░█████╗░██╗░░░██╗ ███████╗░█████╗░██████╗░
╚══██╔══╝██║░░██║██╔══██╗████╗░██║██║░██╔╝ ╚██╗░██╔╝██╔══██╗██║░░░██║ ██╔════╝██╔══██╗██╔══██╗
░░░██║░░░███████║███████║██╔██╗██║█████═╝░ ░╚████╔╝░██║░░██║██║░░░██║ █████╗░░██║░░██║██████╔╝
░░░██║░░░██╔══██║██╔══██║██║╚████║██╔═██╗░ ░░╚██╔╝░░██║░░██║██║░░░██║ ██╔══╝░░██║░░██║██╔══██╗
░░░██║░░░██║░░██║██║░░██║██║░╚███║██║░╚██╗ ░░░██║░░░╚█████╔╝╚██████╔╝ ██║░░░░░╚█████╔╝██║░░██║
░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚═╝╚═╝░░╚══╝╚═╝░░╚═╝ ░░░╚═╝░░░░╚════╝░░╚═════╝░ ╚═╝░░░░░░╚════╝░╚═╝░░╚═╝
██████╗░██╗░░░░░░█████╗░██╗░░░██╗██╗███╗░░██╗░██████╗░
██╔══██╗██║░░░░░██╔══██╗╚██╗░██╔╝██║████╗░██║██╔════╝░
██████╔╝██║░░░░░███████║░╚████╔╝░██║██╔██╗██║██║░░██╗░
██╔═══╝░██║░░░░░██╔══██║░░╚██╔╝░░██║██║╚████║██║░░╚██╗
██║░░░░░███████╗██║░░██║░░░██║░░░██║██║░╚███║╚██████╔╝
╚═╝░░░░░╚══════╝╚═╝░░╚═╝░░░╚═╝░░░╚═╝╚═╝░░╚══╝░╚═════╝░";
Technical Implementation
Core Technologies
- Language: C#
- Development Environment: Visual Studio
- Architecture: Object-oriented design with clean code
Key Classes & Systems
Main class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static void Main(string[] args)
{
bool mainLoop = true;
int playerChoice = 0;
//The starting screen of the game
Console.WriteLine(welcomeText);
Console.WriteLine(miniDungeonText);
Console.WriteLine("");
Console.WriteLine("==================");
Console.WriteLine("| 1) New Game |");
Console.WriteLine("| 2) Credits |");
Console.WriteLine("| 3) Quit Game |");
Console.WriteLine("==================");
Console.WriteLine("");
Console.Write(":> ");
while (mainLoop)
{
while (!int.TryParse(Console.ReadLine(), out playerChoice))
{
Console.WriteLine("Invalid choice! Try again!");
Console.WriteLine(":> ");
}
switch (playerChoice)
{
case 1:
NewGame();
break;
case 2:
Console.WriteLine("Not implemented yet!");
Console.WriteLine("");
Console.WriteLine("==================");
Console.WriteLine("| 1) New Game |");
Console.WriteLine("| 2) Credits |");
Console.WriteLine("| 3) Quit Game |");
Console.WriteLine("==================");
Console.WriteLine("");
Console.Write(":> ");
break;
case 3:
Console.WriteLine("Thank you for playing this small game! Hope that you enjoyed it!");
mainLoop = false;
break;
default:
break;
}
}
}
Player Character System
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public enum PlayerRace
{
Human = 1,
Elf,
Dwarf,
Orc
}
/// <summary>
/// Enum Player Class
/// </summary>
public enum PlayerClass
{
Warrior = 1,
Mage,
Paladin,
Bard
}
/// <summary>
/// Class Player
/// </summary>
public class Player : LifeForm
{
public string PlayerName { get; set; }
public PlayerRace playerRace { get; set; }
public PlayerClass playerClass { get; set; }
public string CurrentWeapon { get; set; }
public string CurrentArmor { get; set; }
public int ClassMinimumDamage { get; set; }
public int ClassMaximumDamage { get; set; }
public int ClassMinimumProtection { get; set; }
public int ClassMaximumProtection { get; set; }
public float Gold{ get; set; }
public int ExperiencePoints { get; set; }
public int Level { get; set; }
public Location CurrentLocation { get; set; }
public List<InventoryItem> Inventory { get; set; }
public List<PlayerQuest> Quests { get; set; }
/// <summary>
/// Constructor of Player Class
/// </summary>
/// <param name="currentHitPoints">Current HP</param>
/// <param name="maximumHitPoints">Max HP</param>
/// <param name="minimumDamage">Minimum Damage with weapon stats applied</param>
/// <param name="maximumDamage">Maximum Damage with weapon stats applied</param>
/// <param name="minimumProtection">Minimum Protection with armor stats applied</param>
/// <param name="maximumProtection">Minimum Damage with weapon stats applied</param>
/// <param name="currentWeapon">Current Weapon equipped</param>
/// <param name="currentArmor">Current Armor equipped</param>
/// <param name="classMinimumDamage">Base Minimum Damage with no weapon equipped</param>
/// <param name="classMaximumDamage">Base Maximum Damage with no weapon equipped</param>
/// <param name="classMinimumProtection">Base Minimum Damage</param>
/// <param name="classMaximumProtection">Base Maximum Damage</param>
/// <param name="gold">Money bag of the player</param>
/// <param name="experiencePoints">Current experience points</param>
/// <param name="level"></param>
public Player(int currentHitPoints,
int maximumHitPoints,
int minimumDamage,
int maximumDamage,
int minimumProtection,
int maximumProtection,
string currentWeapon,
string currentArmor,
int classMinimumDamage,
int classMaximumDamage,
int classMinimumProtection,
int classMaximumProtection,
float gold,
int experiencePoints,
int level
) : base(minimumDamage,maximumDamage,minimumProtection,maximumProtection,currentHitPoints, maximumHitPoints)
{
Inventory = new List<InventoryItem>();
Quests = new List<PlayerQuest>();
CurrentWeapon = currentWeapon;
CurrentArmor = currentArmor;
ClassMinimumDamage = classMinimumDamage;
ClassMaximumDamage = classMaximumDamage;
ClassMinimumProtection = classMinimumProtection;
ClassMaximumProtection = classMaximumProtection;
Gold = gold;
ExperiencePoints = experiencePoints;
Level = level;
}
}
World Map & Dungeon Map
The world of this small game is created using a static class. All the items, weapons, armors, NPCs, monsters, quests, and locations are assigned with different IDs to make the scalability of this game more efficient and comprehensive. Also the class is creating the lists for each of the element in game.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public static readonly List<Item> Items = new List<Item>();
public static readonly List<Weapon> Weapons = new List<Weapon>();
public static readonly List<Armor> Armors = new List<Armor>();
public static readonly List<HealingPotion> HealingPotions = new List<HealingPotion>();
public static readonly List<Monster> Monsters = new List<Monster>();
public static readonly List<NPC> NPCs = new List<NPC>();
public static readonly List<Quest> Quests = new List<Quest>();
public static readonly List<Location> Locations = new List<Location>();
public const int ITEM_ID_RUSTY_SWORD = 1;
public const int ITEM_ID_THE_MIGHTY_SWORD_OF_THE_VOID = 2;
public const int ITEM_ID_THE_WAND_OF_FIRE = 3;
public const int ITEM_ID_THE_HAMMER_OF_SUNSHINE = 4;
public const int ITEM_ID_THE_GUITAR_SLAYER = 5;
public const int ITEM_ID_CLUB = 6;
public const int ITEM_ID_THE_MIGHTY_WOODEN_STICK = 7;
public const int ITEM_ID_THE_CHESTPLATE_OF_THE_VOID = 8;
public const int ITEM_ID_THE_ROBE_OF_THE_HOPELESS = 9;
public const int ITEM_ID_THE_CHESTPLATE_OF_SUNSHINE = 10;
public const int ITEM_ID_THE_LIGHT_CHESTPLATE_OF_CATARINA = 11;
public const int ITEM_ID_THE_MIGHTY_USELESS_CLOTHES = 12;
public const int ITEM_ID_GOBLIN_EAR = 13;
public const int ITEM_ID_TROLL_EYE = 14;
public const int ITEM_ID_HEALING_POTION = 15;
public const int ITEM_ID_OLIVER_PONYTAIL = 16;
public const int ITEM_ID_OLIVER_BRAIN = 17;
public const int ITEM_ID_LUKE_BEARD = 18;
public const int ITEM_ID_LUKE_MASK = 19;
public const int ITEM_ID_VIKTOR_GLASSES = 20;
public const int ITEM_ID_VIKTOR_DOUBLE_MASK = 21;
public const int ITEM_ID_VIKTOR_GAMEPASS = 22;
public const int ITEM_ID_VIKTOR_BROKEN_TOE = 23;
public const int ITEM_ID_MINI_DUNGEON_KEY = 24;
public const int ITEM_ID_THE_CABIN_IN_THE_WOODS_KEY = 25;
public const int ITEM_ID_THE_NEW_WORLD_CROWN = 26;
public const int ITEM_ID_THRONE_ROOM_KEY = 27;
public const int ITEM_ID_SOURDOUGH_BREAD = 28;
public const int ITEM_ID_BIG_HEALING_POTION = 29;
public const int ITEM_ID_THE_SPELLBOOK_OF_REPAIR = 30;
public const int MONSTER_ID_GOBLIN = 1;
public const int MONSTER_ID_TROLL = 2;
public const int MONSTER_ID_OLIVER = 3;
public const int MONSTER_ID_LUKE = 4;
public const int MONSTER_ID_VIKTOR = 5;
public const int MONSTER_ID_KING_GOBLIN = 6;
public const int MONSTER_ID_KING_TROLL = 7;
public const int NPC_ID_IAN = 1;
public const int NPC_ID_INNKEEPER = 2;
public const int NPC_ID_THE_HERMIT_IN_THE_WOODS = 3;
public const int NPC_ID_CENK_THE_NERD_SHOPKEEPER = 4;
public const int QUEST_ID_CLEAR_THE_FOREST = 1;
public const int QUEST_ID_ANTIDOTE = 2;
public const int QUEST_ID_KILL_VIKTOR = 3;
public const int QUEST_ID_THE_SPELL_BOOK_OF_REPAIR = 4;
public const int LOCATION_ID_HOME = 1;
public const int LOCATION_ID_COMPASS_HOUSE = 2;
public const int LOCATION_ID_THE_NERD_SHRINE = 3;
public const int LOCATION_ID_THE_WHITE_HORSE_TAVERN = 4;
public const int LOCATION_ID_IAN_HOUSE = 5;
public const int LOCATION_ID_THE_RED_FOREST = 6;
public const int LOCATION_ID_THE_CABIN_IN_THE_WOODS = 7;
public const int LOCATION_ID_THE_RED_LAKE = 8;
public const int LOCATION_ID_MINI_DUNGEON_ENTRANCE = 9;
public const int LOCATION_ID_MINI_DUNGEON_FIRST_ROOM = 10;
public const int LOCATION_ID_MINI_DUNGEON_SECOND_ROOM = 11;
public const int LOCATION_ID_MINI_DUNGEON_THIRD_ROOM = 12;
public const int LOCATION_ID_MINI_DUNGEON_THRONE_ROOM = 13;
Populte The Item lists
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private static void PopulateItems()
{
Items.Add(new Weapon(ITEM_ID_RUSTY_SWORD, "Rusty sword", "Rusty Swords", 2.0f, 1, 3));
Items.Add(new Weapon(ITEM_ID_CLUB, "Club", "Clubs", 2.0f, 1, 5));
Items.Add(new Weapon(ITEM_ID_THE_MIGHTY_WOODEN_STICK, "The Mighty Wooden Stick", "The Mighty Wooden Stick", 2.0f, 1, 2));
Items.Add(new Weapon(ITEM_ID_THE_MIGHTY_SWORD_OF_THE_VOID, "The Mighty Sword of the Void", "The Maighty Swords of the Void", 50, 2, 10));
Items.Add(new Weapon(ITEM_ID_THE_WAND_OF_FIRE, "The Wand of Fire", "The Wands of Fire", 5.0f, 2, 10));
Items.Add(new Weapon(ITEM_ID_THE_HAMMER_OF_SUNSHINE, "The Hammer of Sunshine", "The Hammers of Sunshine", 5.0f, 2, 10));
Items.Add(new Weapon(ITEM_ID_THE_GUITAR_SLAYER, "The Guitar Slayer", "The Guitars Slayer", 5.0f, 2, 10));
Items.Add(new Armor(ITEM_ID_THE_CHESTPLATE_OF_THE_VOID, "Chestplate of the Void", "Chestplates of the Void", 5.0f, 2, 5));
Items.Add(new Armor(ITEM_ID_THE_ROBE_OF_THE_HOPELESS, "The Robe of the Hopeless", "The Robes of the Hopeless", 5.0f, 1, 4));
Items.Add(new Armor(ITEM_ID_THE_CHESTPLATE_OF_SUNSHINE, "Chestplate of the Sunshine", "Chestplates of the Sunshine", 5.0f, 2, 5));
Items.Add(new Armor(ITEM_ID_THE_LIGHT_CHESTPLATE_OF_CATARINA, "Light Chestplate of Catarina", "Light Chestplates of Catarina", 5.0f, 2, 5));
Items.Add(new Armor(ITEM_ID_THE_MIGHTY_USELESS_CLOTHES, "The Mighty Useless Clothes", "The Mighty Useless Clothes", 2.0f, 0, 2));
Items.Add(new Item(ITEM_ID_GOBLIN_EAR, "Globlin Ear", "Globlin Ears", 1.0f));
Items.Add(new Item(ITEM_ID_TROLL_EYE, "Troll Eye", "Troll Eyes", 2.0f));
Items.Add(new Item(ITEM_ID_THE_NEW_WORLD_CROWN, "New World Crown", null, 0));
Items.Add(new Item(ITEM_ID_LUKE_BEARD, "Luke Beard", null, 40.0f));
Items.Add(new Item(ITEM_ID_LUKE_MASK, "Luke Mask", null, 80.0f));
Items.Add(new Item(ITEM_ID_OLIVER_PONYTAIL, "Oliver Ponytail", null, 45.0f));
Items.Add(new Item(ITEM_ID_OLIVER_BRAIN, "Oliver Brain", null, 85.0f));
Items.Add(new Item(ITEM_ID_VIKTOR_GLASSES, "Viktor Glasses", null, 60.0f));
Items.Add(new Item(ITEM_ID_VIKTOR_DOUBLE_MASK, "Viktor Double Mask", null, 100.0f));
Items.Add(new Item(ITEM_ID_VIKTOR_GLASSES, "Viktor GamePass", null, 150.0f));
Items.Add(new Item(ITEM_ID_VIKTOR_BROKEN_TOE, "Viktor Broken Toe", null, 200));
Items.Add(new Item(ITEM_ID_MINI_DUNGEON_KEY, "Dungeon key", null, 50.0f));
Items.Add(new Item(ITEM_ID_THE_CABIN_IN_THE_WOODS_KEY, "The Cabin in the Woods Key", null, 0));
Items.Add(new Item(ITEM_ID_THRONE_ROOM_KEY, "The Throne Room Key", null, 0));
Items.Add(new Item(ITEM_ID_THE_SPELLBOOK_OF_REPAIR, "The Spell Book of Repair", null, 0));
Items.Add(new HealingPotion(ITEM_ID_HEALING_POTION, "Healing Potion", "Healing Potions", 5.0f, 5));
Items.Add(new HealingPotion(ITEM_ID_BIG_HEALING_POTION, "Big Healing Potion", "Big Healing Potions", 10.0f, 10));
Items.Add(new HealingPotion(ITEM_ID_SOURDOUGH_BREAD, "Sourdough Bread", "Healing Potions", 2.0f, 2));
}
Populate Monsters List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/// <summary>
/// This method is populating all the mosters
/// </summary>
private static void PopulateMonsters()
{
Monster goblin = new Monster(MONSTER_ID_GOBLIN, "Goblin", 1, 2, 0, 2, 3, 10, 3, 3);
goblin.LootTable.Add(new LootItem(ItemByID(ITEM_ID_GOBLIN_EAR), 75, true));
goblin.LootTable.Add(new LootItem(ItemByID(ITEM_ID_RUSTY_SWORD), 75, false));
Monster troll = new Monster(MONSTER_ID_TROLL, "Troll", 1, 3, 1, 3, 4, 15, 4, 4);
troll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_TROLL_EYE), 75, true));
troll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_CLUB), 75, false));
Monster kingGoblin = new Monster(MONSTER_ID_KING_GOBLIN, "King Goblin", 2, 3, 1, 3, 4, 15, 5, 5);
kingGoblin.LootTable.Add(new LootItem(ItemByID(ITEM_ID_GOBLIN_EAR), 75, true));
kingGoblin.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_MIGHTY_SWORD_OF_THE_VOID), 75, false));
kingGoblin.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_CHESTPLATE_OF_THE_VOID), 75, false));
Monster kingTroll = new Monster(MONSTER_ID_KING_TROLL, "King Troll", 2, 4, 2, 4, 5, 20, 6, 6);
kingTroll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_TROLL_EYE), 75, true));
kingTroll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_HAMMER_OF_SUNSHINE), 75, false));
kingTroll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_CHESTPLATE_OF_SUNSHINE), 75, false));
kingTroll.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_SPELLBOOK_OF_REPAIR), 100, true));
Monster oliver = new Monster(MONSTER_ID_OLIVER, "Oliver the Chosen One", 2, 4, 2, 4, 5, 20, 5, 5);
oliver.LootTable.Add(new LootItem(ItemByID(ITEM_ID_OLIVER_PONYTAIL), 75, true));
oliver.LootTable.Add(new LootItem(ItemByID(ITEM_ID_OLIVER_BRAIN), 25, false));
oliver.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_ROBE_OF_THE_HOPELESS), 75, false));
oliver.LootTable.Add(new LootItem(ItemByID(ITEM_ID_MINI_DUNGEON_KEY), 100, true));
Monster luke = new Monster(MONSTER_ID_LUKE, "Luke The Gentle Giant", 3, 5, 3, 6, 6, 25, 7, 7);
luke.LootTable.Add(new LootItem(ItemByID(ITEM_ID_LUKE_BEARD), 100, true));
luke.LootTable.Add(new LootItem(ItemByID(ITEM_ID_LUKE_MASK), 25, false));
luke.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_WAND_OF_FIRE), 75, false));
luke.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THE_LIGHT_CHESTPLATE_OF_CATARINA), 75, false));
luke.LootTable.Add(new LootItem(ItemByID(ITEM_ID_THRONE_ROOM_KEY), 100, true));
Monster viktor = new Monster(MONSTER_ID_VIKTOR, "Viktor One Foot", 4, 10, 4, 8, 10, 300, 15, 15);
viktor.LootTable.Add(new LootItem(ItemByID(ITEM_ID_VIKTOR_GLASSES), 75, false));
viktor.LootTable.Add(new LootItem(ItemByID(ITEM_ID_VIKTOR_DOUBLE_MASK), 75, false));
viktor.LootTable.Add(new LootItem(ItemByID(ITEM_ID_VIKTOR_GAMEPASS), 25, false));
viktor.LootTable.Add(new LootItem(ItemByID(ITEM_ID_VIKTOR_BROKEN_TOE), 100, true));
Monsters.Add(goblin);
Monsters.Add(troll);
Monsters.Add(kingGoblin);
Monsters.Add(kingTroll);
Monsters.Add(oliver);
Monsters.Add(luke);
Monsters.Add(viktor);
}
Populate NPCs list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/// <summary>
/// This methos is populating all the NPCs
/// </summary>
private static void PopulateNPC()
{
NPC ian = new NPC(
NPC_ID_IAN,
" Ian, the Alchemist",
" Hello traveler. I'm Ian, the Alchemist.\n I have a job for ya. I'm preparing an antidote for my dog that by\n accident ate snake chocolate.\n I can't leave home right now and I need `3 trolls eye` to complete the\n antidote.\n I saw some trolls just around the Red Lake.\n If you'll help me I'll give you the `key` that open the `Cabin in the\n woods`, just South of the Red Forest and 20 gold.",
"I need the ingredients ASAP or my dog will make a mess for at least 3 days in a row!",
" Thank you for your help!\n This is your reward and good luck for your adventure!",
"I'm quite busy preparing the antidote for my dog! Thank you again for your help! See you around!");
NPC innkeeper = new NPC(
NPC_ID_INNKEEPER,
" James, the innkeeper",
" Are you looking for a job? Fine...Kill the goblins that infesting the\n Red Forest\n and bring back `3 goblins ears`.\n I'll give you an healing potion and, of course, 15 golds!",
"The goblins are still alive? I think it's not a big deal kill\n some annoying goblins, right? Come back with the ears!",
"Hope the goblins didn't give you hard time! This is your reward!",
"All day cleaning already clean glasses. What's the point to have a business in this town?\n FOR THE GLORY!!\n [A glass crash in his hands] I need a medic...");
NPC hermit = new NPC(
NPC_ID_THE_HERMIT_IN_THE_WOODS,
" Dominique, the hermit in the woods",
" Hello traveler.\n The situation inside of Mini Dungeon is getting worse day by day.\n Viktor One Foot is sitting on the throne inside of the last room of\n the dungeon and his command is to take over of our amazing city:\n Compass House.\n Unfortunately the dungeon's infested by goblins, trolls and 2 Viktor's\n guard: `Oliver The Chosen One` and `Luke The Gentle Giant`.\n He needs to be stopped ASAP to finally create our `New World`.\n Bring me Viktor's `toe`.\n Let's say that I'll give you 500 gold for the `toe` and..... you'll\n see! Good Luck!!",
" Where is Viktor's toe? Still on his foot? Go back in the Mini Dungeon and defeat him!!!",
"Congratulation my new Lord! Now we'll rebuild our new world! The Mini Dungeon is now in our hands and you'll be my muppet! Now you're in my dominion! Death and diseases will be upon Compass House, Cambridge Town and all the lands surrounding us.........ehm of course I'm joking. We'll refurbish the Mini Dungeon and we'll open again the museum.",
"Dominique disappeared and he left this message:\n `I'm away for holidays in New Castle`. I'll be back in few years!");
NPC cenk = new NPC(
NPC_ID_CENK_THE_NERD_SHOPKEEPER,
" Cenk, the Nerd Shopkeeper",
" OMG!\n Finally somebody is coming here to help me out.\n The King Troll and The King Goblin raided the shop few days ago.\n They stole everything and trashed the place! I tried to get\n back my staff but, I couldn't defeat the King Troll!\n He was using my stuff! I've seen even regular trolls and goblins\n with armors and weapons from the shop!\n I need you help and in exchange you can keep some of the equipment!\n Bring me back my 'Spell Book of Repair'!",
" I don't want to make you rush but, without the book it will take\n me a decade to repair everything!",
" Thank you so much for your help, traveler",
" Cenk is repairing the shop! Will open soon!");
NPCs.Add(ian);
NPCs.Add(innkeeper);
NPCs.Add(hermit);
NPCs.Add(cenk);
}
Populate Quests list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/// <summary>
/// This method is populating all the quests
/// </summary>
private static void PopulateQuests()
{
Quest clearTheForest = new Quest(
QUEST_ID_CLEAR_THE_FOREST,
" Goblin Genocide",
" The innkeeper in town needs help to clear the Red forest from the\ngoblins.", 20, 15);
clearTheForest.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_GOBLIN_EAR), 3));
clearTheForest.RewardItem = ItemByID(ITEM_ID_HEALING_POTION);
Quest antidote = new Quest(
QUEST_ID_ANTIDOTE,
" The Eye of Madness",
" Ian, The alchemist, asked me to help him with antidote for his dog.", 20, 20);
antidote.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_TROLL_EYE), 3));
antidote.RewardItem = ItemByID(ITEM_ID_THE_CABIN_IN_THE_WOODS_KEY);
Quest killViktor = new Quest(
QUEST_ID_KILL_VIKTOR,
" Kill Viktor One Foot",
" The hermit asked me to kill Viktor that is located in the last\n room inside of Mini Dungeon.", 50, 500);
killViktor.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_VIKTOR_BROKEN_TOE), 1));
killViktor.RewardItem = ItemByID(ITEM_ID_THE_NEW_WORLD_CROWN);
Quest theSpellBookOfRepair = new Quest(
QUEST_ID_THE_SPELL_BOOK_OF_REPAIR,
" Repair never been so easy",
" Cenk needs the Spell Book of Repair to fix the shop! He mentioned the King Troll in the Mini Dungeon!", 50, 500);
theSpellBookOfRepair.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_THE_SPELLBOOK_OF_REPAIR), 1));
theSpellBookOfRepair.RewardItem = ItemByID(ITEM_ID_THE_NEW_WORLD_CROWN);
Quests.Add(clearTheForest);
Quests.Add(antidote);
Quests.Add(killViktor);
Quests.Add(theSpellBookOfRepair);
}
Populate Locations & Dungeon Map list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/// <summary>
/// This method is populating all the locations
/// </summary>
private static void PopulateLocations()
{
Location home = new Location(LOCATION_ID_HOME, "Home", "This is your home. Not the best place ever where\n you can sleep well but, at least, you got a roof\n above your head.");
Location compassHouse = new Location(LOCATION_ID_COMPASS_HOUSE, "Compass House Town", "Our majestic town made by nerds without social\n life. People like to spend most of their time\n programming and eating crisps Tyrell (cheese and\n onions).\n You can still smell the ash produced by our evil\n lord during the last session of Applied Science\n for Games!");
Location theWhiteHorseTavern = new Location(LOCATION_ID_THE_WHITE_HORSE_TAVERN, "The White Horse Tavern", "There is a guy waiting at the counter, cleanig\n really shiny and already clean glasses.\n Quite empty for all day.\n Probably people in town are not interested to\n drink a pint of finest `dirty water`. Ah yeah,\n nerds life!");
theWhiteHorseTavern.NpcLivingHere = NPCbyID(NPC_ID_INNKEEPER);
theWhiteHorseTavern.QuestAvailableHere = QuestbyID(QUEST_ID_CLEAR_THE_FOREST);
Location redForest = new Location(LOCATION_ID_THE_RED_FOREST, "The Red Forest", "In the moment that you stepped in this forest,\n from far away, you can hear goblins screaming\n and swearing.");
redForest.MonsterLivingHere = MonsterByID(MONSTER_ID_GOBLIN);
Location ianHouse = new Location(LOCATION_ID_IAN_HOUSE, "Ian, The Alchemist, house", "This amazing house is made by thousends of\n boardgames in limited edition. You can see even\n a Settles of Catan made by gold with a signature:\n from Viktor");
ianHouse.NpcLivingHere = NPCbyID(NPC_ID_IAN);
ianHouse.QuestAvailableHere = QuestbyID(QUEST_ID_ANTIDOTE);
Location redLake = new Location(LOCATION_ID_THE_RED_LAKE, "The Red Lake", "An amazing lake full of life! Dangerous place\n without any armor!!");
redLake.MonsterLivingHere = MonsterByID(MONSTER_ID_TROLL);
Location nerdShrine = new Location(LOCATION_ID_THE_NERD_SHRINE, "The Nerd Shrine", "This placed is completely trashed!\n The shopkeeper is sitting on a broken barrel\n and he's smiling at you!");
nerdShrine.NpcLivingHere = NPCbyID(NPC_ID_CENK_THE_NERD_SHOPKEEPER);
nerdShrine.QuestAvailableHere = QuestbyID(QUEST_ID_THE_SPELL_BOOK_OF_REPAIR);
// This is part of the shop that I have not finished to implement, indeed, I commenteded it
//Vendor cenk = new Vendor("Cenk, the Nerd Shopkeeper");
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_MIGHTY_SWORD_OF_THE_VOID), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_WAND_OF_FIRE), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_HAMMER_OF_SUNSHINE), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_GUITAR_SLAYER), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_CHESTPLATE_OF_THE_VOID), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_ROBE_OF_THE_HOPELESS), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_CHESTPLATE_OF_SUNSHINE), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_THE_LIGHT_CHESTPLATE_OF_CATARINA), 1);
//cenk.AddItemToVendorInventory(ItemByID(ITEM_ID_HEALING_POTION), 5);
//nerdShrine.VendorWorkingHere = cenk;
Location theCabinInTheWoods = new Location(LOCATION_ID_THE_CABIN_IN_THE_WOODS, "The Cabin in the Woods", "Mysterious cabin in the middle of nowhere.\n Sorrounded by dead trees and a strange timeless\n fog.", ItemByID(ITEM_ID_THE_CABIN_IN_THE_WOODS_KEY));
theCabinInTheWoods.NpcLivingHere = NPCbyID(NPC_ID_THE_HERMIT_IN_THE_WOODS);
theCabinInTheWoods.QuestAvailableHere = QuestbyID(QUEST_ID_KILL_VIKTOR);
Location miniDungeonEntrance = new Location(LOCATION_ID_MINI_DUNGEON_ENTRANCE, "Mini Dungeon Entrance", "The entrance of the Mini Dungeon with a gold\n plated door");
miniDungeonEntrance.MonsterLivingHere = MonsterByID(MONSTER_ID_OLIVER);
Location miniDungeonFirstRoom = new Location(LOCATION_ID_MINI_DUNGEON_FIRST_ROOM, "First Mini Dungeon Room", "Quite dark room without windows.\n You can see something moving in the darkness and\n you can hear .... a fart(?). From the smell is a\n goblin for sure with some sirious belly problems.\n Are Ian dog and this goblin related?", ItemByID(ITEM_ID_MINI_DUNGEON_KEY));
miniDungeonFirstRoom.MonsterLivingHere = MonsterByID(MONSTER_ID_KING_GOBLIN);
Location miniDungeonSecondRoom = new Location(LOCATION_ID_MINI_DUNGEON_SECOND_ROOM, "Second Mini Dungeon Room", "Dark as the previous one. Strange sounds in the\n darkness again!");
miniDungeonSecondRoom.MonsterLivingHere = MonsterByID(MONSTER_ID_KING_TROLL);
Location miniDungeonThirdRoom = new Location(LOCATION_ID_MINI_DUNGEON_THIRD_ROOM, "Third Mini Dungeon Room", "This room smells different. You can hear somebody\n screming!");
miniDungeonThirdRoom.MonsterLivingHere = MonsterByID(MONSTER_ID_LUKE);
Location miniDungeonThroneRoom = new Location(LOCATION_ID_MINI_DUNGEON_THRONE_ROOM, "The Throne Room", "The majestic Throne Room where Viktor One Foot\n manage his guild", ItemByID(ITEM_ID_THRONE_ROOM_KEY));
miniDungeonThroneRoom.MonsterLivingHere = MonsterByID(MONSTER_ID_VIKTOR);
// All the location connected to each other
home.LocationToEast = compassHouse;
home.LocationToWest = redForest;
compassHouse.LocationToNorth = nerdShrine;
compassHouse.LocationToSouth = theWhiteHorseTavern;
compassHouse.LocationToEast = ianHouse;
compassHouse.LocationToWest = home;
theWhiteHorseTavern.LocationToNorth = compassHouse;
ianHouse.LocationToWest = compassHouse;
nerdShrine.LocationToSouth = compassHouse;
redForest.LocationToNorth = miniDungeonEntrance;
redForest.LocationToSouth = theCabinInTheWoods;
redForest.LocationToWest = redLake;
redForest.LocationToEast = home;
theCabinInTheWoods.LocationToNorth = redForest;
redLake.LocationToEast = redForest;
miniDungeonEntrance.LocationToNorth = miniDungeonFirstRoom;
miniDungeonEntrance.LocationToSouth = redForest;
miniDungeonFirstRoom.LocationToEast = miniDungeonSecondRoom;
miniDungeonFirstRoom.LocationToSouth = miniDungeonEntrance;
miniDungeonSecondRoom.LocationToNorth = miniDungeonThirdRoom;
miniDungeonSecondRoom.LocationToWest = miniDungeonFirstRoom;
miniDungeonThirdRoom.LocationToEast = miniDungeonThroneRoom;
miniDungeonThirdRoom.LocationToSouth = miniDungeonSecondRoom;
miniDungeonThroneRoom.LocationToWest = miniDungeonThirdRoom;
Locations.Add(home);
Locations.Add(compassHouse);
Locations.Add(ianHouse);
Locations.Add(nerdShrine);
Locations.Add(theWhiteHorseTavern);
Locations.Add(redForest);
Locations.Add(redLake);
Locations.Add(miniDungeonEntrance);
Locations.Add(miniDungeonFirstRoom);
Locations.Add(miniDungeonSecondRoom);
Locations.Add(miniDungeonThirdRoom);
Locations.Add(miniDungeonThroneRoom);
}
Accessing by IDs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/// <summary>
/// Get the NPCs by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static NPC NPCbyID(int id)
{
foreach (NPC npc in NPCs)
{
if (npc.ID == id)
{
return npc;
}
}
return null;
}
/// <summary>
/// Get the monster by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Monster MonsterByID(int id)
{
foreach (Monster monster in Monsters)
{
if (monster.ID == id)
{
return monster;
}
}
return null; ;
}
/// <summary>
/// Get the quest by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private static Quest QuestbyID(int id)
{
foreach (Quest quest in Quests)
{
if (quest.ID == id)
{
return quest;
}
}
return null; ;
}
/// <summary>
/// Get the items by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Item ItemByID(int id)
{
foreach (Item item in Items)
{
if (item.ID == id)
{
return item;
}
}
return null;
}
/// <summary>
/// Get the locations by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Location LocationByID(int id)
{
foreach (Location location in Locations)
{
if (location.ID == id)
{
return location;
}
}
return null;
}
Technical Challenges & Solutions
Challenge 1: User Input Validation
Problem: Handling various user input formats and preventing crashes
Solution: Created robust input parsing system with error handling and user-friendly feedback
1
2
3
4
5
while (!int.TryParse(Console.ReadLine(), out playerClassInt))
{
Console.WriteLine("Invalid input! Try again!");
Console.Write(":> ");
}
1
2
3
4
5
6
7
8
9
10
11
12
// <summary>
/// Invalid input message
/// </summary>
private static void InvalidInput()
{
Console.Clear();
Console.WriteLine(miniDungeonText);
Console.WriteLine("Invalid Input! Try again!");
Console.Write("[ENTER] to go back");
Console.ReadKey();
Console.Clear();
}
1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// Message for the wrong path
/// </summary>
private static void WrongPathMessage()
{
Console.Clear();
Console.WriteLine(miniDungeonText);
Console.WriteLine("You can't go there!");
Console.Write("[ENTER] to go back");
Console.ReadKey();
Console.Clear();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private bool GetConfirmation(string playerInput)
{
string response;
while (true)
{
Console.WriteLine("Is " + playerInput + " correct? y/n");
Console.Write(":> ");
response = Console.ReadLine().ToLower();
if (response == "y" || response == "yes")
{
return true;
}
else if (response == "n" || response == "no")
{
Console.WriteLine("Insert the correct one");
Console.Write(":> ");
return false;
}
else
{
Console.WriteLine("Invalid input!");
}
}
}
Challenge 2: Combat Balance
Problem: Ensuring fair and engaging combat without being too easy or impossible
Solution: Mathematical balancing of stats and playtesting to fine-tune difficulty progression
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static int CalcEffectiveDamageToMonster(int damageToMonster, int monsterDefense)
{
int effectiveDamageToMonster = damageToMonster - monsterDefense;
if (effectiveDamageToMonster <= 0)
{
effectiveDamageToMonster = 0;
}
else
{
_currentMonster.CurrentHitPoints -= effectiveDamageToMonster;
}
return effectiveDamageToMonster;
}
1
Monster viktor = new Monster(MONSTER_ID_VIKTOR, "Viktor One Foot", 4, 10, 4, 8, 10, 300, 15, 15);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Monster : LifeForm
{
public int ID { get; set; }
public string Name { get; set; }
public int RewardExperiencePoints { get; set; }
public int RewardGold { get; set; }
public List<LootItem> LootTable { get; set; }
/// <summary>
/// Monster class constructor
/// </summary>
/// <param name="id">Monster ID</param>
/// <param name="name">Monster Name</param>
/// <param name="minimumDamage">Monster Minumum Damage</param>
/// <param name="maximumDamage">Monster Maximum Damage</param>
/// <param name="minimumProtection">Monster Minimum Protection</param>
/// <param name="maximumProtection">Monster Maximum Protection</param>
/// <param name="rewardExperiencePoints">After kill the moster this is the reward in experience points</param>
/// <param name="rewardGold">After kill the moster this is the reward in gold</param>
/// <param name="currentHitPoints">Monster Current HP</param>
/// <param name="maximumHitPoints">Monster Maximum HP</param>
public Monster(int id,
string name,
int minimumDamage,
int maximumDamage,
int minimumProtection,
int maximumProtection,
int rewardExperiencePoints,
int rewardGold,
int currentHitPoints,
int maximumHitPoints) : base(minimumDamage, maximumDamage, minimumProtection, maximumProtection, currentHitPoints, maximumHitPoints)
{
ID = id;
Name = name;
//MaximumDamage = maximumDamage;
//MinimumProtection = minimumProtection;
//MaximumProtection = maximumProtection;
RewardExperiencePoints = rewardExperiencePoints;
RewardGold = rewardGold;
LootTable = new List<LootItem>();
}
}
Game Flow & Architecture
Command Processing System
- Input Parser: Converts user commands into game actions
- Command Validation: Ensure commands are valid for the current context
- Action Execution: Performs game actions and update state accordingly
Data Structure Design
1
2
3
4
5
6
7
Game Structure:
├── Static World Class
├── Main Class (Main game loop)
├── Player (Character stats & inventory)
├── Map (Room connections)
├── Combat System (Turn-based fighting)
└── Inventory System (Items & equipment)
Game States
- Main Menu: Game Start
- Exploration: Map navigation and interaction with the environment
- Combat: Turn-based fighting encounters
- Inventory: Item management and player stats
- Game Over: Win/lose conditions
Code Architecture Principles
Design Patterns Applied
- Strategy Pattern: Different enemies with different stats and specific droppable loot based on randomised dropping percentage
- Command Pattern: User input procesissing and action execution
- IDs System-based: Everything is based on IDs to facilitate the access and for easier future improvements
Object-Oriented Features
- Encapsulation: Private fields with public properties
- Inheritance: Base classes for Items, Monsters, and Player
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/// <summary> /// Class Item /// </summary> public class Item { public int ID { get; set; } public string Name { get; set; } public string NamePlural { get; set; } public float Price { get; set; } public bool IsWeapon { get; set; } public bool IsArmor { get; set; } public bool IsHealingPotion { get; set; } /// <summary> /// Item class contructor /// </summary> /// <param name="id">Item ID</param> /// <param name="name">Item Name</param> /// <param name="namePlural">Item Name, plural</param> /// <param name="price">Item Price</param> /// <param name="isWeapon">Bool set up to false</param> /// <param name="isArmor">Bool set up to false</param> /// <param name="isHealingPotion">Bool set up to false</param> public Item(int id, string name, string namePlural, float price, bool isWeapon = false, bool isArmor = false, bool isHealingPotion = false) { ID = id; Name = name; NamePlural = namePlural; Price = price; IsWeapon = isWeapon; IsArmor = isArmor; IsHealingPotion = isHealingPotion; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/// <summary> /// Class Life Form /// </summary> public class LifeForm { public int MinimumDamage { get; set; } public int MaximumDamage { get; set; } public int MinimumProtection { get; set; } public int MaximumProtection { get; set; } public int MaximumHitPoints { get; set; } public int CurrentHitPoints { get; set; } /// <summary> /// Constructor of Life Form /// </summary> /// <param name="minimumDamage">Minimum Damage</param> /// <param name="maximumDamage">Maximum Damage</param> /// <param name="minimumProtection">Minimum Protection</param> /// <param name="maximumProtection">Maximum Protection</param> /// <param name="maximumHitPoints">Max HP</param> /// <param name="currentHitPoints">Current HP</param> public LifeForm(int minimumDamage, int maximumDamage, int minimumProtection, int maximumProtection, int maximumHitPoints, int currentHitPoints) { MinimumDamage = minimumDamage; MaximumDamage = maximumDamage; MinimumProtection = minimumProtection; MaximumProtection = maximumProtection; MaximumHitPoints = maximumHitPoints; CurrentHitPoints = currentHitPoints; } }
- Polimorphism: Different item types with shared interfaces
Learning Outcomes
Programming Skills Developed
During the development process I have learnt some important skills.
- C# Fundamentals: solid grasps of language syntax and features
- OOP Principles: Practical application of object-oriented programming
- Problem Solving: Breaking down complex game logic into manageable components
- Code Organization: Structuring code for maintanability and readability
Game Development Insights
- Player Experience: Creating engaging gameplay without graphics
- Game Balance: Mathematical approach to combat and progression systems
- User Interface: Designing intuitive text-based interactions
- Game Flow: Managing transitions between different game states
What I Learnt
Developing MiniDungeon as a text-based game taught me the importance of solid programming fundamentals. Without graphics to hide behind, every system had to be well-designed and clearly implemented. This project strengthened my understanding of:
- Clean Code Practices: Writing readable, maintainable code
- Object-Oriented Design: When and how to use inheritance, encapsulation, and polymorphism
- User Experience: Making complex systems accessible through simple interfaces
- Game Mechanics: How to create engaging gameplay through code logic alone
Technical Specifications
- Language: C#
- Framework: .NET Framework/Core
- Platform: Windows Console
- Development Tools: Visual Studio
- Version Control: Git with GitHub
- Architecture: Object-oriented console application
Future Improvements
If I were to expand this project, I would consider:
- Save System: Game state persistence between sessions
- More Content: Additional rooms, enemies, items, and the shop
- Enhanced Combat: Special abilities and magic systems
- GUI Version: Converting to a graphical interface while maintaining core mechanics
Links
- GitHub Repository View the complete source code
-
Download the game
This project demonstrates my C# programming fundamentals, object-oriented design skills, and ability to create complete game systems using console applications. It showcases clean code practices and thoughtful game design without relying on external graphics or game engines.