Created
May 5, 2021 13:47
-
-
Save MerijnHendriks/570d28c420c4e1ad4b0c0670c71cb413 to your computer and use it in GitHub Desktop.
Simple combat system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace RandomRPG | |
{ | |
public static class Log | |
{ | |
public static void Write(string text) | |
{ | |
Console.WriteLine(text); | |
} | |
public static void Debug(string text) | |
{ | |
Write(text); | |
} | |
} | |
public static class RandomUtil | |
{ | |
private static Random random; | |
static RandomUtil() | |
{ | |
random = new Random(); | |
} | |
public static int Next(int minimum, int maximum) | |
{ | |
return random.Next(minimum, maximum); | |
} | |
public static double Next() | |
{ | |
return random.NextDouble(); | |
} | |
} | |
public enum ERollResult | |
{ | |
Failure, | |
Partial, | |
Success | |
} | |
public enum ECreature | |
{ | |
Player, | |
Goblin | |
} | |
public enum EAttribute | |
{ | |
Strength, | |
Dexterity, | |
Constitution | |
} | |
public class Dice | |
{ | |
public int Count; | |
public int Sides; | |
public Dice(int count, int sides) | |
{ | |
Count = count; | |
Sides = sides; | |
} | |
/// <summary> | |
/// Rolls the default 2x6 dice | |
/// </summary> | |
/// <remarks> | |
/// From dungeon world playbook | |
/// </remarks> | |
/// <returns>Dice result</returns> | |
public static ERollResult RollDefault() | |
{ | |
int count = 2; | |
int sides = 6; | |
int value = 0; | |
for (int i = 0; i < count; i++) | |
{ | |
value += RandomUtil.Next(1, sides); | |
} | |
if (value >= 10) | |
{ | |
return ERollResult.Success; | |
} | |
else if (value >= 6) | |
{ | |
return ERollResult.Partial; | |
} | |
else | |
{ | |
return ERollResult.Failure; | |
} | |
} | |
public static int Roll(int count, int sides) | |
{ | |
int value = 0; | |
for (int i = 0; i < count; i++) | |
{ | |
value += RandomUtil.Next(1, sides + 1); | |
} | |
return value; | |
} | |
public int Roll() | |
{ | |
return Roll(Count, Sides); | |
} | |
} | |
public class Attribute | |
{ | |
public EAttribute AttributeType; | |
public int Value; | |
public Attribute(EAttribute attribute, int value) | |
{ | |
AttributeType = attribute; | |
Value = value; | |
} | |
} | |
public class HitPoints | |
{ | |
public int Current; | |
public int Maximum; | |
public HitPoints(int value) | |
{ | |
Current = value; | |
Maximum = value; | |
} | |
} | |
public class Character | |
{ | |
public string Name; | |
public int Level; | |
public int Experience; | |
public Attribute[] Attributes; | |
public HitPoints HitPoints; | |
public Dice HitDice; | |
public Character(string name, int strength, int dexterity, int constitution, Dice dice, int level = 1, int experience = 1) | |
{ | |
Name = name; | |
Level = level; | |
Experience = experience; | |
Attributes = new Attribute[3] | |
{ | |
new Attribute(EAttribute.Strength, strength), | |
new Attribute(EAttribute.Dexterity, dexterity), | |
new Attribute(EAttribute.Constitution, constitution) | |
}; | |
HitPoints = new HitPoints(10 + GameGlobals.Modifier[Attributes[2].Value - 1]); | |
HitDice = dice; | |
} | |
} | |
public static class CharacterFactory | |
{ | |
public static Character GetCharacter(ECreature creature) | |
{ | |
switch (creature) | |
{ | |
case ECreature.Player: | |
return new Character("adventurer", 16, 15, 13, new Dice(1, 10)); | |
case ECreature.Goblin: | |
return new Character("Goblin", 8, 14, 1, new Dice(1, 6)); | |
default: | |
throw new Exception($"Creature {creature} does not exist"); | |
} | |
} | |
} | |
public class CharacterSorter | |
{ | |
private static int SortDexterity(Character a, Character b) | |
{ | |
return a.Attributes[1].Value.CompareTo(b.Attributes[1].Value); | |
} | |
public static Character[] ByDexterity(Character[] characters) | |
{ | |
Array.Sort(characters, new Comparison<Character>(SortDexterity)); | |
return characters; | |
} | |
} | |
public class BattleSystem | |
{ | |
private static bool IsAlive(Character character) | |
{ | |
return character.HitPoints.Current > 0; | |
} | |
private static bool IsAllDead(Character[] side) | |
{ | |
foreach (Character character in side) | |
{ | |
if (IsAlive(character)) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
private static Character GetCharacter(Character[] side) | |
{ | |
Character character = side[RandomUtil.Next(0, side.Length)]; | |
if (IsAlive(character)) | |
{ | |
return character; | |
} | |
else | |
{ | |
return GetCharacter(side); | |
} | |
} | |
private static void DealDamage(Character a, Character b) | |
{ | |
int result = a.HitDice.Roll(); | |
b.HitPoints.Current -= result; | |
if (b.HitPoints.Current < 0) | |
{ | |
b.HitPoints.Current = 0; | |
} | |
Log.Debug("-------------------------"); | |
Log.Debug($"{b.Name}'s hitpoints was {b.HitPoints.Current + result}, is {b.HitPoints.Current}"); | |
if (b.HitPoints.Current == 0) | |
{ | |
Log.Debug($"{b.Name} died"); | |
} | |
} | |
private static void Attack(Character a, Character b) | |
{ | |
ERollResult result = Dice.RollDefault(); | |
Log.Debug("-------------------------"); | |
Log.Debug($"{a.Name} rolled {result}"); | |
switch (result) | |
{ | |
case ERollResult.Failure: | |
DealDamage(b, a); | |
break; | |
case ERollResult.Partial: | |
DealDamage(a, b); | |
DealDamage(b, a); | |
break; | |
case ERollResult.Success: | |
DealDamage(a, b); | |
break; | |
} | |
} | |
public static void Fight(Character[] sideA, Character[] sideB) | |
{ | |
Character a = null; | |
Character b = null; | |
// sort order of attack | |
CharacterSorter.ByDexterity(sideA); | |
CharacterSorter.ByDexterity(sideB); | |
Log.Debug("-------------------------"); | |
Log.Debug("Side A:"); | |
foreach (Character character in sideA) | |
{ | |
Log.Write($"* {character.Name}"); | |
} | |
Log.Debug("-------------------------"); | |
Log.Debug("Side B:"); | |
foreach (Character character in sideB) | |
{ | |
Log.Write($"* {character.Name}"); | |
} | |
// attack until one side is dead | |
while (!IsAllDead(sideA) && !IsAllDead(sideB)) | |
{ | |
a = GetCharacter(sideA); | |
b = GetCharacter(sideB); | |
Attack(a, b); | |
} | |
// debug results | |
if (!IsAllDead(sideA)) | |
{ | |
Log.Debug("Side A won"); | |
} | |
else | |
{ | |
Log.Debug("Side B won"); | |
} | |
} | |
} | |
public static class GameGlobals | |
{ | |
public static readonly int[] Modifier; | |
static GameGlobals() | |
{ | |
// from dungeon world playbook | |
Modifier = new int[20] | |
{ | |
-3, | |
-3, | |
-3, | |
-2, | |
-2, | |
-1, | |
-1, | |
-1, | |
0, | |
0, | |
0, | |
0, | |
1, | |
1, | |
1, | |
2, | |
2, | |
3, | |
3, | |
3 | |
}; | |
} | |
} | |
public class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
List<Character> sideA = new List<Character> | |
{ | |
CharacterFactory.GetCharacter(ECreature.Player) | |
}; | |
List<Character> sideB = new List<Character> | |
{ | |
CharacterFactory.GetCharacter(ECreature.Goblin) | |
}; | |
BattleSystem.Fight(sideA.ToArray(), sideB.ToArray()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment