Skip to content

Instantly share code, notes, and snippets.

@MerijnHendriks
Created May 5, 2021 13:47
Show Gist options
  • Save MerijnHendriks/570d28c420c4e1ad4b0c0670c71cb413 to your computer and use it in GitHub Desktop.
Save MerijnHendriks/570d28c420c4e1ad4b0c0670c71cb413 to your computer and use it in GitHub Desktop.
Simple combat system
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