Castagne Engine

The Open Source Fighting Game Framework

Forum - Discord - Youtube

This is a pure banger feature, one of the best usability improvements of the new Castagne. State Calls were a huge feature in the previous version, and you could use it to create behavior either by extending a state or through composition. Interally both were the same, but I added warnings and _Helper() to distinguish between the two. You could alter internal defines, but it was a bit janky.

First off, there’s a new syntax for state names, which immediately distinguishes between the two: StateName is a regular state, while StateName() is a helper state. Both can be called without issues by using !StateName (equivalent to Call(StateName) in the old version), that part doesn’t change. What is new is that you can now add parameters to that, which are treated as local variables! Here’s a long snippet showcasing the new abilities:

:HelperState(var X int(), var Y bool() = true):
# You have access to both X and Y here.

# A is a local variable that you can't set from the outside.
var A int() = 10

V Y:
	Add(A, X)
	!OtherHelper(A)
endif


:OtherHelper(var Z int = 5):
# This has only got access to Z, even when called from HelperState.
# These are copies, so even if called through !OtherHelper(X), it can't change X

# You can call parent with arguments too
!!(10)


:RegularState:
# You can specify parameters with or without defaults
!HelperState(5)
!HelperState(5, false)

# If there are no parameters, these are equivalent
!OtherHelper
!OtherHelper()

# Hooks also work! The parameter list will resolve when needed.
!SuperState?(10, true)

So now you’re either super hype or super confused. This is a core feature of the engine that allows many mechanics and tools to be made inside of CASP in an efficient manner. Let me illustrate the difference with the old Castagne with some examples:

  • Kronian Titans has a lot of dashes, but internally there are only a few types with specific values. This can be replaced with a simple !AirStep(5000) to make the behavior easily readable and contained!
  • Molten Winds has levels and some characters might have custom behavior when a levul up happens. In this case, use the hook syntax: !OnLevelUp?. Compared to before, you don’t need to create the state immediately, it will only be used in the characters that want it.
  • Kronian Titans again has IMPACT moves, which launch a cinematic under specific circumstances, and requires a couple lines to set up with a hitstun that could differ. A simple !IMPACT or !IMPACT(120) can now take care of everything!
  • Molten Winds has Aster who has overdrive version of some moves which can be done by holding the button for the first X frames. Instead of rewriting it each time, how about a !TransitionIfHeld(M, 6, OverdriveMove) to automatically handle moving to OverdriveMove if M is held for the first 6 frames? Easy QoL on something that’s a bit too niche for the modules!
  • If you made custom modules in general, this is a way to make new CASP functions in a rollback-safe way, without leaving the editor. Take FlagCarryover(F) for example, it’s just FlagNext(F) in an L branch. You could make functions like that for your own use in minutes! It’s not as optimized, but that’s easily done later, and you can get started with it faster.

This is already super useful, but when you’ll combine it with the upcoming MechMods (!!? is specifically superb in this context, as it allows needed modularity) and Varspec (which can understand when it’s seen from a state call), it will be incredible. These three features are the ones that will bring the editor to a new level and I can’t wait for that!

Slight design musings to finish, since this post is already super long:

  • I thought about adding persistant variables to states, potentially allowing a Transition(CoolState(500)) or something, but I think it would be a bit overkill in complexity and we can hold variables in memory for that. Maybe I could add a variable tag for that.
  • Speaking of, the variable syntax is now lighter: var X int is valid. The variable tags are optional (the () in int(), which isn’t used for anything right now nor was it before, always a planned feature), and you don’t need to specify a default. In fact, some variable types don’t accept defaults, such as state or flag (semantic types in general), but you may use them in state calls.
  • Some modules have functions that are basically just syntaxic sugar and could be made as CASP states now. The Attack module is a prime example, as it’s mostly functions that call AttackParam internally. I don’t think this would be for the best however, as it would complexify the docs and make it more confusing to follow. Allowing the ! to become optional would fix that but introduce confusion instead. Making them as CASP functions would allow overriding module functions, which is another can of worms. I’m probably going to introduce module functions as macros to implement that.

What’s next is a bit tricky to choose, but I think I’ll start working on the modules again. Logically, I won’t be actually able to implement the MechMods without having something to put in them! Varspec will probably be the very last core feature to be added, since it’s so dependant on the rest.

Try the Godot 3 Version now!