This is a very crude mockup of how replacement effects could look in Laterna Magica. Instead of any impressive effect, I'm just modifying a variable that represents a player's life total, but it does quite a lot for only being about 30 lines of code:
//a replaceable event
trait Event { def execute(): Unit }
//an effect that replaces an event
type ReplacementEffect = PartialFunction[Event, Event]
//applies all effects to the event. If a replacement doesn't match, the
//event remains unchanged
def replace(event: Event, effects: ReplacementEffect*) =
effects.foldLeft(event) { case (event, effect) => effect.applyOrElse(event, identity[Event] _) }
var activeEffects: Seq[ReplacementEffect] = Nil
def execute(event: Event) = replace(event, activeEffects: _*).execute()
//example: A player's life points
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def execute() = _life += life
}
//execution:
//gain three life
gainLife(3)
println(life) //23
//gain twice as much life
activeEffects = List({ case GainLife(x) => GainLife(x * 2) })
//gain six life
gainLife(3)
println(life) //29
The trait Event is used to represent replaceable events, and effects are simply "partial functions": functions that can only be applied to some of the values that its parameter type would allow. For example, at the bottom there's the partial function:
{ case GainLife(x) => GainLife(x * 2) }
While ReplacementEffect is defined for all Events, this one only accepts GainLife instances. Using PartialFunction.applyOrElse, I handle the cases where a replacement effect does not apply to an event.
The example is self-contained, so you should be able to run it yourself. In any case, the output is next to the print statements: The first time, you only get 3 life, but after registering the effect, the amount is indeed doubled. In reality, execute (and replace?) needs to be a bit smarter to recognize the right effects to apply, but otherwise this could stay almost as it is.
Tuesday, February 10, 2015
Replacement effects with partial functions
Subscribe to:
Post Comments (Atom)
3 comments:
If I wasn't wholly unfamiliar with the syntax I would likely appreciate this more :) That being the case, how does this handle circular replacement, ie two Boon Reflections?
Nevermind my question, I'm dim and it's early in the morning and various other excuses lol. :P
I know, functional syntax can be very different, and it doesn't get better by how much is going on in little code. ;)
For example, take this function:
Traversable[A].foldLeft[B](z: B)(op: (B, A) => B): B
Folds are a staple of functional programming. it takes a start value and a binary function - op: (B, A) => B - and repeatedly applies op to the intermediate result and one of the elements.
In my case, each element is itself a (partial) function, and I apply that function to the intermediate. If the function does not accept the parameter, I fall back to the identity function, which doesn't change its parameter.
Circular replacement looks more like an issue for another blog post - that I just wrote ;)
Post a Comment