text:
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: QuinnBast

---

<!--- Provide a general summary of the issue in the Title above -->

<!-- Please answer the following questions for yourself before submitting an issue.

- [ ] I am running the latest version
- [ ] I checked the documentation and found no answer
- [ ] I checked to make sure that this issue has not already been filed
- [ ] I'm reporting the issue to the correct repository (for multi-repository projects)
-->

## Expected Behavior
<!--- Tell us what should happen -->

## Current Behavior
<!--- Tell us what happens instead of the expected behavior -->

## Failure Information

<!--
Please help provide information about the failure if this is a bug.
If it is not a bug, please remove the rest of this template.
This section should include error messages, logs, etc.
-->

## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->

## Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
<!--- Provide screenshots if possible/relevant -->
1.
2.
3.
4.

## Context (Environment)
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
<!-- Include information about your environment including device & version, app version, etc. -->
* Device:
* Operating System:
* App Version:

## Detailed Description
<!--- Provide a detailed description of the change or addition you are proposing -->


text:
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: QuinnBast

---

## Enhancement Description
<!-- Present a description of the problem to be addressed by this feature request. -->

## Solution
<!-- Suggestions to implement a solution are greatly appreciated. If you have a potential solution leave it here

Things to address include:
* Details of the technical implementation
* Tradeoffs made in design decisions
* Caveats and considerations for the future

If there are multiple solutions, please present each one separately. Save comparisons for the very end.)
-->

repo_name: Subterfuge-Revived/Remake-Core

repo_language: C#

mime_type: text/plain


text:
# This is a basic workflow to help you get started with Actions

name: Test Server

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  pull_request:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  testServer:
    # The type of runner that the job will run on
    runs-on: ubuntu-22.04

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2
    # Test the server
    - name: Deploy server
      run: docker-compose --profile test up --abort-on-container-exit --exit-code-from server_test


text:
# This is a basic workflow to help you get started with Actions

name: Test Dll

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  pull_request:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  unitTest:
    # The type of runner that the job will run on
    runs-on: ubuntu-22.04
    # The container image to pull
    container: mcr.microsoft.com/dotnet/sdk:7.0

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2
    # Build the DLL
    - name: Build DLL
      run: dotnet build "Core/SubterfugeCore/SubterfugeCore.csproj"
    - name: Run tests
      run: dotnet test "Core/SubterfugeCoreTest/SubterfugeCoreTest.csproj"
  # A step to build docfx documents using a differnt container with docfx installed
  gen-docs:
    runs-on: ubuntu-22.04
    container: erothejoker/docker-docfx:latest
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2
    # Run the docfx generation command.
    - name: Generate docs
      run: docfx Core/SubterfugeCore/docfx.json


text:
# This is a basic workflow to help you get started with Actions

name: Deploy Release

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  unitTest:
    # The type of runner that the job will run on
    runs-on: ubuntu-22.04
    # The container image to pull from dockerhub
    container: mcr.microsoft.com/dotnet/sdk:7.0

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2
    # Install nuget packages
    - name: Install NuGet packages
      run: dotnet restore "Core/SubterfugeCore/SubterfugeCore.csproj"
    # Build the DLL
    - name: Build DLL
      run: dotnet build "Core/SubterfugeCore/SubterfugeCore.csproj"
    - name: Run tests
      run: dotnet test "Core/SubterfugeCoreTest/SubterfugeCoreTest.csproj"
  # A step to build docfx documents using a differnt container with docfx installed to PATH
  upload-docs:
    runs-on: ubuntu-18.04
    container: erothejoker/docker-docfx:latest
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2
    # Run the docfx generation command.
    - name: Generate docs
      run: docfx Core/SubterfugeCore/docfx.json
    - name: GitHub Pages
      uses: crazy-max/ghaction-github-pages@v1.5.1
      with:
        # Build directory to deploy
        build_dir: Core/SubterfugeCore/_site/
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  deploy:
    # The type of runner that the job will run on
    runs-on: ubuntu-18.04
    # The container image to pull from dockerhub
    container: mcr.microsoft.com/dotnet/sdk:7.0
    # Test and upload-docs must both succeed before deploying a release
    needs: [unitTest, upload-docs]
    steps:
        # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Checkout
      uses: actions/checkout@v2

    # Install nuget packages
    - name: Install NuGet packages
      run: dotnet restore "Core/SubterfugeCore/SubterfugeCore.csproj"
    # Build the DLL
    - name: Build DLL
      run: dotnet build "Core/SubterfugeCore/SubterfugeCore.csproj"
    - uses: actions/upload-artifact@v2
      with:
        name: DLLs
        path: ./**/*.dll


text:
- name: Articles
  href: articles/
- name: Api Documentation
  href: api/


text:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <LangVersion>8</LangVersion>
    <Nullable>enable</Nullable>
    <RootNamespace>Subterfuge.Remake</RootNamespace>
    <TargetFramework>netstandard2.0</TargetFramework>
	<AssemblyVersion>1.0.0</AssemblyVersion>
	<FileVersion>1.0.0</FileVersion>
	<NeutralLanguage>en-CA</NeutralLanguage>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="Core\README.txt" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="Core\README.txt" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
    <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Models\Models.csproj" />
  </ItemGroup>

</Project>


text:
# SubterfugeCore documentation

Welcome to the SubterfugeCore documentation. This API documentation provides some introductory examples of how to get
started with the API and provides API references for all possible classes and methods.all

You can see a references of all components at the [Api Reference](api/) in the navigation bar. The best way to get
started is by reading the [Articles](articles/intro.md).


text:
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Subterfuge.Remake.Api.Network;
using Subterfuge.Remake.Core.Entities;
using Subterfuge.Remake.Core.Entities.Components;
using Subterfuge.Remake.Core.Entities.Positions;
using Subterfuge.Remake.Core.Entities.Specialists;
using Subterfuge.Remake.Core.Players;
using Subterfuge.Remake.Core.Timing;
using Subterfuge.Remake.Core.Topologies;

namespace Subterfuge.Remake.Core
{
    public interface IGameState
    {
        /// <summary>
        /// Gets the GameTick that this GameState object is representing.
        /// </summary>
        /// <returns>The GameTick that the GameState represents.</returns>
        GameTick GetCurrentTick();

        /// <summary>
        /// Gets the game's start time
        /// </summary>
        /// <returns>The game's start time</returns>
        GameTick GetStartTick();

        /// <summary>
        /// Advances the GameState by one tick. IMPORTANT: This does NOT apply any time machine actions.
        /// </summary>
        /// <returns>The next GameTick</returns>
        GameTick GoToNextTick();

        /// <summary>
        /// Returns a list of all active subs in the game
        /// </summary>
        /// <returns>A list of the active subs</returns>
        List<Sub> GetSubList();

        /// <summary>
        /// Returns a list of all outposts in the game
        /// </summary>
        /// <returns>A list of all outposts</returns>
        List<Outpost> GetOutposts();

        /// <summary>
        /// Returns a list of all the players in the game
        /// </summary>
        /// <returns>A list of all players</returns>
        List<Player> GetPlayers();

        /// <summary>
        /// Determines if a given sub exists in the GameState. This method is useful for checking if an event
        /// is holding a reference to an object that has lost a combat and was removed from the game or if the object
        /// has survived and is still in the game.
        /// </summary>
        /// <param name="sub">The sub to check exists</param>
        /// <returns>if the sub exists in the GameState</returns>
        bool SubExists(Sub sub);

        /// <summary>
        /// Launches a sub (or brings a sub back into the game if reversing). Adding the sub to the active sub list
        /// </summary>
        /// <param name="sub">The sub to add to the GameState</param>
        void AddSub(Sub sub);

        void AddOutpost(Outpost outpost);

        /// <summary>
        /// Revmoes a sub from the list of active subs
        /// </summary>
        /// <param name="sub">The sub to remove from the game</param>
        void RemoveSub(Sub sub);

        /// <summary>
        /// Determine all subs that are on a path between two outpost desinations
        /// </summary>
        /// <param name="source">The source outpost</param>
        /// <param name="destination">The destination outpost</param>
        /// <returns>A list of all subs sailing between the source and destination</returns>
        List<Sub> getSubsOnPath(Outpost source, Outpost destination);

        /// <summary>
        /// Get a list of the specific player's subs
        /// </summary>
        /// <param name="player">The player to get the subs of</param>
        /// <returns>A list of the player's subs</returns>
        List<Sub> GetPlayerSubs(Player player);

        /// <summary>
        /// Get a list of the specific player's outposts
        /// </summary>
        /// <param name="player">The player to get the outposts of</param>
        /// <returns>A list of the player's controlled outposts</returns>
        List<Outpost> GetPlayerOutposts(Player player);

        /// <summary>
        /// Gets an entity by its ID
        /// </summary>
        /// <param name="id">The ID of the outpost you want to obtain.</param>
        /// <returns>The outpost matching the input Guid. Null if no results.</returns>
        Entity? GetEntity(string id);

        /// <summary>
        /// Determines if the referenced outpost exists.
        /// </summary>
        /// <param name="outpost">The outpost to check</param>
        /// <returns>If the referenced outpost exists</returns>
        bool OutpostExists(Outpost outpost);

        /// <summary>
        /// Determines if the player is in the game
        /// </summary>
        /// <param name="player">The player to check</param>
        /// <returns>If the player exists in the game</returns>
        bool PlayerExists(Player player);

        /// <summary>
        /// Replaces an outpost in the game state with another outpost. The two outposts should have the same Outpost-level fields, only differing in subclass.
        /// </summary>
        /// <param name="remove">An outpost in the game state to remove.</param>
        /// <param name="add">An outpost not in the game state to add.</param>
        /// <returns>True if the replacement was successful, and false otherwise.</returns>
        bool ReplaceOutpost(Outpost remove, Outpost add);

