Game/Profile.cs
using System;
/// <summary>
/// Everything the puppet needs to draw a gladiator. Gear values are item indexes (-1 = nothing worn).
/// </summary>
public struct Look
{
public int Race;
public int Skin;
public int Hair; // 0 = bald, 1.. = S&S2 hairstyles
public int Beard; // 0 = clean shaven
public int HairColour;
public int Cloth;
public int Helm, Chest, Shoulders, Gauntlets, Thighs, Greaves, Boots, Shield;
public int Weapon; // 0 = fists
public static Look Default => new()
{
Helm = -1, Chest = -1, Shoulders = -1, Gauntlets = -1, Thighs = -1, Greaves = -1, Boots = -1, Shield = -1,
HairColour = 1
};
public string SkinHex
{
get
{
var skins = Catalog.Races[Math.Clamp( Race, 0, Catalog.Races.Length - 1 )].Skins;
return skins[Math.Clamp( Skin, 0, skins.Length - 1 )];
}
}
public string HairHex => Catalog.HairColours[Math.Clamp( HairColour, 0, Catalog.HairColours.Length - 1 )];
public string ClothHex => Catalog.ClothColours[Math.Clamp( Cloth, 0, Catalog.ClothColours.Length - 1 )];
public string RaceName => Catalog.Races[Math.Clamp( Race, 0, Catalog.Races.Length - 1 )].Name;
public int Get( GearSlot s ) => s switch
{
GearSlot.Helm => Helm,
GearSlot.Chest => Chest,
GearSlot.Shoulders => Shoulders,
GearSlot.Gauntlets => Gauntlets,
GearSlot.Thighs => Thighs,
GearSlot.Greaves => Greaves,
GearSlot.Boots => Boots,
_ => Shield
};
public void Set( GearSlot s, int item )
{
switch ( s )
{
case GearSlot.Helm: Helm = item; break;
case GearSlot.Chest: Chest = item; break;
case GearSlot.Shoulders: Shoulders = item; break;
case GearSlot.Gauntlets: Gauntlets = item; break;
case GearSlot.Thighs: Thighs = item; break;
case GearSlot.Greaves: Greaves = item; break;
case GearSlot.Boots: Boots = item; break;
default: Shield = item; break;
}
}
/// <summary>Armour from everything worn.</summary>
public int ArmourPoints()
{
int total = 0;
foreach ( var s in Catalog.Slots )
{
var g = Catalog.GearItem( s, Get( s ) );
if ( g is not null ) total += g.Points;
}
return total;
}
}
public class WeaponEnchant
{
public int Weapon { get; set; }
public Enchant Type { get; set; }
public int Tier { get; set; }
}
public class Profile
{
public const string SaveFile = "gladiator.json";
public const int CurrentVersion = 2;
public int SaveVersion { get; set; } = CurrentVersion;
public string Name { get; set; } = "Gladiator";
public int Level { get; set; } = 1;
public int Gold { get; set; } = 60;
/// <summary>Every stat starts at this, with <see cref="StartPoints"/> to spend on top (like the Flash game).</summary>
public const int StartStat = 1;
public const int StartPoints = 9;
public const int StatCount = 8;
public int StatPoints { get; set; } = StartPoints;
/// <summary>Everyone starts as a slave: dungeon duels until <see cref="Catalog.DuelsToFreedom"/> are won.</summary>
public bool Enslaved { get; set; } = true;
public int DuelWins { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int Strength { get; set; } = StartStat;
public int Attack { get; set; } = StartStat;
public int Defence { get; set; } = StartStat;
public int Vitality { get; set; } = StartStat;
public int Agility { get; set; } = StartStat;
public int Stamina { get; set; } = StartStat;
public int Charisma { get; set; } = StartStat;
public int Magicka { get; set; } = StartStat;
public int Race { get; set; }
public int Skin { get; set; }
public int Hair { get; set; }
public int Beard { get; set; }
public int HairColour { get; set; } = 1;
public int Cloth { get; set; }
public int Weapon { get; set; } = 1;
public List<int> OwnedWeapons { get; set; } = new() { 0, 1 };
/// <summary>Worn item per slot (index into the slot's list), -1 = nothing.</summary>
public int[] Gear { get; set; } = NewGear();
/// <summary>"slot:index" of everything bought.</summary>
public List<string> OwnedGear { get; set; } = new() { "1:0" };
public List<WeaponEnchant> Enchants { get; set; } = new();
public List<int> Spells { get; set; } = new();
public int[] Potions { get; set; } = new int[3];
public static Profile Current { get; set; }
/// <summary>A slave starts in rags (the first chest piece) with a wooden club.</summary>
static int[] NewGear()
{
var g = new int[Catalog.Slots.Length];
for ( int i = 0; i < g.Length; i++ ) g[i] = -1;
g[(int)GearSlot.Chest] = 0;
return g;
}
public Look Look
{
get
{
var l = new Look { Race = Race, Skin = Skin, Hair = Hair, Beard = Beard, HairColour = HairColour, Cloth = Cloth, Weapon = Weapon };
foreach ( var s in Catalog.Slots ) l.Set( s, Gear[(int)s] );
return l;
}
}
public void ApplyLook( Look l )
{
Race = l.Race; Skin = l.Skin; Hair = l.Hair; Beard = l.Beard; HairColour = l.HairColour; Cloth = l.Cloth;
}
public int GetStat( int i ) => i switch
{
0 => Strength, 1 => Attack, 2 => Defence, 3 => Vitality, 4 => Agility, 5 => Stamina, 6 => Charisma, _ => Magicka
};
public void AddStat( int i, int amount )
{
switch ( i )
{
case 0: Strength += amount; break;
case 1: Attack += amount; break;
case 2: Defence += amount; break;
case 3: Vitality += amount; break;
case 4: Agility += amount; break;
case 5: Stamina += amount; break;
case 6: Charisma += amount; break;
default: Magicka += amount; break;
}
}
public static readonly string[] StatNames = { "Strength", "Attack", "Defence", "Vitality", "Agility", "Stamina", "Charisma", "Magicka" };
/// <summary>The order the Flash game lists them in.</summary>
public static readonly int[] StatOrder = { 0, 4, 1, 2, 3, 6, 5, 7 };
public static readonly string[] StatHelp =
{
"Strength: +2 damage per point. Unlocks heavier weapons.",
"Attack: better chance to hit.",
"Defence: reduces the damage you take.",
"Vitality: +10 max health per point.",
"Agility: move further, dodge more, act first.",
"Stamina: +10 max energy per point. Resting recovers more.",
"Charisma: stronger taunts, happier crowds, cheaper shops.",
"Magicka: +10 magic per point, stronger spells, and lets you learn better ones."
};
public WeaponEnchant EnchantFor( int weapon ) => Enchants.FirstOrDefault( x => x.Weapon == weapon );
public bool Owns( GearSlot slot, int index ) => OwnedGear.Contains( $"{(int)slot}:{index}" );
public int ArmourPoints() => Look.ArmourPoints();
public static bool HasSave => FileSystem.Data.FileExists( SaveFile );
public void Save() => FileSystem.Data.WriteJson( SaveFile, this );
public static Profile Load()
{
try
{
var p = FileSystem.Data.ReadJson<Profile>( SaveFile );
if ( p is null ) return null;
// saves from before the S&S2 art: keep the career, reset looks and gear
if ( p.SaveVersion < CurrentVersion )
{
p.Hair = 0; p.Beard = 0; p.Race = 0; p.Skin = 0; p.HairColour = 1; p.Cloth = 0;
p.Gear = NewGear();
p.OwnedGear = new() { "1:0" };
p.Weapon = 1;
p.OwnedWeapons = new() { 0, 1 };
p.Enchants = new();
if ( p.Magicka < StartStat ) p.Magicka = StartStat;
p.SaveVersion = CurrentVersion;
}
if ( p.Gear is null || p.Gear.Length != Catalog.Slots.Length ) p.Gear = NewGear();
p.OwnedWeapons ??= new() { 0, 1 };
p.OwnedGear ??= new();
p.Enchants ??= new();
p.Spells ??= new();
if ( p.Potions is null || p.Potions.Length != 3 ) p.Potions = new int[3];
p.Weapon = Math.Clamp( p.Weapon, 0, Catalog.Weapons.Length - 1 );
p.Hair = Math.Clamp( p.Hair, 0, Catalog.HairCount );
p.Beard = Math.Clamp( p.Beard, 0, Catalog.BeardCount );
return p;
}
catch ( Exception e )
{
Log.Warning( $"Couldn't load save: {e.Message}" );
return null;
}
}
}