Engine - Rogue-Frontier/Rogue-Frontier GitHub Wiki
Transcendence Rogue Frontier contains at least 16,000 lines of code. Much of this code implements world, movement, combat, and UI mechanics.
.NET 5.0 is a cross-platform runtime that allows us to build cross-platform applications
MonoGame is a framework on which SadConsole runs.
SadConsole is a tile rendering engine.
SadConsole provides the Console
class onto which we can render tiles onto a grid. We call Update
to update the logic, Render
to draw the console onto the screen, ProcessKeyboard
to handle keyboard controls, and ProcessMouse
to handle mouse controls.
A console may call ProcessKeyboard
or ProcessMouse
on another console to handle input.
Render
is automatically called every frame if the console is a child of the current screen and has IsVisible
. Additionally, we can call Render
to draw a console on the frame regardless of whether it should be visible. Consoles show up in the order that we call Render
- first console is the last layer and the last console is the top layer.
By default, a Console holds Children
elements provided by the SadConsole library and placed by the programmer. To implement game-specific code, we usually subclass Console
and override Update
and Render
functions. To separate logic from graphics, we use Update
for game logic only and Render
for graphics only.
We strongly follow the principle Composition over Inheritance.
- Graphics classes (e.g. the menus and displays which drive SadConsole) may inherit from Console
- All other classes may not use inheritance.
- Common behaviors should be split into separate modules.
We follow the principle of modular design. Groups of functions that address a common task go into their own modules.
To enable serialization, we must avoid using lambda functions at all costs. Instead, we encapsulate functions into container classes. For the most part, we keep most class members public for easy, automatic serialization.
The World
keeps track of all active game objects.
We want to avoid modifying the entity/effect/event sets in the middle of an update. We keep added
and removed
sets which we use to update the entity/effect/event sets after each update. We also remove objects that are no longer active
. Objects should call the appropriate Add
and Remove
functions if they must manually remove other objects from the world (e.g. without setting active
to false).
The BaseShip is a structure that contains systems and logic common to both PlayerShip and AIShip. This includes basic movement, damage, and destruction mechanics. The BaseShip also holds hooks for events like Damaged and Destroyed.
The EnergySystem
handles fuel mechanics for the player. EnergySystem
requires access to the ship's DeviceSystem
to calculate power usage and update Reactor energy usage.
DeviceSystem
maintains collections for the ship's installed devices.
DamageSystem
implements HP and armor mechanics, and automatically calls Destroy on the object.
HPSystem
implements a basic, type-neutral HP system. Objects use a single HP counter and get destroyed upon reaching 0 HP.
LayeredArmorSystem
implements a simple system of armor segments. Each armor segment has an HP counter. Damage is first applied to the topmost armor segment, then overflows to the next armor segment until all of it is absorbed. Objects get destroyed upon all armor segments reaching 0 HP.
The IShip
interface determines the visible methods shared by both PlayerShip
and AIShip
. Noted: We may want to consolidate PlayerShip
and AIShip
into a single class, with differentiating behaviors implemented entirely via Controller
.
PlayerShip
implements logic for the player-controlled ship.
Player
holds information specific to the player character, including name, genome, and money. We should also keep known station information here. The PlayerShip
currently does not run on Controller
, so consider the Player
as the Controller
equivalent for now.
AIShip
implements behaviors for AI-controlled ships.
The IOrder
interface determines the visible methods for AIShip
behavior modules. We call Update(IShip owner)
to implement the behavior. Note that we pass in the owner because an order could be shared by multiple ships, and that orders are not exclusive to AIShip
(e.g. we can control PlayerShip
behavior)
The Controller
is the main IOrder
that runs on the ship. Controller
manages special ship behaviors and sub-orders.
We implement a variety of orders that any ship may perform. Some are composite orders.
The PlayerMain
console manages the main loop, player controls and UI during gameplay. We split UI elements into separate consoles so that we can update and draw each element as needed.
The Readout
shows the player basic text-based information about Energy, Device, HP, Targeting, and Messaging systems.
The Vignette
shows the colored border around the player field of view.
The EdgeMap shows objects beyond the player's current field of view as colored hash symbols on the edge of the screen.
The MegaMap
allows the player to scale the field of view and see more surroundings.