Notnips / Brutal-Company-Plus

A CUSTOMIZABLE hardcore mod that randomizes events ranging between insanity and normal.
GNU General Public License v3.0
1 stars 1 forks source link

feat: Implement Events API (plus major refactor) #1

Open Sculas opened 8 months ago

Sculas commented 8 months ago

This PR implements a new events API and refactors most of the code base to allow this change. A lot of code has been separated and moved into their respective classes, and some things have been outright rewritten.

See https://github.com/notnips/Brutal-Company-Plus/pull/1#issuecomment-1868584741 for more information!

Sculas commented 8 months ago

Everything has been implemented at this point, and all that is left is checking for bugs. So then, let's summarize what I've done in this PR:

Changes

Configuration

While most of the config options have been kept, some have been renamed. This is what the default config looks like as of writing:

Show default config ```ini ## Settings file was created by plugin BrutalCompanyPlus v4.0.0 ## Plugin GUID: BrutalCompanyPlus [Credits-related Adjustments] ## Amount of money to give when a player leaves a moon alive (to disable, use: -1) # Setting type: Int32 # Default value: 150 FreeMoneyAmount = 150 [Custom Scrap Values] ## Define min/max scrap pieces and min/max total scrap value for Experimentation (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 ExperimentationLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Assurance (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 AssuranceLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Vow (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 VowLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Offense (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 OffenseLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for March (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 MarchLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Rend (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 RendLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Dine (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 DineLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for Titan (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 TitanLevel = 6,25,400,1500 ## Define min/max scrap pieces and min/max total scrap value for any custom levels (for vanilla, use: -1,-1,-1,-1) # Setting type: String # Default value: 6,25,400,1500 CustomLevel = 6,25,400,1500 [Enemy Rarity Values] ## Set this to `false` if you want vanilla spawning behavior. # Setting type: Boolean # Default value: true Enabled = true ## Define custom enemy rarity values for Experimentation (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ExperimentationLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Assurance (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 AssuranceLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Vow (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 VowLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Offense (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 OffenseLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for March (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 MarchLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Rend (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 RendLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Dine (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 DineLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for Titan (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 TitanLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 ## Define custom enemy rarity values for any custom levels (no spawn: 0, max chance: 100, BCP default: -1) # Setting type: String # Default value: Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 CustomLevel = Centipede:-1,Bunker Spider:-1,Hoarding bug:-1,Flowerman:-1,Crawler:-1,Blob:-1,Girl:-1,Puffer:-1,Nutcracker:-1,Spring:-1,Jester:-1,Masked:-1 [Enemy-related Adjustments] ## Whether all enemies should be nominated for spawning on all moons (vanilla: false) # Setting type: Boolean # Default value: true SpawnOnAllMoons = true [Event Rarities] ## Here I was, thinking that Pokémon was the only game with evolutions. # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common They are EVOLVING?! = Uncommon ## Mommy, the ice cream truck is here! # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common ICE SCREAM!!! = Rare ## They just want a hug... # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Bring a shovel! = Uncommon ## Better not get kicked out again... # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common They guard this place! = Uncommon ## She's gonna find you... # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Hide and Seek = Uncommon ## Come on, those are mine! # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Hoarder Town = Rare ## May the odds be ever in your favor. # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common The Hunger Games = Rare ## Lord have mercy on your soul. # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Pop goes the.. HOLY FUC- = Rare ## Just another Tuesday. # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Nothing happened today! = Uncommon ## Creepy... # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Outside the box! = Rare ## Seems like Pluto just passed by. # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common All moon heat has been reset! = Uncommon ## Did you hear that? # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common The shadows are roaming = Rare ## I'm innocent, I swear! # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common When did we get this installed?! = Uncommon ## Don't get a concussion! # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Springbreak = Uncommon ## EXPLOSION!!! # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common The surface is explosive! = Uncommon ## Ssshhhh... # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common The Beasts Inside = Uncommon ## Fix the Barricades! # Setting type: EventRarity # Default value: Uncommon # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common The Rumbling = Uncommon ## Instructions unclear. # Setting type: EventRarity # Default value: Rare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Dont look... away? = Rare ## Sometimes, life just ain't fair. # Setting type: EventRarity # Default value: VeryRare # Acceptable values: Disabled, UltraRare, VeryRare, Rare, Uncommon, Common Unfair Company = VeryRare [Event Settings] ## Chance between 0 and 100 for an event to occur (this is separate from event rarity and no events may still occur even if the chance is met!) # Setting type: Int32 # Default value: 100 # Acceptable value range: From 0 to 100 GlobalChance = 100 ## Whether all events have equal chance to happen (disables event rarity altogether!) # Setting type: Boolean # Default value: false EqualChance = false [Map Hazard Adjustments] ## Amount of turrets that should be spawned (for vanilla, use: -1) # Setting type: Int32 # Default value: 8 TurretSpawnRate = 8 ## Amount of landmines that should be spawned (for vanilla, use: -1) # Setting type: Int32 # Default value: 30 LandmineSpawnRate = 30 [Moon Heat] ## Rate at which the moon heat increases by when landing back on the same planet # Setting type: Single # Default value: 20 IncreaseRate = 20 ## Rate at which the moon heat decreases by when not visiting the planet # Setting type: Single # Default value: 10 DecreaseRate = 10 ## Defines how moon heat affects the weather (format: `start:end:type`, start is inclusive and end is exclusive) # Setting type: String # Default value: 20:40:Rainy,40:60:Foggy,60:80:Flooded,80:100:Stormy,100:101:Eclipsed HeatCurve = 20:40:Rainy,40:60:Foggy,60:80:Flooded,80:100:Stormy,100:101:Eclipsed [Quota-related Adjustments] ## Days available before deadline (for vanilla, use: -1) # Setting type: Int32 # Default value: 4 DeadlineDays = 4 ## Amount of credits everyone gets at the start of a new session (for vanilla, use: -1) # Setting type: Int32 # Default value: 200 StartingCredits = 200 ## Quota you begin with at the start of a new session (for vanilla, use: -1) # Setting type: Int32 # Default value: 400 StartingQuota = 400 ## Rate at which the quota increases by when it's met (for vanilla, use: -1) # Setting type: Int32 # Default value: 275 BaseIncrease = 275 ```