        /// <summary>
        /// Gets a list of all specilists in the game
        /// </summary>
        /// <returns>A list of all specliasts in the game</returns>
        List<Specialist> GetAllSpecialists();

        /// <summary>
        /// Gets a list of a player's speciliasts
        /// </summary>
        /// <param name="player">The player to get specialists for</param>
        /// <returns>A list of the player's specialists</returns>
        List<Specialist> GetPlayerSpecialists(Player player);

        /// <summary>
        /// Gets the player's driller count
        /// </summary>
        /// <param name="player">The player's driller count</param>
        /// <returns></returns>
        int GetPlayerDrillerCount(Player player);

        /// <summary>
        /// Determines the player's driller capacity
        /// </summary>
        /// <param name="player">player</param>
        /// <returns>The player's driller capacity</returns>
        int GetPlayerDrillerCapacity(Player player);

        /// <summary>
        /// Determines how many extra drillers the specified player's electrical
        /// can support.
        /// </summary>
        /// <param name="player">player</param>
        /// <returns>The amount of extra drillers the player can have.
        /// If it returns 0 or less, the player is out of electricity. </returns>
        int GetExtraDrillerCapacity(Player player);

        /// <summary>
        /// Determines whether a position on the map is visible to the
        /// specified player.
        /// </summary>
        /// <param name="positionManager">The position manager</param>
        /// <param name="player">player</param>
        /// <returns>True if the position is visible and false otherwise.</returns>
        bool IsInVisionRange(PositionManager positionManager, Player player);

        /// <summary>
        /// Gets a list of all ICombatable instances of a particular player.
        /// </summary>
        /// <param name="player">The player to get combatables for</param>
        /// <returns>The list of the player's property</returns>
        List<Entity> GetPlayerTargetables(Player player);

        /// <summary>
        /// Gets a list of all game objects
        /// </summary>
        /// <returns>A list of all game objects.</returns>
        List<IEntity> GetAllGameObjects();

        /// <summary>
        /// Gets a list of all entities within range of a particular location
        /// </summary>
        /// <param name="range">The range to search</param>
        /// <param name="pos">The position to check</param>
        /// <returns>A list of entities in range of the position</returns>
        List<IEntity> EntitesInRange(float range, RftVector pos);
    }

    /// <summary>
    /// This class holds only the nessecary information that is needed to render the map at any given point in time.
    /// This class contains a list of all subs, outposts, and players in the game. Get an instance of this class by
    /// using `Game.timeMachine.getState()` to get the current state of the game.
    /// </summary>
    public class GameState : IGameState
    {
        /// <summary>
        /// A list of the currently existing subs
        /// </summary>
        private readonly List<Sub> _activeSubs = new List<Sub>();

        /// <summary>
        /// A list of the current oupost states
        /// </summary>
        private readonly List<Outpost> _outposts = new List<Outpost>();

        /// <summary>
        /// A list of the players in the game
        /// </summary>
        private readonly List<Player> _players;

        /// <summary>
        /// The current game tick
        /// </summary>
        public GameTick CurrentTick;

        /// <summary>
        /// The time the game started.
        /// </summary>
        private readonly GameTick _startTime;

        /// <summary>
        /// Constructs a new instance of a GameState to represent the game.
        /// Generally, the `Game` and `TimeMachine` objects will handle the creation and management of the GameState
        /// you will likely never need to use this constructor.
        /// </summary>
        /// <param name="configuration">The Game configuration used to create the game. Required to setup the player list.</param>
        public GameState(List<Player> playersInGame)
        {
            // Set the start time to the time the game was initialized at and set the current tick
            this._startTime = new GameTick();
            this.CurrentTick = this._startTime;

            // Set the players and attach their specialist pools.
            this._players = playersInGame;
        }

        public GameState(GameConfiguration gameConfig)
        {
            // Set the start time to the time the game was initialized at and set the current tick
            this._startTime = new GameTick();
            this.CurrentTick = this._startTime;

            // Set the players.
            this._players = gameConfig.PlayersInLobby.Select(it => new Player(it)).ToList();
        }

        /// <summary>
        /// Gets the GameTick that this GameState object is representing.
        /// </summary>
        /// <returns>The GameTick that the GameState represents.</returns>
        public GameTick GetCurrentTick()
        {
            return this.CurrentTick;
        }

        /// <summary>
        /// Gets the game's start time
        /// </summary>
        /// <returns>The game's start time</returns>
        public GameTick GetStartTick()
        {
            return this._startTime;
        }

        /// <summary>
        /// Advances the GameState by one tick. IMPORTANT: This does NOT apply any time machine actions.
        /// </summary>
        /// <returns>The next GameTick</returns>
        public GameTick GoToNextTick()
        {
            this.CurrentTick = this.CurrentTick.GetNextTick();
            return this.CurrentTick;
        }

        /// <summary>
        /// Returns a list of all active subs in the game
        /// </summary>
        /// <returns>A list of the active subs</returns>
        public List<Sub> GetSubList()
        {
            return this._activeSubs;
        }

        /// <summary>
        /// Returns a list of all outposts in the game
        /// </summary>
        /// <returns>A list of all outposts</returns>
        public List<Outpost> GetOutposts()
        {
            return this._outposts;
        }

        /// <summary>
        /// Returns a list of all the players in the game
        /// </summary>
        /// <returns>A list of all players</returns>
        public List<Player> GetPlayers()
        {
            return this._players;
        }

        /// <summary>
        /// Determines if a given sub exists in the GameState. This method is useful for checking if an event
        /// is holding a reference to an object that has lost a combat and was removed from the game or if the object
        /// has survived and is still in the game.
        /// </summary>
        /// <param name="sub">The sub to check exists</param>
        /// <returns>if the sub exists in the GameState</returns>
        public bool SubExists(Sub sub)
        {
            return this._activeSubs.Contains(sub);
        }

        /// <summary>
        /// Launches a sub (or brings a sub back into the game if reversing). Adding the sub to the active sub list
        /// </summary>
        /// <param name="sub">The sub to add to the GameState</param>
        public void AddSub(Sub sub)
        {
            this._activeSubs.Add(sub);
        }

        public void AddOutpost(Outpost outpost)
        {
            this._outposts.Add(outpost);
        }

        /// <summary>
        /// Revmoes a sub from the list of active subs
        /// </summary>
        /// <param name="sub">The sub to remove from the game</param>
        public void RemoveSub(Sub sub)
        {
            this._activeSubs.Remove(sub);
        }

        /// <summary>
        /// Determine all subs that are on a path between two outpost desinations
        /// </summary>
        /// <param name="source">The source outpost</param>
        /// <param name="destination">The destination outpost</param>
        /// <returns>A list of all subs sailing between the source and destination</returns>
        public List<Sub> getSubsOnPath(Outpost source, Outpost destination)
        {
            List<Sub> subsOnPath = new List<Sub>();

            // Check if a sub is on that path

            // This logic is not nessicarily true..
            // If a sub has a navigator this logic kind of falls apart.
            foreach (Sub sub in this._activeSubs)
            {

                if (
                    sub.GetComponent<PositionManager>().GetExpectedDestination() == destination.GetComponent<PositionManager>().CurrentLocation ||
                    sub.GetComponent<PositionManager>().CurrentLocation == source.GetComponent<PositionManager>().CurrentLocation)
                {
                    // TODO:
                    // if (sub.GetComponent<PositionManager>().GetSource() == source || sub.GetComponent<PositionManager>().GetSource() == destination)
                    // {
//                         // Sub is on the path.
//                         subsOnPath.Add(sub);
//                     }
                }
            }
            return subsOnPath;
        }

        /// <summary>
        /// Get a list of the specific player's subs
        /// </summary>
        /// <param name="player">The player to get the subs of</param>
        /// <returns>A list of the player's subs</returns>
        public List<Sub> GetPlayerSubs(Player player)
        {
            List<Sub> playerSubs = new List<Sub>();
            foreach(Sub sub in this._activeSubs)
            {
                if(sub.GetComponent<DrillerCarrier>().GetOwner() == player)
                {
                    playerSubs.Add(sub);
                }
            }
            return playerSubs;
        }

        /// <summary>
        /// Get a list of the specific player's outposts
        /// </summary>
        /// <param name="player">The player to get the outposts of</param>
        /// <returns>A list of the player's controlled outposts</returns>
        public List<Outpost> GetPlayerOutposts(Player player)
        {
            return this._outposts
                .Where(it => it.GetComponent<DrillerCarrier>().GetOwner() != null)
                .Where(it => it.GetComponent<DrillerCarrier>().GetOwner().Equals(player))
                .ToList();
        }

        /// <summary>
        /// Gets an entity by its ID
        /// </summary>
        /// <param name="id">The ID of the outpost you want to obtain.</param>
        /// <returns>The outpost matching the input Guid. Null if no results.</returns>
        public Entity? GetEntity(string id)
        {
            foreach (Outpost outpost in this._outposts)
            {
                if (outpost.GetComponent<IdentityManager>().GetId() == id)
                {
                    return outpost;
                }
            }

            foreach (Sub sub in this._activeSubs)
            {
                if (sub.GetComponent<IdentityManager>().GetId() == id)
                {
                    return sub;
                }
            }

            return null;
        }

        /// <summary>
        /// Determines if the referenced outpost exists.
        /// </summary>
        /// <param name="outpost">The outpost to check</param>
        /// <returns>If the referenced outpost exists</returns>
        public bool OutpostExists(Outpost outpost)
        {
            return this._outposts.Contains(outpost);
        }

