Occult Project
System-driven cult management game
The Context & Design Philosophy
Occult Project is a turn-based management game where the player must expand a secret society across a territory. The core mechanic relies on balancing resources: maximizing Influence to reach 100% while keeping Police suspicion and Follower attrition under control.
Co-programming the game alongside Sophia Delaty, my personal focus was to architect a highly modular, data-driven foundation. Because strategy games require constant balancing tweaks from our Game Designer (Samuel Geneste), hardcoding values was not an option.
I engineered a system entirely reliant on Scriptable Objects (SSO) and Event Channels (RSE). This structure decoupled the game logic from the user interface, ensuring that UI updates, card effects, and region calculations communicate seamlessly without creating a tangled web of dependencies.
My Contributions
- 01 Systems & Architecture Designed a resilient Event-Driven architecture and a robust state machine, handling complex mathematical modifiers and preventing negative value crashes.
- 02 Advanced Polymorphism Engineered generic classes for card and apostle effects, allowing infinite combinations of modifiers without touching the core codebase.
- 03 Predictive UI & Tooltips Developed a dynamic interface with contextual tooltips that automatically calculates and previews complex mathematical outcomes before the player commits to an action.
Learning & Implementation
01. Generic Architecture & Polymorphism
Handling different types of entities (Action Cards, Apostles) modifying the same regions could easily lead to duplicated code. To solve this, I leveraged C# generics (<T>) and inheritance to create a unified effect pipeline.
By creating an abstract base class SO_Effect<T>, the system encapsulates the logic while strongly typing the execution context. Child classes like SO_ActionCardEffect share the same execution contract via override, but can implement their own specific behaviors using virtual methods.
This architecture enabled me to make a specific behaviour for an Event system, which would have affected global resources rather than a specific region. Unfortunately, this system was not included in the final draft.
using Sirenix.OdinInspector;
using UnityEngine;
// Generic base class for all effects in the game
public abstract class SO_Effect<T> : ScriptableObject
{
public bool IsInstantEvent = true;
public abstract void ExecuteEffect(T context);
}
// Inherited class specifically for Region contexts
public abstract class SO_ActionCardEffect : SO_Effect<RSO_RegionResources>
{
[TextArea] public string TooltipText;
// The region is dynamically injected as the context
public override void ExecuteEffect(RSO_RegionResources region)
{
// Custom effect logic here
}
public virtual float GetModifierForRessource(ResourceType type, RSO_RegionResources regionRuntimeDatas)
{
return 0;
}
}
02. Non-Intrusive Tutorial State Machine
Integrating a tutorial at the end of production is notoriously dangerous, as it often breaks existing logic. Instead of hardcoding tutorial checks into the gameplay scripts, I built a secure State Machine driven by Enums.
This acts as an external layer of "logical gates". The narrative events and UI blockers listen to the state machine via Runtime Scriptable Object (RSE). The core gameplay loop remains untouched and completely unaware of the tutorial, ensuring maximum stability even when the player clicks frantically.
03. Dynamic Tooltips & Predictive UI
To give the player perfect control over their strategy, the UI needed to be predictive. When dragging a card over a region, the Global Manager calculates the upcoming math (including negative threshold penalties for Police and Money) and updates the top bar before the action is even confirmed.
The tooltips themselves use a Dynamic Payload structure. Depending on whether the player hovers an Apostle or an Action Card, the tooltip auto-generates its layout, stats lines, and colors based entirely on the injected Scriptable Object data, completely decoupling the UI layout from the gameplay data.