Game/GameManager.cs
using System;
public enum GameScreen { Title, Create, Intro, Town, Weaponsmith, Armoury, Magic, Church, Stats, Arena, Fitting }
/// <summary>
/// Owns the player's save and which screen is showing.
/// </summary>
public sealed class GameManager : Component
{
public GameScreen Screen { get; set; } = GameScreen.Title;
public GameScreen StatsReturn { get; set; } = GameScreen.Town;
public Profile Player => Profile.Current;
public string Notice { get; set; }
/// <summary>Heading on the stats screen after a fight ("You have risen to gladiator level 2.").</summary>
public string Heading { get; set; }
public static GameManager Instance { get; private set; }
protected override void OnAwake()
{
Instance = this;
// the 3D places live next to the game manager
GameObject.GetOrAddComponent<World3D>();
Fit.Load();
}
public void NewGame()
{
Profile.Current = new Profile();
Screen = GameScreen.Create;
}
public void Continue()
{
var p = Profile.Load();
if ( p is null ) return;
Profile.Current = p;
Screen = GameScreen.Town;
}
public void FinishCreate( string name, Look look )
{
var p = Player;
p.Name = string.IsNullOrWhiteSpace( name ) ? "Gladiator" : name.Trim();
p.ApplyLook( look );
p.Save();
// a new gladiator starts as a slave: short intro, then the town
Screen = GameScreen.Intro;
}
public void FinishIntro()
{
Notice = null;
Screen = GameScreen.Town;
}
public void OpenFitting()
{
Profile.Current ??= Profile.Load() ?? new Profile();
Screen = GameScreen.Fitting;
}
// ---------------- character creation ----------------
/// <summary>Take a point back out of a stat while creating the gladiator (never below the starting 1).</summary>
public void UnspendStat( int stat )
{
var p = Player;
if ( p is null || p.GetStat( stat ) <= Profile.StartStat ) return;
p.AddStat( stat, -1 );
p.StatPoints++;
}
public void Go( GameScreen s )
{
Notice = null;
if ( s == GameScreen.Stats )
{
StatsReturn = Screen == GameScreen.Stats ? GameScreen.Town : Screen;
Heading = null;
}
// the town's arena gate: a slave only gets dungeon duels
if ( s == GameScreen.Arena )
{
EnterFight();
return;
}
Screen = s;
}
public void QuitToTitle()
{
Player?.Save();
Scene.Get<ArenaFight>()?.Clear();
Screen = GameScreen.Title;
}
// ---------------- fights ----------------
public FightMode NextFight => Player is null || Player.Enslaved ? FightMode.Duel : FightMode.Arena;
public void EnterFight()
{
var fight = Scene.Get<ArenaFight>();
if ( !fight.IsValid() || Player is null ) return;
Screen = GameScreen.Arena;
fight.StartFight( Player, NextFight );
}
public void LeaveArena()
{
var fight = Scene.Get<ArenaFight>();
if ( !fight.IsValid() ) return;
bool won = fight.Winner.IsValid() && fight.Winner.IsLocalPlayer;
bool duel = fight.IsDuel;
var p = Player;
// potions drunk are gone
if ( fight.Left.IsValid() )
{
p.Potions[0] = fight.Left.HealthPotions;
p.Potions[1] = fight.Left.EnergyPotions;
p.Potions[2] = fight.Left.MagicPotions;
}
int gold, points;
Heading = null;
if ( won )
{
gold = duel ? 25 + p.Level * 10 : 60 + p.Level * 30 + fight.Crowd * p.Level / 4;
points = 5;
p.Level++;
p.Wins++;
Heading = $"You have risen to gladiator level {p.Level}.";
if ( duel )
{
p.DuelWins++;
if ( p.DuelWins >= Catalog.DuelsToFreedom && p.Enslaved )
{
p.Enslaved = false;
Heading = "The crowd has heard of you. You've earned your place in the arena!";
}
}
p.Gold += gold;
Notice = $"Victory! +{gold} gold and {points} skill points.";
}
else
{
p.Losses++;
if ( duel )
{
// losing a duel costs 5-15% of your gold: the quicker you lost, the more
float frac = Math.Clamp( 0.15f - (fight.Turns - 4) * (0.10f / 16f), 0.05f, 0.15f );
gold = (int)MathF.Round( p.Gold * frac );
p.Gold -= gold;
points = 1;
Notice = $"You lost the duel and {gold} gold ({MathF.Round( frac * 100 )}%) to the guards' bets. +{points} skill point.";
}
else
{
gold = 20 + p.Level * 8;
points = 2;
p.Gold += gold;
Notice = $"Defeat... +{gold} gold for your trouble and {points} skill points.";
}
}
p.StatPoints += points;
p.Save();
fight.Clear();
StatsReturn = GameScreen.Town;
Screen = GameScreen.Stats;
}
// ---------------- stats ----------------
public void SpendStat( int stat )
{
var p = Player;
if ( p.StatPoints <= 0 ) return;
p.AddStat( stat, 1 );
p.StatPoints--;
p.Save();
}
// ---------------- shops ----------------
public int Price( int basePrice ) => Catalog.Discount( basePrice, Player.Charisma );
public string BuyWeapon( int id )
{
var p = Player;
var w = Catalog.Weapons[id];
if ( p.OwnedWeapons.Contains( id ) )
{
p.Weapon = id;
p.Save();
return $"{w.Name} equipped.";
}
if ( p.Strength < w.Strength ) return $"{w.Name}\nNeeds {w.Strength} strength.\nThis item is not available.";
int price = Price( w.Price );
if ( p.Gold < price ) return $"The {w.Name} costs {price} gold. Come back when you have it.";
p.Gold -= price;
p.OwnedWeapons.Add( id );
p.Weapon = id;
p.Save();
return $"A fine choice! The {w.Name} is yours.";
}
public string BuyGear( GearSlot slot, int index )
{
var p = Player;
var g = Catalog.GearItem( slot, index );
if ( g is null ) return "";
if ( p.Owns( slot, index ) )
{
p.Gear[(int)slot] = p.Gear[(int)slot] == index ? -1 : index;
p.Save();
return p.Gear[(int)slot] == index ? $"{g.Name} equipped." : $"{g.Name} taken off.";
}
if ( p.Level < g.Level ) return $"{g.Name}\nRequired level {g.Level}\nThis item is not available.";
int price = Price( g.Price );
if ( p.Gold < price ) return $"{g.Name} costs {price} gold. You can't afford it.";
p.Gold -= price;
p.OwnedGear.Add( $"{(int)slot}:{index}" );
p.Gear[(int)slot] = index;
p.Save();
return $"{g.Name}, a perfect fit!";
}
public string BuyEnchant( Enchant type, int tier )
{
var p = Player;
if ( p.Weapon <= 0 ) return "I can't enchant your fists, dear. Buy a weapon first.";
var current = p.EnchantFor( p.Weapon );
if ( current is not null && current.Type == type && current.Tier >= tier ) return "Your weapon already holds that magic.";
int price = Price( Catalog.EnchantPrice( tier ) );
if ( p.Gold < price ) return $"That costs {price} gold. You don't have enough.";
p.Gold -= price;
p.Enchants.RemoveAll( x => x.Weapon == p.Weapon );
p.Enchants.Add( new WeaponEnchant { Weapon = p.Weapon, Type = type, Tier = tier } );
p.Save();
var (min, max) = Catalog.EnchantRange( tier );
return $"Done! Your {Catalog.Weapons[p.Weapon].Name} now deals +{min}-{max} {type.ToString().ToLower()} damage.";
}
public string BuySpell( Spell s )
{
var p = Player;
var info = Catalog.SpellInfo( s );
if ( p.Spells.Contains( (int)s ) ) return $"You already know {info.Name}.";
if ( p.Magicka < info.MinMagicka ) return $"{info.Name}\nNeeds {info.MinMagicka} magicka.\nYour mind isn't ready for it yet.";
int price = Price( info.Price );
if ( p.Gold < price ) return $"{info.Name} costs {price} gold.";
p.Gold -= price;
p.Spells.Add( (int)s );
p.Save();
return $"The words of {info.Name} are yours. Use them wisely.";
}
public string BuyPotion( Potion potion )
{
var p = Player;
int price = Price( Catalog.PotionPrice( potion ) );
if ( p.Potions[(int)potion] >= 5 ) return "You can't carry any more of those, my child.";
if ( p.Gold < price ) return $"A {Catalog.PotionName( potion ).ToLower()} costs {price} gold.";
p.Gold -= price;
p.Potions[(int)potion]++;
p.Save();
return $"Bless you. A {Catalog.PotionName( potion ).ToLower()} for the fight ahead.";
}
}