        public bool EntityExists(IEntity entity)
        {
            if (entity is Outpost)
            {
                return this._outposts.Contains(entity);
            }

            if (entity is Sub)
            {
                return this._activeSubs.Contains(entity);
            }

            return false;
        }

        /// <summary>
        /// Determines if the player is in the game
        /// </summary>
        /// <param name="player">The player to check</param>
        /// <returns>If the player exists in the game</returns>
        public bool PlayerExists(Player player)
        {
            return this._players.Contains(player);
        }

        /// <summary>
        /// Replaces an outpost in the game state with another outpost. The two outposts should have the same Outpost-level fields, only differing in subclass.
        /// </summary>
        /// <param name="remove">An outpost in the game state to remove.</param>
        /// <param name="add">An outpost not in the game state to add.</param>
        /// <returns>True if the replacement was successful, and false otherwise.</returns>
        public bool ReplaceOutpost(Outpost remove, Outpost add)
        {
            if (_outposts.Contains(remove) && !_outposts.Contains(add))
            {
                _outposts.Remove(remove);
                _outposts.Add(add);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Gets a list of all specilists in the game
        /// </summary>
        /// <returns>A list of all specliasts in the game</returns>
        public List<Specialist> GetAllSpecialists()
        {
            List<Specialist> specialists = new List<Specialist>();
            foreach(Outpost o in this._outposts)
            {
                specialists.AddRange(o.GetComponent<SpecialistManager>().GetSpecialists());
            }

            foreach (Sub s in _activeSubs)
            {
                specialists.AddRange(s.GetComponent<SpecialistManager>().GetSpecialists());
            }

            return specialists;
        }

        /// <summary>
        /// Gets a list of a player's speciliasts
        /// </summary>
        /// <param name="player">The player to get specialists for</param>
        /// <returns>A list of the player's specialists</returns>
        public List<Specialist> GetPlayerSpecialists(Player player)
        {
            List<Specialist> specialists = new List<Specialist>();
            foreach(Outpost o in this._outposts)
            {
                if (o.GetComponent<DrillerCarrier>().GetOwner() == player)
                {
                    specialists.AddRange(o.GetComponent<SpecialistManager>().GetSpecialists());
                }
            }

            foreach (Sub s in _activeSubs)
            {
                if (s.GetComponent<DrillerCarrier>().GetOwner() == player)
                {
                    specialists.AddRange(s.GetComponent<SpecialistManager>().GetSpecialists());
                }
            }

            return specialists;
        }

        /// <summary>
        /// Gets the player's driller count
        /// </summary>
        /// <param name="player">The player's driller count</param>
        /// <returns></returns>
        public int GetPlayerDrillerCount(Player player)
        {
            return GetPlayerOutposts(player).Sum( it => it.GetComponent<DrillerCarrier>().GetDrillerCount()) + GetPlayerSubs(player).Sum(it => it.GetComponent<DrillerCarrier>().GetDrillerCount());
        }

        /// <summary>
        /// Determines the player's driller capacity
        /// </summary>
        /// <param name="player">player</param>
        /// <returns>The player's driller capacity</returns>
        public int GetPlayerDrillerCapacity(Player player)
        {
            return (GetPlayerOutposts(player)
                       .FindAll(outpost => outpost.GetOutpostType().Equals(OutpostType.Generator)).Count *
                   Constants.BaseGeneratorCapacity) + Constants.BaseDrillerCapacity;
        }

        /// <summary>
        /// Determines how many extra drillers the specified player's electrical
        /// can support.
        /// </summary>
        /// <param name="player">player</param>
        /// <returns>The amount of extra drillers the player can have.
        /// If it returns 0 or less, the player is out of electricity. </returns>
        public int GetExtraDrillerCapacity(Player player)
        {
            return GetPlayerDrillerCapacity(player) - GetPlayerDrillerCount(player);
        }

        /// <summary>
        /// Determines whether a position on the map is visible to the
        /// specified player.
        /// </summary>
        /// <param name="positionManager">The position manager</param>
        /// <param name="player">player</param>
        /// <returns>True if the position is visible and false otherwise.</returns>
        public bool IsInVisionRange(PositionManager positionManager, Player player)
        {
            foreach(Outpost o in GetPlayerOutposts(player))
            {
                if (o.GetComponent<VisionManager>().IsInVisionRange(positionManager))
                {
                    return true;
                }
            }

            foreach (Sub s in GetPlayerSubs(player))
            {
                if (s.GetComponent<VisionManager>().IsInVisionRange(positionManager))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Gets a list of all ICombatable instances of a particular player.
        /// </summary>
        /// <param name="player">The player to get combatables for</param>
        /// <returns>The list of the player's property</returns>
        public List<Entity> GetPlayerTargetables(Player player)
        {
            List<Entity> entites = new List<Entity>();
            entites.AddRange(GetPlayerOutposts(player));
            entites.AddRange(GetPlayerSubs(player));
            return entites;
        }

        /// <summary>
        /// Gets a list of all game objects
        /// </summary>
        /// <returns>A list of all game objects.</returns>
        public List<IEntity> GetAllGameObjects()
        {
            List<IEntity> gameObjects = new List<IEntity>();
            gameObjects.AddRange(_activeSubs);
            gameObjects.AddRange(_outposts);
            return gameObjects;
        }

        /// <summary>
        /// Gets a list of all entities within range of a particular location
        /// </summary>
        /// <param name="range">The range to search</param>
        /// <param name="pos">The position to check</param>
        /// <returns>A list of entities in range of the position</returns>
        public List<IEntity> EntitesInRange(float range, RftVector pos)
        {
            var gameObjects = GetAllGameObjects();
            List<IEntity> gameObjectsinRange = new List<IEntity>();

            foreach (IEntity e in gameObjects)
            {
                if (Vector2.Distance(e.GetComponent<PositionManager>().CurrentLocation.ToVector2(), pos.ToVector2()) < range)
                {
                    gameObjectsinRange.Add(e);
                }
            }
            return gameObjectsinRange;
        }
    }
}


text:
namespace Subterfuge.Remake.Core
{
    /// <summary>
    /// A Collection of Constants like player base stats etc.
    /// </summary>
    public static class Constants
    {
		/// <summary>
		/// The player's base driller capacity
		/// </summary>
		public const int BaseDrillerCapacity = 150;

		/// <summary>
		/// The base capacity of generator type outposts
		/// </summary>
		public const int BaseGeneratorCapacity = 50;

		/// <summary>
		/// The base factory production per tick
		/// </summary>
		public const int BaseFactoryProductionAmount = 6;

		/// <summary>
		/// The base time (in minutes) per production
		/// </summary>
		public const int TicksPerProduction = 32;

		/// <summary>
		/// The initial number of drillers an outpost will have when generated.
		/// </summary>
		public const int InitialDrillersPerOutpost = 10;

		/// <summary>
		/// The initial number of drillers an outpost will have when generated.
		/// </summary>
		public const int InitialMaxShieldsPerOutpost = 20;

		/// <summary>
		/// The default vision range for an outpost.
		/// </summary>
		public const float BaseOutpostVisionRadius = 50;

		/// <summary>
		/// The default vision for a watchtower.
		/// </summary>
		public const float BaseWatchtowerVisionRadius = 75;

		/// <summary>
		/// The base sub vision radius.
		/// </summary>
		public const float BaseSubVisionRadius = BaseOutpostVisionRadius * 0.2f;

		/// <summary>
		/// The amount of neptunium required to win.
		/// </summary>
		public const int MiningNeptuniumToWin = 200;

		/// <summary>
		/// The driller cost for the first {2} drilled mines.
		/// </summary>
		public static readonly int[] MiningCostInitial = { 50, 100 };

		/// <summary>
		/// The driller cost increase for each mine beyond the first {2} drilled mines.
		/// </summary>
		public const int MiningCostIncrease = 100;

		/// <summary>
		/// The amount of units a sub with speed multiplier of 1 travels in an Rft map in 24 hours. 144 units / day = 0.1 units / minute = 1 unit / 10 minutes
		/// By default, subs will move 1 unit per tick.
		/// </summary>
		public const float STANDARD_SUB_RFT_UNITS_PER_DAY = 144;

		/// <summary>
		/// The number of ticks required to elapse before 1 shield is generated
		/// (10 ticks * 20 shields * 15m per tick) / 60 m per hour = 50 hours to regen 20 shields.
		/// </summary>
		public const int BASE_SHIELD_REGENERATION_TICKS = 10;

		/// <summary>
		/// The number of specialists to offer per cycle
		/// </summary>
		public const int SPECIALISTS_OFFERED_PER_CYCLE = 2;

		/// <summary>
		/// The number of specialists required in the player's deck when they join a game.
		/// </summary>
		public const int REQUIRED_PLAYER_SPECIALIST_DECK_SIZE = 15;
    }
}


text:
using System.Collections.Generic;
using System.Linq;
using Subterfuge.Remake.Api.Network;
using Subterfuge.Remake.Core.Entities.Positions;
using Subterfuge.Remake.Core.GameEvents.Base;
using Subterfuge.Remake.Core.GameEvents.Combat.CombatEvents;
using Subterfuge.Remake.Core.GameEvents.PlayerTriggeredEvents;
using Subterfuge.Remake.Core.Generation;
using Subterfuge.Remake.Core.Players;
using Subterfuge.Remake.Core.Timing;

namespace Subterfuge.Remake.Core
{
    /**
     * This game server class will hold all of the game logic.
     * This includes holding the game state, as well as being able to interpolate the locations of all a player's outposts,
     * subs, etc.
     *
     * No graphics will be used within this project. The graphics engine will need to reference the objects within this class to
     * determine how to draw.
     */
    public class Game
    {
        /// <summary>
        /// Time machine instance which controls the game state
        /// </summary>
        public TimeMachine TimeMachine;

        /// <summary>
        /// The random number generated used for all randomly selected events within the game.
        /// This includes things like outpost generation and specialist pool randomization.
        /// </summary>
        public static SeededRandom SeededRandom;

        /// <summary>
        /// Current Game configuration
        /// </summary>
        public static GameConfiguration GameConfiguration;

        /// <summary>
        /// Creates a new game using the provided GameConfiguration. Calling this constructor will trigger
        /// map generation and generate the map based on the GameConfiguration that was passed into the game.
        /// </summary>
        /// <param name="gameConfiguration">Settings that determine how the game should be configured during generation.</param>
        public Game(GameConfiguration gameConfiguration)
        {
            GameConfiguration = gameConfiguration;
            SeededRandom = new SeededRandom();

            // Creates a new game state and makes a time machine to reference the state
            GameState state = new GameState(gameConfiguration.PlayersInLobby.Select(it => new Player(it)).ToList());
            TimeMachine = new TimeMachine(state);

            // Creates the map generator with a random seed
            MapGenerator mapGenerator = new MapGenerator(gameConfiguration.MapConfiguration, state.GetPlayers(), TimeMachine);

            // Generate the map
            List<Outpost> generatedOutposts = mapGenerator.GenerateMap();

            // Add the outposts to the map
            state.GetOutposts().AddRange(generatedOutposts);

            // Create a global currency event.
            GlobalCurrencyProductionEvent.SpawnNewCurrencyEvent(TimeMachine);
        }

        public void LoadGameEvents(List<GameRoomEvent> gameEvents)
        {
            gameEvents
                .ConvertAll<GameEvent>(m => GameEventFactory.ParseGameEvent(m))
                .ForEach(parsedEvent =>
                {
                    if (parsedEvent != null)
                    {
                        TimeMachine.AddEvent(parsedEvent);
                    }
                });
        }

        /// <summary>
        /// Determines if the game is in a game over state. In Mining, ties are broken by
        /// whoever happens to be first in the list of players.
        /// </summary>
        /// <returns>null if the game is not over, or the Player who has won if it is over.</returns>
        public Player IsGameOver()
        {
            GameEndEvent? endEvent = (GameEndEvent)this.TimeMachine.GetPastEvents().FirstOrDefault(it => it is GameEndEvent);
            if (endEvent != null)
            {
                return this.TimeMachine.GetState()
                    .GetPlayers()
                    .FirstOrDefault(player => player.GetId() == endEvent.GetEventData().WinningPlayer.Id);
            }
            switch (GameConfiguration.GameSettings.Goal)
            {
                case Goal.Mining:
                    foreach (Player p in TimeMachine.GetState().GetPlayers())
                    {
                        if (!p.IsEliminated() && p.GetNeptunium() >= Constants.MiningNeptuniumToWin)
                        {
                            return p;
                        }
                    }

                    return null;

                case Goal.Domination:
                    foreach (Player p in TimeMachine.GetState().GetPlayers())
                    {
                        if (!p.IsEliminated() && TimeMachine.GetState().GetPlayerOutposts(p).Count >
                            TimeMachine.GetState().GetOutposts().Count / 2)
                        {
                            return p;
                        }
                    }

                    return null;

                // Other cases to be implemented

                default:
                    return null;
            }
        }
    }
}


text:
### Game Server

The GameServer is what will be hosted on both the client, to analyze everything about a game.
This object will NOT use any textured objects. THis is because textures take a log of computing power and hold the server up.

The GameServer will need to be able to generate a list of all of the events that occur within the game and generate a GameState.
The GameState object will then be used by the app to render content in the correct location.


text:
using System;
using System.Collections.Generic;
using System.Linq;
using Subterfuge.Remake.Core.GameEvents.Base;
using Subterfuge.Remake.Core.GameEvents.EventPublishers;
using Subterfuge.Remake.Core.GameEvents.PlayerTriggeredEvents;

namespace Subterfuge.Remake.Core.Timing
{
    public class TimeMachine : ITickEventPublisher
    {
        // List of Player Events mapped to each tick
        private List<GameEvent> eventQueue = new List<GameEvent>();

        // Current representation of the game state
        private GameState _gameState;

        private bool IsPaused = false;

        /// <summary>
        /// Creates a new instance of the TimeMachine. You will likely never need to call this as this is created in the
        /// `Game` object when the game is created.
        /// </summary>
        /// <param name="state">The initial GameState</param>
        public TimeMachine(GameState state)
        {
            _gameState = state;
        }

        /// <summary>
        /// Get the time machine's current state
        /// </summary>
        /// <returns>The GameState at the current time of the TimeMachine</returns>
        public GameState GetState()
        {
            return this._gameState;
        }

        /// <summary>
        /// Adds an event to the future event queue
        /// </summary>
        /// <param name="gameEvent">The game event to add to the Queue</param>
        public void AddEvent(GameEvent gameEvent)
        {
            if (!eventQueue.Contains(gameEvent))
            {
                eventQueue.Add(gameEvent);
            }
        }

        /// <summary>
        /// Adds an event to the future event queue
        /// </summary>
        /// <param name="gameEvent">The game event to add to the Queue</param>
        public void AddEvents(List<GameEvent> gameEvents)
        {
            gameEvents.ForEach(eventToAdd =>
            {
                if (!eventQueue.Contains(eventToAdd))
                {
                    eventQueue.Add(eventToAdd);
                }
            });
        }

        /// <summary>
        /// Removes a GameEvent from the game.
        /// </summary>
        /// <param name="gameEvent">The GameEvent to remove from the queue</param>
        public void RemoveEvent(GameEvent gameEvent)
        {
            eventQueue.Remove(gameEvent);
        }

        /// <summary>
        /// Jumps to a specific GameTick
        /// </summary>
        /// <param name="tick">The GameTick to jump to</param>
        public void GoTo(GameTick tick)
        {
            TimeMachineDirection direction = tick > _gameState.CurrentTick
                ? TimeMachineDirection.FORWARD
                : TimeMachineDirection.REVERSE;

            while (tick != GetCurrentTick())
            {
                if (direction == TimeMachineDirection.FORWARD)
                {
                    this._gameState.CurrentTick = this._gameState.CurrentTick.Advance(1);
                }

                if (IsPaused)
                {
                    var nonAdminEvents = eventQueue
                        .Where(it => !(it is UnpauseGameEvent) && !(it is PauseGameEvent))
                        .ToList();

                    if (direction == TimeMachineDirection.FORWARD)
                    {
                        // Make all events in the future get shoved 1 tick forward (except Unpause events)
                        nonAdminEvents.ForEach(it => it.OccursAt = it.OccursAt.Advance(1));
                    }
                    else
                    {
                        // Make all events in the future get shoved 1 tick forward (except Unpause events)
                        nonAdminEvents.ForEach(it => it.OccursAt = it.OccursAt.Rewind(1));
                    }
                }
                else
                {
                    // Trigger "OnTick" to allow listeners to generate in-game events.
                    // This is mainly useful for resource producers.
                    this.OnTick?.Invoke(this, new OnTickEventArgs()
                    {
                        Direction = direction,
                        CurrentState = this._gameState,
                        CurrentTick = this._gameState.CurrentTick,
                    });
                }

                eventQueue
                    .Where(it => it.OccursAt == this._gameState.CurrentTick)
                    .ToList()
                    .ForEach(it =>
                    {
                        if (direction == TimeMachineDirection.FORWARD)
                        {
                            it.ForwardAction(this);
                        }
                        else
                        {
                            it.BackwardAction(this);
                        }

                        this.OnGameEvent?.Invoke(this, new OnGameEventTriggeredEventArgs()
                        {
                            Direction = direction,
                            CurrentState = this._gameState,
                            CurrentTick = this._gameState.CurrentTick,
                            GameEvent = it,
                        });
                    });

                if (direction == TimeMachineDirection.REVERSE)
                {
                    // Remove non-player triggered events from the queue when going in reverse.
                    eventQueue.RemoveAll(it => it.OccursAt == this._gameState.CurrentTick && !(it is PlayerTriggeredEvent));
                    this._gameState.CurrentTick = this._gameState.CurrentTick.Rewind(1);
                }
            }
        }

        /// <summary>
        /// Gets the GameTick that the time machine is currently representing.
        /// </summary>
        /// <returns>The GameTick that the timeMachine is showing</returns>
        public GameTick GetCurrentTick()
        {
            return _gameState.CurrentTick;
        }

        /// <summary>
        /// Jumps to a specific GameEvent
        /// </summary>
        /// <param name="eventOfInterest">The GameEvent to jump to</param>
        public void GoTo(GameEvent eventOfInterest)
        {
            this.GoTo(eventOfInterest.OccursAt);
        }

        /// <summary>
        /// For debugging. Advances the timeMachine by a specified number of ticks.
        /// </summary>
        /// <param name="ticks">The number of ticks to advance</param>
        public void Advance(int ticks)
        {
            GameTick tick = this.GetCurrentTick().Advance(ticks);
            this.GoTo(tick);
        }

        /// <summary>
        /// For debugging. Rewinds the timeMachine by a specified number of ticks.
        /// </summary>
        /// <param name="ticks">The number of ticks to rewind</param>
        public void Rewind(int ticks)
        {
            GameTick tick = this.GetCurrentTick().Rewind(ticks);
            this.GoTo(tick);
        }

        /// <summary>
        /// Gets a list of the queued events
        /// </summary>
        /// <returns>A list of the events in the future event queue</returns>
        public List<GameEvent> GetQueuedEvents()
        {
            return eventQueue
                .Where(it => it.OccursAt > GetCurrentTick())
                .ToList();
        }

        public List<T> GetFutureEventsOf<T>() where T : GameEvent
        {
            return eventQueue
                .OfType<T>()
                .Where(it => it.OccursAt > GetCurrentTick())
                .ToList<T>();
        }

        /// <summary>
        /// Get past events
        /// </summary>
        /// <returns>A list of the events in the future event queue</returns>
        public List<GameEvent> GetPastEvents()
        {
            return eventQueue
                .Where(it => it.OccursAt <= GetCurrentTick())
                .ToList();
        }

        public void TogglePause(bool isPaused)
        {
            this.IsPaused = isPaused;
        }

        public event EventHandler<OnTickEventArgs>? OnTick;
        public event EventHandler<OnGameEventTriggeredEventArgs>? OnGameEvent;
    }
}


text:
using System;

namespace Subterfuge.Remake.Core.Timing
{
    /// <summary>
    /// Seeded random class to ensure that all randomization within the game is done from the same randomizer.
    /// This ensures that all randomly calculated values are calculated the same for all users.
    /// </summary>
    public class SeededRandom
    {
        private Random _generator;
        private int _currentRand;

        /// <summary>
        /// Seeded random constructor.
        /// </summary>
        public SeededRandom()
        {
            this._generator = new Random(Game.GameConfiguration.MapConfiguration.Seed);
            this._currentRand = 0;
        }

        /// <summary>
        /// Only needed for testing
        /// </summary>
        /// <param name="seed">The seed to generate random numbers from</param>
        public SeededRandom(int seed)
        {
            this._generator = new Random(seed);
            this._currentRand = 0;
        }

        /// <summary>
        /// Gets a random number between min and max
        /// </summary>
        /// <param name="min">The minimum</param>
        /// <param name="max">The maximum</param>
        /// <returns>A random number between min and max</returns>
        public int NextRand(int min, int max)
        {
            this._currentRand += 1;
            return this._generator.Next(min, max);
        }

        /// <summary>
        /// Generates a double between 0.0 and 1.0
        /// </summary>
        /// <returns>A random double</returns>
        public double NextDouble()
        {
            this._currentRand += 1;
            return this._generator.NextDouble();
        }

        /// <summary>
        /// Gets the n'th random number between min and max
        /// </summary>
        /// <param name="index">The n'th number to generate</param>
        /// <param name="min">The minimum</param>
        /// <param name="max">The maximum</param>
        /// <returns>The n'th random number between min and max</returns>
        public int GetRand(int index, int min, int max)
        {
            // Generate random numbers until you have generated N
            while(this._currentRand < index)
            {
                this._generator.NextDouble();
                this._currentRand++;
            }
            // Return the n'th number
            return this._generator.Next(min, max);
        }

        /// <summary>
        /// Gets the N'th double
        /// </summary>
        /// <param name="index">The N'th number to return</param>
        /// <returns>The N'th double</returns>
        public double GetDouble(int index)
        {
            while (this._currentRand < index)
            {
                this._generator.NextDouble();
                this._currentRand++;
            }
            return this._generator.NextDouble();
        }

    }
}


text:
using System;
using Subterfuge.Remake.Api.Network;

namespace Subterfuge.Remake.Core.Timing
{
    /// <summary>
    /// GameTick class to more easily go to and from the current DateTime/TimeStamp to in-game ticks
    /// </summary>
    public class GameTick
    {
        public override int GetHashCode()
        {
            return _tickNumber;
        }

        public override bool Equals(object obj)
        {
            GameTick? asTick = obj as GameTick;
            if (asTick is null)
                return false;

            return _tickNumber == asTick._tickNumber;
        }

        public static double MinutesPerTick { get; set; }= 15.0;
        private readonly int _tickNumber;

        /// <summary>
        /// GameTick constructor. Requires a DateTime and an integer tick number
        /// </summary>
        /// <param name="tickNumber">The integer number of the tick</param>
        public GameTick(int tickNumber) {
            this._tickNumber = tickNumber;
        }

        public GameTick(DateTime startTime, DateTime currentTime)
        {
            TimeSpan dateDelta = currentTime.Subtract(startTime);
            // Get minutes elapsed
            double minutesElapsed = dateDelta.TotalMinutes;
            // Determine the number of ticks past
            int ticksElapsed = (int)Math.Ceiling(minutesElapsed / MinutesPerTick);

            // Return a new gametick relative to the start time.
            this._tickNumber = ticksElapsed;
        }

        /// <summary>
        /// Generic GameTick constructor. Sets tick 0 at the current time.
        /// </summary>
        public GameTick()
        {
            this._tickNumber = 0;
        }

        public static GameTick fromGameConfiguration(GameConfiguration config)
        {
            GameTick.MinutesPerTick = config.GameSettings.MinutesPerTick;
            return new GameTick(config.TimeStarted, DateTime.UtcNow);
        }

        /// <summary>
        /// Gets the next GameTick
        /// </summary>
        /// <returns>The next GameTick</returns>
        public GameTick GetNextTick()
        {
            return new GameTick(this._tickNumber + 1);
        }

        /// <summary>
        /// Gets the previous GameTick
        /// </summary>
        /// <returns>The previous GameTick</returns>
        public GameTick GetPreviousTick()
        {
            if (this._tickNumber > 0)
            {
                return new GameTick(this._tickNumber - 1);
            }
            return this;
        }

        /// <summary>
        /// Returns a gametick that is a set number of ticks in the future
        /// </summary>
        /// <param name="ticks">The number of ticks in the future</param>
        /// <returns>A GameTick in the future</returns>
        public GameTick Advance(int ticks)
        {
            return new GameTick(this._tickNumber + ticks);
        }

        /// <summary>
        /// Returns a gametick that is a set number of ticks in the past
        /// </summary>
        /// <param name="ticks">The number of ticks in the past</param>
        /// <returns>A GameTick in the past</returns>
        public GameTick Rewind(int ticks)
        {
            if (this._tickNumber >= ticks)
            {
                return new GameTick(this._tickNumber - ticks);
            }
            return this.Rewind(this.GetTick());
        }

        /// <summary>
        /// Returns the DateTime that the tick started at
        /// </summary>
        /// <returns>the tick's start DateTime</returns>
        public DateTime GetDate(DateTime startTime)
        {
            return startTime.AddMinutes(this._tickNumber * MinutesPerTick);
        }

        /// <summary>
        /// Returns the tick number
        /// </summary>
        /// <returns>Tick number</returns>
        public int GetTick()
        {
            return this._tickNumber;
        }

        /*
         * Operator overloads.
         * Allows performing things like:
         * GameTick1 > GameTick2
         * etc.
         */
        public static bool operator > (GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() > secondArg.GetTick();
        }

        public static bool operator >=(GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() >= secondArg.GetTick();
        }

        public static bool operator <(GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() < secondArg.GetTick();
        }

        public static bool operator <=(GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() <= secondArg.GetTick();
        }

        public static bool operator ==(GameTick? firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() == secondArg.GetTick();
        }

        public static bool operator !=(GameTick? firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() != secondArg.GetTick();
        }

        public static int operator -(GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() - secondArg.GetTick();
        }

        public static int operator +(GameTick firstArg, GameTick secondArg)
        {
            return firstArg.GetTick() + secondArg.GetTick();
        }

    }
}


text:
namespace Subterfuge.Remake.Core.Generation
{
    public class IdGenerator
    {
        int _lastId = 0;

        public string GetNextId()
        {
            _lastId++;
            return _lastId.ToString();
        }
    }
}


text:
using System;
using System.Collections.Generic;
using System.Linq;
using Subterfuge.Remake.Core.Timing;

namespace Subterfuge.Remake.Core.Generation
{
    public class NameGenerator
    {
        private readonly SeededRandom _seeder;

        private List<string> _outpostNames;
        private readonly List<string> _fallbackNames = new List<string>();
        private readonly List<string> _selectedNames = new List<string>();

        /// <summary>
        /// Creates an instance of the name generator using an already generated seeded random tool.
        /// </summary>
        /// <param name="seeder">The seeder to use for random name selection</param>
        public NameGenerator(SeededRandom seeder)
        {
            this._seeder = seeder;
            this.PopulateNames();
        }

        /// <summary>
        /// Sets the list of outpost names to be pulled from.
        /// </summary>
        private void PopulateNames()
        {
            _outpostNames = new List<string>() {
            "Rokovo",
            "Latvia",
            "Pichu",  // Pokemon easer egg
            "Shiloh", // My dog. RIP <3
            "Dozer",
            "Nautilus",
            "Minceraft", // Pretty obvious
            "Sans", // Undertale
            "London",
            "Helsinki",
            "Subterfuge",
            "Lyons",
            "Norris",
            "Hooper",
            "Glover",
            "Mills",
            "Pueblo", // My hometown

            };

            // As an additional measure,
            // more names are derived from the existing names.
            this._generateDerivedOutpostNames();
        }

        /// <summary>
        /// Generates a random outpost name for an outpost
        /// </summary>
        /// <returns>The random outpost name</returns>
        public string GetRandomName()
        {
            // If there are pre-generated outposts to be selected from,
            // Choose one of them.
            if (_outpostNames.Count > 0)
            {
                int selection = _seeder.NextRand(0, _outpostNames.Count - 1);
                string name = _outpostNames[selection];
                _outpostNames.Remove(name);
                _selectedNames.Add(name);
                return name;
            }

            // If there are some derived names to pick from, select them
            if (_fallbackNames.Count > 0)
            {
                int selection = _seeder.NextRand(0, _fallbackNames.Count - 1);
                string name = _fallbackNames[selection];
                _fallbackNames.Remove(name);
                _selectedNames.Add(name);
                return name;
            }

            // Otherwise, generate a random shuffled string
            // Try 10 scrambles before giving up.
            int scrambleCounter = 0;
            while (scrambleCounter < 10)
            {
                // Pick a random name from the list of names.
                int randomName = _seeder.NextRand(0, _selectedNames.Count - 1);
                // Shuffle the letters in the outpost name.
                string shuffled = this._shuffleString(_selectedNames[randomName]);
                if (!_selectedNames.Contains(shuffled))
                {
                    _selectedNames.Add(shuffled);
                    return shuffled;
                }

                scrambleCounter++;
            }

            // As a final measure, if a string couldn't be derived, generate a random name.
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string generated = new string(Enumerable.Repeat(chars, 8)
                .Select(s => s[_seeder.NextRand(0, s.Length)]).ToArray());

            return this._capitolizeFirstLetter(generated);
        }

        /// <summary>
        /// Determines if the name generator has default names able to be generated.
        /// </summary>
        /// <returns>If the default set of names has been exhausted</returns>
        public bool HasNames()
        {
            return _outpostNames.Count > 0;
        }

        /// <summary>
        /// Randomly shuffles the letters within a string
        /// </summary>
        /// <param name="str">The string to shuffle</param>
        /// <returns>The shuffled string.</returns>
        private string _shuffleString(string str)
        {
            var list = new SortedList<int,char>();
            foreach (var c in str)
            {
                int position = _seeder.NextRand(0, 10000);
                while (list.ContainsKey(position))
                {
                    position = _seeder.NextRand(0, 10000);
                }
                list.Add(position, c);
            }

            string generated = new string(list.Values.ToArray());
            return this._capitolizeFirstLetter(generated);
        }

        /// <summary>
        /// Generates a list of fallback names by deriving names from the default list.
        /// </summary>
        private void _generateDerivedOutpostNames()
        {
            List<string> derived = new List<string>();
            foreach(string deriveFrom in _outpostNames)
            {
                foreach (string appendFrom in _outpostNames)
                {
                    if (appendFrom != deriveFrom)
                    {
                        // Swap first letter
                        string derivedString = appendFrom.Substring(0, 1) + deriveFrom.Substring(1);
                        derivedString = this._capitolizeFirstLetter(derivedString);
                        if (!_outpostNames.Contains(derivedString) && !derived.Contains(derivedString))
                        {
                            derived.Add(derivedString);
                        }

                        // Swap first 3 letters
                        derivedString = appendFrom.Substring(0, 3) + deriveFrom.Substring(3);
                        derivedString = this._capitolizeFirstLetter(derivedString);
                        if (!_outpostNames.Contains(derivedString) && !derived.Contains(derivedString))
                        {
                            derived.Add(derivedString);
                        }
                    }
                }
            }
            // Add to a fallback list instead of the default list.
            // This ensures that the normal outpost list names will get selected first.
            _fallbackNames.AddRange(derived);
        }

        /// <summary>
        /// Returns a string of the form "Abcdefghi", where the first is capitol and all others are lower case
        /// </summary>
        /// <param name="str">The input string to format</param>
        /// <returns>Formatted string</returns>
        private string _capitolizeFirstLetter(string str)
        {
            str = str.ToLower();
            if (str.Length == 0)
                return str;
            if (str.Length == 1)
                return Char.ToUpper(str[0]).ToString();
            return char.ToUpper(str[0]) + str.Substring(1);
        }

        /// <summary>
        /// Function that returns a list of outpost names that have been generated which match the user's guess.
        /// For example, passing in "Lo" might return: ["Lokovo", "Loyns"]
        /// </summary>
        /// <param name="suggestionString">The user's string</param>
        /// <returns>A list of possible outpost names that match the user's selection</returns>
        public List<string> SuggestNames(string suggestionString)
        {
            return _selectedNames.Where(x => x.StartsWith(suggestionString)).ToList();
        }

    }
}


text:
using System;
using System.Collections.Generic;
using System.Linq;
using Subterfuge.Remake.Api.Network;
using Subterfuge.Remake.Core.Entities.Components;
using Subterfuge.Remake.Core.Entities.Positions;
using Subterfuge.Remake.Core.Entities.Specialists;
using Subterfuge.Remake.Core.Players;
using Subterfuge.Remake.Core.Timing;
using Subterfuge.Remake.Core.Topologies;

namespace Subterfuge.Remake.Core.Generation
{
    public class MapGenerator
    {

        /// <summary>
        /// A list of the generated outposts.
        /// </summary>
        private readonly List<Outpost> _outposts = new List<Outpost>();

        /// <summary>
        /// To generate outpost names
        /// </summary>
        private readonly NameGenerator _nameGenerator;

        /// <summary>
        /// Rft for map wrapping
        /// </summary>
        private readonly Rft _map = new Rft(300, 300);

        /// <summary>
        /// The generation configuration object.
        /// </summary>
        private readonly MapConfiguration _mapConfiguration;

        /// <summary>
        /// Id Generator for outposts.
        /// </summary>
        private readonly IdGenerator _generator = new IdGenerator();

        private readonly List<Player> _players;

        private TimeMachine _timeMachine;

        /// <summary>
        /// Map Generation constructor to seed map generation
        /// </summary>
        /// <param name="mapConfiguration">The map configuration parameters</param>
        /// <param name="players">The players in the game</param>
        public MapGenerator(
            MapConfiguration mapConfiguration,
            List<Player> players,
            TimeMachine timeMachine
        ) {
            this._players = players;
            this._mapConfiguration = mapConfiguration;
            _timeMachine = timeMachine;

            if (players.Count <= 0)
            {
                throw new PlayerCountException("A game must have at least one player.");
            }

            if (mapConfiguration.OutpostsPerPlayer <= 0)
            {
                throw new OutpostPerPlayerException("outpostsPerPlayer must be greater than or equal to one.");
            }

            this._nameGenerator = new NameGenerator(Game.SeededRandom);

            // Set the map size.
            int halfPlayers = (int)(Math.Floor(players.Count / 2.0));
            RftVector.Map = new Rft(mapConfiguration.MaximumOutpostDistance * 4, halfPlayers * mapConfiguration.MaximumOutpostDistance * 2);
        }

        /// <summary>
        /// Translates a given set of outposts by a set amount, duplicating the outposts at a different coordinate location.
        /// Result from this function is a newly generated set of outposts offset by the specified translation.
        /// </summary>
        /// <param name="outposts">A list of outposts to translate</param>
        /// <param name="translation">The translation to apply</param>
        /// <returns>A list of translated outposts</returns>
        private List<Outpost> TranslateOutposts(List<Outpost> outposts, RftVector translation)
        {
            // Generate a random rotation of 0/90/180/270 so the map doesn't appear to be tiled.
            double rotation = Game.SeededRandom.NextRand(0, 3) * Math.PI / 2;

            // List to store the newly translated outposts
            List<Outpost> translatedOutposts = new List<Outpost>();

            // Loop through the original outposts.
            foreach (Outpost outpost in outposts)
            {
                // Get original outpost location
                RftVector position = outpost.GetComponent<PositionManager>().GetInitialPosition();

                // New vector for the copied location
                RftVector newPosition = new RftVector(RftVector.Map, position.X, position.Y);

                // Undo the rotation and apply a new rotation.
                // https://stackoverflow.com/questions/620745/c-rotating-a-vector-around-a-certain-point
                double cs = Math.Cos(rotation);
                double sn = Math.Sin(rotation);

                double translatedX = position.X;
                double translatedY = position.Y;

                double resultX = translatedX * cs - translatedY * sn;
                double resultY = translatedY * cs - translatedX * sn;

                resultX += _mapConfiguration.MaximumOutpostDistance;
                resultY += _mapConfiguration.MaximumOutpostDistance;

                newPosition.X = (float)resultX;
                newPosition.Y = (float)resultY;

                // Apply the translation to offset the outpost.
                newPosition += translation;

                // Add a new outpost to the translated outposts list
                Outpost newOutpost = CreateOutpost(newPosition, outpost.GetOutpostType());
                newOutpost.GetComponent<DrillerCarrier>().SetOwner(outpost.GetComponent<DrillerCarrier>().GetOwner());
                newOutpost.GetComponent<IdentityManager>().SetName(_nameGenerator.GetRandomName());
                translatedOutposts.Add(newOutpost);
            }
            // Return the translated outposts.
            return translatedOutposts;
        }

        /// <summary>
        /// Generates a set of outposts for one player based on the map generation configurations.
        /// </summary>
        /// <returns>A list of a single player's outposts</returns>
        private List<Outpost> GeneratePlayerOutposts()
        {
            // List of a player's outposts.
            List<Outpost> playerOutposts = new List<Outpost>();

            // Loop to generate outposts until the number of generated outposts is valid
            while (playerOutposts.Count < _mapConfiguration.OutpostsPerPlayer + _mapConfiguration.DormantsPerPlayer)
            {
                // calculate the new outposts location within allowable radius
                var distance = Game.SeededRandom.NextDouble() * (_mapConfiguration.MaximumOutpostDistance - _mapConfiguration.MinimumOutpostDistance) + _mapConfiguration.MinimumOutpostDistance;
                var direction = Game.SeededRandom.NextDouble() * Math.PI * 2;

                // Determine the type of outpost that is generated
                OutpostType[] validTypes =
                {
                    OutpostType.Factory,
                    OutpostType.Generator,
                    OutpostType.Watchtower,
                };
                OutpostType type = validTypes[Game.SeededRandom.NextRand(0, 3)];

                //convert distance & direction into vector X and Y
                var x = Convert.ToInt32(Math.Cos(direction) * distance);
                var y = Convert.ToInt32(Math.Sin(direction) * distance);
                var currentOutpostPosition = new RftVector(_map, x, y);

                var usableLocation = true;
                // Determine if the generated location is too close to another outpost
                int idx;
                for (idx = 0; idx < playerOutposts.Count & usableLocation; idx++)
                {
                    // Get the X and Y pos to find distance
                    var otherOutpost = playerOutposts[idx];
                    var vectorDistance = otherOutpost.GetComponent<PositionManager>().GetInitialPosition() - currentOutpostPosition;

                    //ensure that the new location is not too close to other outposts
                    if (vectorDistance.Length() < _mapConfiguration.MinimumOutpostDistance)
                    {
                        usableLocation = false;
                    }
                }

                // If the location is not too close to another outpost, add the outpost to the list.
                if (usableLocation || playerOutposts.Count == 0)
                {
                    var currentOutpost = CreateOutpost(currentOutpostPosition, type);
                    currentOutpost.GetComponent<IdentityManager>().SetName(_nameGenerator.GetRandomName());
                    playerOutposts.Add(currentOutpost);
                }
            }

            // Return list of generated outposts.
            return playerOutposts;
        }

        /// <summary>
        ///  From a list of generated outposts, sets the X closest outposts to the centroid to be owned by the specified player.
        ///  Remaining outposts are dormant.
        /// </summary>
        /// <param name="outposts">The list of outposts to be owned</param>
        /// <param name="player">The player to own the outposts</param>
        private void SetOutpostOwner(List<Outpost> outposts, Player player)
        {
            // A list of the outposts that are closest to the centroid of the player's land.
            // This list is the list of outposts that the player should own.
            List<Outpost> closestOutposts = new List<Outpost>();

            // The centroid
            RftVector centroid = new RftVector(_map, 0, 0);

            // Loop outposts in the generation
            foreach (Outpost o in outposts)
            {
                // Add outposts to the closestOutposts list until it has "outpostsPerPlayer" outposts
                if (closestOutposts.Count < _mapConfiguration.OutpostsPerPlayer)
                {
                    closestOutposts.Add(o);
                }
                else
                {
                    // Determine the distance to the current outpost from the centroid
                    float currentDistance = (centroid - o.GetComponent<PositionManager>().GetInitialPosition()).Length();

                    // Sort the closestOutpost list to determine the farthest outpost (maybe this one is closer).
                    closestOutposts.Sort((a, b) => (int)((centroid - a.GetComponent<PositionManager>().GetInitialPosition()).Length() - (centroid - b.GetComponent<PositionManager>().GetInitialPosition()).Length()));

                    // Determine the distance of the farthest outpost
                    float farthestDistance = (centroid - closestOutposts[_mapConfiguration.OutpostsPerPlayer - 1].GetComponent<PositionManager>().GetInitialPosition()).Length();

                    // If the current outpost is closer, put the current outpost in the list, replacing the farther outpost.
                    if (currentDistance < farthestDistance)
                    {
                        closestOutposts[_mapConfiguration.OutpostsPerPlayer - 1] = o;
                    }
                }
            }

            // Once the closest outposts are determined, set the owner.
            // And provide some drillers.
            foreach (Outpost closeOutpost in closestOutposts)
            {
                closeOutpost.GetComponent<DrillerCarrier>().SetOwner(player);
                closeOutpost.GetComponent<DrillerCarrier>().AlterDrillers(Constants.InitialDrillersPerOutpost);
            }
        }

        /// <summary>
        /// Generates the map
        /// </summary>
        /// <returns>A list of outposts within the map</returns>
        public List<Outpost> GenerateMap()
        {
            // To generate the map we will:
            // 1. Generate a single player's outposts
            // 2. Translate the outposts for each player in the game
            // 3. Set the ownership

            // Generate a set of a single player's outposts:
            List<Outpost> firstPlayerOutposts = this.GeneratePlayerOutposts();

            // Set the ownership of the central X generated outposts to the first player.
            if (this._mapConfiguration.OutpostsPerPlayer > 0 && _players.Count > 0)
                this.SetOutpostOwner(firstPlayerOutposts, _players[0]);

            // Tile the outposts based on the # of players on a 2xn grid
            // Example:
            //
            // 1 3 5 7 9
            // 2 4 6 8 10
            //
            // Each # is a player, each player has a (2 * maxSeedDistance)x(2 * maxSeedDistance) area
            int width = (int)(Math.Ceiling(_players.Count / 2.0f)); // calculate n
            int height = 2;

            // Counter for looping through all players
            int widthCounter = 1;
            int heightCounter = 1;

            // Count # of generations
            int playersGenerated = 0;

            // Loop from left to right of player grid
            while (widthCounter <= width)
            {
                // Loop from top to bottom of player grid
                while (heightCounter <= height)
                {
                    // Stop early if there is an odd number of players. Ex. w/ 7 players we don't want to generate the 8th grid.
                    if (playersGenerated < _players.Count)
                    {
                        // Translate the first player's outposts based on the width/height counter for this player.
                        // Function also applies a random rotation.
                        List<Outpost> translatedOutposts = this.TranslateOutposts(firstPlayerOutposts, new RftVector(_map, _mapConfiguration.MaximumOutpostDistance * widthCounter * 2, _mapConfiguration.MaximumOutpostDistance * heightCounter * 2));

                        // Update the owner to the new player
                        bool queenGenerated = false;
                        foreach (Outpost o in translatedOutposts)
                        {
                            if (o.GetComponent<DrillerCarrier>().GetOwner() != null)
                            {
                                o.GetComponent<DrillerCarrier>()
                                    .SetOwner(_players[(widthCounter - 1) * 2 + (heightCounter - 1)]);
                            }

                            if (!queenGenerated)
                            {
                                o.GetComponent<SpecialistManager>().HireSpecialist(SpecialistFactory.CreateSpecialist(SpecialistTypeId.Queen, 0, o.GetComponent<DrillerCarrier>().GetOwner()), _timeMachine);
                                queenGenerated = true;
                            }
                        }

                        // Add the outposts to the generated list.
                        this._outposts.AddRange(translatedOutposts);

                        // Increment the map generation loop
                        heightCounter++;
                        playersGenerated++;
                    }
                    else
                    {
                        // Return the outposts if we stop early.
                        return this._outposts;
                    }
                }
                // After a height of 2, reset the height to 1 but increase the width
                widthCounter++;
                heightCounter = 1;
            }

            // Return a list of the generated outposts.
            return this._outposts;
        }

        /// <summary>
        /// Creates an outpost of the given type and given position.
        /// </summary>
        /// <param name="outpostPosition">Position of outpost</param>
        /// <param name="type"></param>
        /// <returns></returns>
        private Outpost CreateOutpost(RftVector outpostPosition, OutpostType type)
        {
            switch (type)
            {
                case OutpostType.Factory: return new Factory(_generator.GetNextId(), outpostPosition, _timeMachine);
                case OutpostType.Generator: return new Generator(_generator.GetNextId(), outpostPosition, _timeMachine);
                case OutpostType.Mine: return new Mine(_generator.GetNextId(), outpostPosition, _timeMachine);
                case OutpostType.Watchtower: return new Watchtower(_generator.GetNextId(), outpostPosition, _timeMachine);
                default: throw new OutpostTypeException("Invalid Outpost Type");
            }
        }

    }
    public class PlayerCountException : Exception
    {
        public PlayerCountException(string exception) : base(exception)
        {

        }
    }

    public class OutpostPerPlayerException : Exception
    {
        public OutpostPerPlayerException(string message) : base(message)
        {

        }
    }

    public class OutpostTypeException : Exception
    {
        public OutpostTypeException(string message) : base(message)
        {

        }
    }
}


text:
using System;
using System.Collections.Generic;
using Subterfuge.Remake.Api.Network;
using Subterfuge.Remake.Core.Entities.Specialists;
using Subterfuge.Remake.Core.Players.Currency;

namespace Subterfuge.Remake.Core.Players
{
    /// <summary>
    /// An instance of a player
    /// </summary>
    public class Player
    {
        /// <summary>
        /// The name or alias of the player
        /// </summary>
        public SimpleUser PlayerInstance { get; }

        /// <summary>
        /// The number of mines the player has drilled.
        /// </summary>
        private int _numMinesBuilt;

        /// <summary>
        /// The player's total amount of neptunium mined
        /// </summary>
        private int _neptunium;

        /// <summary>
        /// The player's status in game; false if player is alive (i.e. controls their queen)
        /// </summary>
        private bool _isEliminated;

        /// <summary>
        /// The player's currency tracker
        /// </summary>
        public CurrencyManager CurrencyManager;

        public SpecialistPool SpecialistPool;

        /// <summary>
        /// Creates a player from a protobuf player.
        /// </summary>
        /// <param name="user">protobuf player</param>
        public Player(User user)
        {
            this.PlayerInstance = user.ToSimpleUser();
            this._numMinesBuilt = 0;
            this._neptunium = 0;
            this._isEliminated = false;
            this.CurrencyManager = new CurrencyManager();
        }

        public Player(SimpleUser playerInstance)
        {
            this.PlayerInstance = playerInstance;
            this._numMinesBuilt = 0;
            this._neptunium = 0;
            this._isEliminated = false;
            this.CurrencyManager = new CurrencyManager();
        }

        public Player(
            SimpleUser playerInstance,
            List<SpecialistTypeId> specialistPool
        ) {
            this.PlayerInstance = playerInstance;
            this._numMinesBuilt = 0;
            this._neptunium = 0;
            this._isEliminated = false;
            this.CurrencyManager = new CurrencyManager();
            this.SpecialistPool = new SpecialistPool(this, Game.SeededRandom, specialistPool);
        }

        /// <summary>
        /// Gets the player's id
        /// </summary>
        /// <returns>The player's database ID</returns>
        public string GetId()
        {
            return this.PlayerInstance.Id;
        }

        /// <summary>
        /// Get the player's username
        /// </summary>
        /// <returns>The player's username</returns>
        public string GetPlayerName()
        {
            return this.PlayerInstance.Username;
        }

        /// <summary>
        /// Increases or decreases the amount of neptunium the player has. Pass a negative value for neptunium to decease.
        /// </summary>
        /// <param name="neptunium">The value to change _neptunium by</param>
        public int AlterNeptunium(int neptunium)
        {
            this._neptunium += neptunium;
            return neptunium;
        }

        public int GetNeptunium()
        {
            return this._neptunium;
        }

        /// <summary>
        /// Increases or decreases the amount of mines the player has constructed. Pass a negative value to decrease the number of mines built.
        /// </summary>
        /// <param name="numMines">The value to change _numMinesBuilt by</param>
        public void AlterMinesDrilled(int numMines)
        {
            _numMinesBuilt += numMines;
        }

        public int GetMinesDrilled()
        {
            return _numMinesBuilt;
        }

        /// <summary>
        /// Determines the amount of drillers required to drill a new mine based on the amount of mines the player has already drilled.
        /// </summary>
        /// <returns></returns>
        public int GetRequiredDrillersToMine()
        {
            if (GetMinesDrilled() < Constants.MiningCostInitial.Length)
            {
                return Constants.MiningCostInitial[GetMinesDrilled()];
            }
            else
            {
                return Constants.MiningCostInitial[Constants.MiningCostInitial.Length - 1] + (GetMinesDrilled() - Constants.MiningCostInitial.Length + 1) * Constants.MiningCostIncrease;
            }
        }

        public void SetEliminated(bool isEliminated)
        {
            this._isEliminated = isEliminated;
        }

        public bool IsEliminated()
        {
            return this._isEliminated;
        }

        public override bool Equals(object? obj)
        {
            Player? other = obj as Player;
            if (other == null)
                return false;
            return Equals(other);
        }

        public override int GetHashCode()
        {
            return PlayerInstance.GetHashCode();
        }

        private bool Equals(Player other)
        {
            return other.PlayerInstance.Id == this.PlayerInstance.Id;
        }
    }
}


text:
using System.Collections.Generic;

namespace Subterfuge.Remake.Core.Players.Currency
{
	public class CurrencyManager
	{
		/// <summary>
		/// A table of all the player's currencies
		/// </summary>
		private readonly Dictionary<CurrencyType, Subterfuge.Remake.Core.Players.Currency.Currency> _currencies = new Dictionary<CurrencyType, Subterfuge.Remake.Core.Players.Currency.Currency>();

		/// <summary>
		/// Creates a new currency as long as one with the same name doesn't already exist. Returns false if failed.
		/// </summary>
		/// <param name="currencyType">The type of currency (specified in Currency.cs CurrencyType enum)</param>
		/// <param name="currencyValue">The inital value of the currency</param>
		/// <param name="canBeNegative">Can the currency go into the negatives</param>
		/// <returns>True if succeeded</returns>
		private bool CreateCurrency(CurrencyType currencyType, int currencyValue, bool canBeNegative = true)
		{
			if (_currencies.ContainsKey(currencyType) == false)
			{
				Subterfuge.Remake.Core.Players.Currency.Currency newCurrency = new Subterfuge.Remake.Core.Players.Currency.Currency(currencyValue, canBeNegative);
				_currencies.Add(currencyType, newCurrency);
				return true; // Return true if the value was successfully created
			}
			return false; // Return false if the value was not created
		}

		/// <summary>
		/// Add a value to a created currency. Returns false if failed. Pass a negative value to subtract.
		/// </summary>
		/// <param name="currencyType">The type of currency (specified in Currency.cs CurrencyType enum)</param>
		/// <param name="addition">The value to add to the currency. Make negative if you wish to subtract.</param>
		/// <returns>True if succeeded</returns>
		public bool AddCurrency(CurrencyType currencyType, int addition)
		{
			if (_currencies.ContainsKey(currencyType))
			{
				bool canGoNegative = _currencies[currencyType].CanBeNegative;
				int oldValue = _currencies[currencyType].Value;
				int newValue = oldValue + addition;

				if (newValue < 0)
				{
					if (canGoNegative)
					{
						_currencies[currencyType].Value = newValue;
						return true;
					}
					return false;
				}
				_currencies[currencyType].Value = newValue;
				return true;
			}
			return SetCurrency(currencyType, addition);
		}

		/// <summary>
		/// Directly sets the currency's value. Returns false if failed. Must adhere to the CanBeNegative value.
		/// </summary>
		/// <param name="currencyType">The type of currency (specified in Currency.cs CurrencyType enum)</param>
		/// <param name="newValue">The value of the currency</param>
		/// <param name="canGoNegative">Used when creating a new currency, or altering the bool of another one</param>
		/// <returns>True if succeeded</returns>
		public bool SetCurrency(CurrencyType currencyType, int newValue, bool canGoNegative = true)
		{
			if (_currencies.ContainsKey(currencyType))
			{
				if (newValue < 0)
				{
					if (_currencies[currencyType].CanBeNegative)
					{
						_currencies[currencyType].Value = newValue;
						return true;
					}
					return false;
				}
				_currencies[currencyType].Value = newValue;
				return true; // Successfully set negative value
			}
			CreateCurrency(currencyType, newValue, canGoNegative);
			return true;
		}

		/// <summary>
        /// Used to get the value of a certain currency
        /// </summary>
        /// <param name="currencyType">The type of currency (specified in Currency.cs CurrencyType enum)</param>
        /// <returns> A number when succeeded</returns>
		public int? GetCurrency(CurrencyType currencyType)
        {
			if (_currencies.ContainsKey(currencyType))
            {
				return _currencies[currencyType].Value;
            }
			return 0;
        }
	}
}


text:
namespace Subterfuge.Remake.Core.Players.Currency
{
    /// <summary>
	/// A currency object. This is used by the CurrencyManager.
	/// </summary>
    class Currency
    {
        public int Value;
        public readonly bool CanBeNegative;

        /// <summary>
        /// THe currency class constructor
        /// </summary>
        /// <param name="value">The initial currency value</param>
        /// <param name="canBeNegative">If the currency can be negative</param>
        public Currency(int value = 0, bool canBeNegative = true)
        {
            this.Value = value;
            this.CanBeNegative = canBeNegative;
        }
    }

    /// <summary>
	/// The enum that lists the currency types.
	/// </summary>
    public enum CurrencyType
    {
        Specialist
    }
}


text:
using System;
using System.Collections.Generic;
using Subterfuge.Remake.Core.Entities.Components;

namespace Subterfuge.Remake.Core.Entities
{
    public abstract class Entity : IEntity
    {
        private readonly Dictionary<Type, EntityComponent> _componentMap = new Dictionary<Type, EntityComponent>();

        public void AddComponent(EntityComponent component)
        {
            _componentMap[component.GetType()] = component;
        }

        public T GetComponent<T>()
            where T : EntityComponent
        {
            if (_componentMap.ContainsKey(typeof(T)))
            {
                return _componentMap[typeof(T)] as T ?? throw new ComponentDoesNotExistException($"Found component {typeof(T)} but it was null.");
            }
            throw new ComponentDoesNotExistException($"Looking for component of {typeof(T)} but the component was not present.");
        }

        public bool HasComponent<T>()
            where T : EntityComponent
        {
            return _componentMap.ContainsKey(typeof(T));

        }
    }

    public interface IEntity
    {
        bool HasComponent<T>() where T : EntityComponent;
        T GetComponent<T>() where T : EntityComponent;
        void AddComponent(EntityComponent component);
    }

    public class ComponentDoesNotExistException : Exception
    {
        public ComponentDoesNotExistException(string detail) : base(detail){}
    }
}


text:
using Subterfuge.Remake.Core.Entities.Components;
using Subterfuge.Remake.Core.Players;
using Subterfuge.Remake.Core.Timing;

namespace Subterfuge.Remake.Core.Entities
{
    /// <summary>
    /// An instance of a Sub
    /// </summary>
    public class Sub : Entity
    {
        /// <summary>
        /// Sub constructor
        /// </summary>
        /// <param name="id">The ID of the sub</param>
        /// <param name="source">The initial location of the sub</param>
        /// <param name="destination">The destination of the sub</param>
        /// <param name="launchTime">The time of launch</param>
        /// <param name="drillerCount">The amount of drillers to launch</param>
        /// <param name="owner">The owner</param>
        public Sub(
            string id,
            IEntity source,
            IEntity destination,
            GameTick launchTime,
            int drillerCount,
            Player owner,
            TimeMachine timeMachine
        )
        {
            AddComponent(new DrillerCarrier(this, drillerCount, owner));
            AddComponent(new SpeedManager(this, 1.0f));
            AddComponent(new PositionManager(this, source, destination, launchTime, timeMachine));
            AddComponent(new SpecialistManager(this));
            AddComponent(new IdentityManager(this, id));
            AddComponent(new ShieldManager(this, 0));
            AddComponent(new SubLauncher(this));
            AddComponent(new VisionManager(this, Constants.BaseOutpostVisionRadius * 0.2f));
        }
    }
}