Errors

BCP now handles errors properly. It makes sure the config is valid at startup and shows an error screen when you enter the main menu, notifying you of what went wrong. You can try this out for yourself by, for example, setting the weather type in HeatCurve to something invalid!

Events

All events but Landmine and Turret were kept. A couple of events have minor changes to them, for better playability. Here's an overview of the events and optionally changelogs:

Name Description Notes
They are EVOLVING?! Here I was, thinking that Pokémon was the only game with evolutions.
ICE SCREAM!!! Mommy, the ice cream truck is here! Now includes all items, instead of just the vanilla items.
Bring a shovel! They just want a hug...
They guard this place! Better not get kicked out again...
Hide and Seek She's gonna find you... Formerly called They just wants to play!!. I felt that this was a better name.
Hoarder Town Come on, those are mine!
The Hunger Games May the odds be ever in your favor. Does nothing when you're playing solo. A guaranteed wipe just isn't fun.
Pop goes the.. HOLY FUC- Lord have mercy on your soul. Popup timer shortened to 1-5, instead of 0-10.
Nothing happened today! Just another Tuesday.
Outside the box! Creepy...
All moon heat has been reset! Seems like Pluto just passed by.
The shadows are roaming Did you hear that? The mist feature has been removed due to conflicts with moon heat.
When did we get this installed?! I'm innocent, I swear!
Springbreak Don't get a concussion! Formerly called Spring Escape!. I felt that this was a better name.
The surface is explosive! EXPLOSION!!!
The Beasts Inside Ssshhhh... Added support for custom levels.
The Rumbling Fix the Barricades! Added support for custom levels.
Don't look... away? Instructions unclear.
Unfair Company Sometimes, life just ain't fair.

Networking

BCP is now fully networked and all clients are aware of which event is currently happening. Weather from moon heat is now also networked. Most of the EnemyAI patches are no longer needed, because isOutside is also properly shared across all clients.

Events API

This is what this PR is all about, adding a new events API. This allows any mod developer to extend upon BCP and add new events from their own mods, keeping things nicely separated. Rarity values are handled from BCP, but saved into the plugin's own config file.

Creating your own events is quite simple:

using BrutalCompanyPlus.Api;
using BrutalCompanyPlus.Objects;
using UnityEngine;

public class MyEvent : IEvent {
    public string Name => "My event name";
    public string Description => "My event description";
    public EventPositivity Positivity => EventPositivity.Negative;
    public EventRarity DefaultRarity => EventRarity.Rare;

    public void ExecuteServer(SelectableLevel Level) {
        EnemySpawnManager.DraftEnemySpawn<SpringManAI>(new EnemySpawnManager.SpawnInfo(1));
        EnemySpawnManager.DraftEnemySpawn<FlowerManAI>(new EnemySpawnManager.SpawnInfo(1, Outside: true));
        EnemySpawnManager.DraftEnemySpawn<CentipedeAI>(new EnemySpawnManager.SpawnInfo(1, Immediate: false));
        LevelManager.ModifyLevelProperties(Level, Level => {
                Level.outsideEnemySpawnChanceThroughDay = new AnimationCurve(
                    new Keyframe(0f, 999f),
                    new Keyframe(21f, 999f)
                );
                Level.enemySpawnChanceThroughoutDay = new AnimationCurve(
                    new Keyframe(0f, 500f),
                    new Keyframe(0.5f, 500f)
                );
            },
            nameof(SelectableLevel.outsideEnemySpawnChanceThroughDay),
            nameof(SelectableLevel.enemySpawnChanceThroughoutDay)
        );
    }

    public void ExecuteClient(SelectableLevel Level) { }

    public void UpdateServer() { }

    public void OnEnd(SelectableLevel Level) { }
}

To register your events, it's as simple as:

using BepInEx;
using BrutalCompanyPlus.Api;

[BepInPlugin(...)]
public class Plugin : BaseUnityPlugin {
    private void Awake() {
        EventRegistry.AutoRegister(this);
    }
}

Many more utilities to aid in event development can be found in BrutalCompanyPlus.Utils and BrutalCompanyPlus.Objects.