Console command helpers for the Der Wunderfizz buyable. Provides developer/console commands to place a machine, list configured machines, clear configured machines, check the model, and inspect/retune perk-slot prices and costs.
using Sandbox;
using System.Linq;
namespace NZombies;
/// <summary>
/// Console access to Der Wunderfizz. Every button in the tool panel has an
/// equivalent here, so the machine can be placed and inspected without clicking.
/// </summary>
public static class WunderfizzCommands
{
static MapEditor Editor
=> Game.ActiveScene?.GetAllComponents<MapEditor>().FirstOrDefault();
static NZPlayer Player
=> NZPlayer.Local;
/// <summary>Drop one at your feet: `nz_fizz [price]`.</summary>
[ConCmd( "nz_fizz" )]
public static void Place( int price = 0 )
{
var ed = Editor;
var p = Player;
if ( !ed.IsValid() || !p.IsValid() ) { Log.Warning( "[nz-fizz] no editor/player" ); return; }
if ( price > 0 ) ed.WunderfizzPrice = price;
// ⚠️ At the PLAYER's feet with an UP normal, not a crosshair trace. The
// command exists so this can be driven headlessly, and a trace needs
// somewhere to be aiming.
ed.AddWunderfizzAt( p.WorldPosition, Vector3.Up );
}
/// <summary>What is placed: `nz_fizz_list`.</summary>
[ConCmd( "nz_fizz_list" )]
public static void List()
{
var list = ActiveConfig.Current.Wunderfizzes;
if ( list.Count == 0 )
{
Log.Info( "[nz-fizz] none placed — Q > Placeables > Player assisting > "
+ "Der Wunderfizz, or nz_fizz" );
return;
}
for ( int i = 0; i < list.Count; i++ )
{
var w = list[i];
Log.Info( $"[nz-fizz] [{i}] {w.BasePrice,6} base"
+ (w.PriceIncrement > 0 ? $" +{w.PriceIncrement}/roll" : " (flat)")
+ (w.StartRound > 1 ? $" from round {w.StartRound}" : "")
+ (w.RequiresPower ? " ⚡power" : "")
+ (w.PerkSlotPrice > 0
? $" slot {w.PerkSlotPrice}"
+ (w.PerkSlotIncrement > 0 ? $" +{w.PerkSlotIncrement}/slot" : "")
: "")
+ $" flag {DoorLinks.Display( w.Link )}" );
}
var mgr = WunderfizzManager.Instance;
// ⚠️ CONFIGURED vs STANDING, said separately. A machine whose model failed
// to load leaves a spot in the config and nothing in the world, and those
// two numbers disagreeing is the only cheap way to notice.
Log.Info( $"[nz-fizz] {list.Count} configured, "
+ $"{(mgr.IsValid() ? mgr.Built.ToString() : "?")} standing" );
}
/// <summary>Remove them all: `nz_fizz_clear`.</summary>
[ConCmd( "nz_fizz_clear" )]
public static void Clear()
{
var n = ActiveConfig.Current.Wunderfizzes.Count;
ActiveConfig.Current.Wunderfizzes.Clear();
WunderfizzManager.Ensure( Game.ActiveScene )?.Rebuild();
Log.Info( $"[nz-fizz] removed {n}" );
}
/// <summary>
/// Is the machine's MODEL usable? `nz_fizz_model`.
///
/// ⚠️ Asks the asset system, not the config. A freshly ported model is the one
/// thing most likely to be wrong here, and "the machine did not appear" has
/// three different causes — no spot placed, a bad path, or a compiled-but-
/// broken vmdl that loads as the checkerboard error model.
/// </summary>
[ConCmd( "nz_fizz_model" )]
public static void ModelCheck()
{
var m = Model.Load( WunderfizzManager.ModelPath );
if ( m is null )
{
Log.Warning( $"[nz-fizz] {WunderfizzManager.ModelPath} — NOT FOUND" );
return;
}
if ( m.IsError )
{
Log.Warning( $"[nz-fizz] {WunderfizzManager.ModelPath} — ERROR MODEL "
+ "(the vmdl exists but did not compile)" );
return;
}
Log.Info( $"[nz-fizz] {WunderfizzManager.ModelPath} ok — "
+ $"bounds {m.Bounds.Size}, meshes {m.MeshCount}" );
}
/// <summary>
/// `nz_fizz_slotprice [base] [increment]` — retune the perk-slot price on EVERY
/// placed machine and the tool defaults, then print the ladder.
///
/// ⚠️ BOTH, DELIBERATELY. The config is what standing machines charge; the MapEditor
/// fields are what the NEXT one placed is stamped with. Setting one alone means the
/// price changes now and silently reverts later. Negative or omitted leaves a value
/// alone; no arguments just reports.
/// </summary>
[ConCmd( "nz_fizz_slotprice" )]
public static void SlotPrice( int basePrice = -1, int increment = -1 )
{
var list = ActiveConfig.Current.Wunderfizzes;
if ( basePrice >= 0 || increment >= 0 )
{
foreach ( var w in list )
{
if ( basePrice >= 0 ) w.PerkSlotPrice = basePrice;
if ( increment >= 0 ) w.PerkSlotIncrement = increment;
}
var ed = Editor;
if ( ed.IsValid() )
{
if ( basePrice >= 0 ) ed.WunderfizzSlotPrice = basePrice;
if ( increment >= 0 ) ed.WunderfizzSlotIncrement = increment;
}
else
{
Log.Warning( "[nz-fizz] no MapEditor — machines already placed were retuned, "
+ "but the NEXT one placed will use the old numbers" );
}
}
if ( list.Count == 0 )
{
Log.Info( "[nz-fizz] none placed — nz_fizz drops one" );
return;
}
SlotCost();
}
/// <summary>
/// `nz_fizz_slotcost` — what the next slot costs you and the four after it.
///
/// ⚠️ NOT `nz_fizz_slots`, which is one character from `nz_fizz_slot` — and that one
/// SPENDS POINTS. A read-only command a typo turns into a purchase is a trap.
///
/// ⛔ THE LADDER COMES FROM `SlotPriceAt`, NOT RECOMPUTED HERE. A second copy of
/// `base + step * bought` in a diagnostic can agree with itself while disagreeing with
/// the till — exactly the failure this is meant to catch.
/// </summary>
[ConCmd( "nz_fizz_slotcost" )]
public static void SlotCost()
{
var p = Player;
var machine = Game.ActiveScene?.GetAllComponents<Wunderfizz>()
.FirstOrDefault( m => m.IsValid() );
if ( !machine.IsValid() ) { Log.Warning( "[nz-fizz] no Wunderfizz standing — nz_fizz" ); return; }
var spot = machine.Spot;
Log.Info( $"[nz-fizz] perk slots — base {spot?.PerkSlotPrice ?? 0}"
+ ( (spot?.PerkSlotIncrement ?? 0) > 0
? $" +{spot.PerkSlotIncrement} per slot already bought"
: " (flat — increment 0)" ) );
if ( !p.IsValid() ) { Log.Warning( "[nz-fizz] no player" ); return; }
Log.Info( $"[nz-fizz] you: {p.Perks.Count}/{p.PerkSlots} used · {p.BonusPerkSlots} bought"
+ $" · {p.Points} points" );
var real = p.BonusPerkSlots;
// ⛔ ASKS `SlotPriceAt` FOR EACH RUNG — it does NOT move `BonusPerkSlots` and read
// it back. Walking the ladder by writing the player is how a diagnostic ends up
// granting slots it only meant to price.
for ( int i = 0; i < 5; i++ )
{
var price = machine.SlotPriceAt( real + i );
Log.Info( $"[nz-fizz] slot #{real + i + 1,-2} {price,7}"
+ ( i == 0 ? ( p.Points >= price ? " <- next, affordable" : " <- next, TOO DEAR" ) : "" ) );
}
}
}