Implementing Pushey: Eggerland's Namesake Mechanic

The Eggerland and Adventures of Lolo franchise draws much of its rich puzzling from the ability to push eggs and emerald frames. Snakey becomes mechanically interesting once you account for the game’s pushing mechanics, so let’s finish Snakey and do the pushey-push thing.


Before we begin, let’s go through some helpful context. Normally the ground doesn’t move much and that simplifies the code for standing on it. Programming challenges start to appear when mobile entities start standing on each other and pushing each other around, challenges that many games sidestep or don’t implement at all.



We can’t just sidestep pushing for Eggerworld because pushing is a pillar of Lolo’s arsenal. Lolo can push a single egg or emerald frame as long as there is space for it to be pushed into. Once the pushed thing bumps into any obstacle, further pushing in that direction becomes just as immovable as any rock.


FYI, each entity in our code is responsible for its own movement in its own update() method. Yes, that means no entity in our code has ever moved another entity. It’s been nice to know that an entity controls its own movement, and only ever during its update(), it really simplifies hunting down movement bugs. After all, “any fool can write code that a computer can understand. Good programmers write code that humans can understand.” - Martin Fowler


Things will change now, though, because Lolo needs to be able to push an entity out of its spot and then immediately move into the vacated spot. Pushed entities must be able to move during Lolo’s update instead of their own. This will open a can of worms later when we implement this scumbag, but we’ll worry about that when we get there.


Rocky deserves to wear this hat as much as anyone ever deserved it


Let’s make a getPushed() method on our entities that takes a Direction and sets that entity up to move in that direction. Lolo’s update can push an emerald frame by calling that method. The emerald frame will technically still be responsible for its own movement even if it’s not during the emerald frame’s own update, which is pretty cool. And, ooh, here’s a thought: what if getPushed() returns a boolean reporting whether or not the push was successful? If getPushed() returns true, then Lolo is free to continue as if his desired movement is unimpeded, and if it returns false, then Lolo should treat the entity as an obstacle. I’m actually getting excited about this encapsulation of responsibility.


Hey, there’s another benefit too: if the getPushed() method lives on the Entity parent class for all entities, then Lolo’s code doesn’t have to know what he’s pushing, he can just call getPushed() on anything he runs into. Emerald frames and eggs will handle their own push logic, while everything else can just return false meaning they can’t be pushed. It’s always better when abstract types like Entity don’t have to be aware of individual implementations like EmeraldFrame and Rock and Snakey.


So let’s say we have a getPushed() method, what about Snakey in particular? Snakey is pushable, but only when he’s an egg. And Snakey isn’t the only “eggable” entity, nearly any enemy can be an egg.


Disclaimer ahead: I already coded up the transformation into an egg. Better still, I think I did it wrong. Wait, did I say better? I meant the other one; worse. I made an abstract class called EggableEntity. That abstract class will hook into the usual Entity stuff on behalf of that entity. It works and it’s nearly entirely self-contained, but it’s super messy. Suffice it to say entities can become an egg and then temporarily override whatever behavior the entity normally uses.


Snakey itself will always return false to a call to getPushed(), but when it transforms into an egg, the egg will return true meaning Lolo’s push will succeed. This feels good, but we’re still not done, because eggs can ride in water and Lolo can ride on those eggs.


There’s a good in-universe explanation for why he walks while he’s on the egg… but I’m not telling :p


Each water tile has its own (invisible 😒) current, and that current informs a submerged egg where to drift to. Lolo can step onto and off of those eggs as they drift along, depicted above. Eventually, an egg will either hit an obstacle and sink, enter stagnant water and sink, or keep floating along a looping current forever. Eggs can’t be pushed once they’re in the water, so perhaps they should have a state machine to move between states. I drew this up to describe every state transition for an egg in the water, check it out:


Still doing my best work on the floor, in my feety jammies. I didn't have crayons on hand for this one.


This is another state machine like the others we've done so far. The movement of a floating egg is guided by water tiles and their individual currents (up, down, left, right, or none). Perhaps more interesting is that Lolo must enter his own riding state where he gives up player control to have his motion controlled by the egg. Funny how Lolo will use getPushed() to make other entities move out of turn, and now eggs will tell Lolo how to move out of turn right back.


What more is there to say here? Oh, did you know that on the NES and Famicom, only one egg can be in the water at once? If one egg is floating along, a second egg will treat the water as an obstacle until the first egg has completely sunk. I suspect the water logic is complicated enough that the original designers didn’t want to complicate it further with multiple egg water collisions. I think we can do "better", though that is subjective because certain puzzles might have been designed with that engine quirk in mind. Unlike Don's silly egg behavior in Revival! Eggerland, I'm okay with this change. Even so, it’s not necessary for “feature parity” to allow multiple floating eggs so I suggest we do that another time.


This resulting blog post ended up being less about code nuance and more about throwing science at the wall to see what sticks. Let me know in the comments if you have any questions about this whole pushing thing and I’ll be glad to answer them there.


I have an idea for a new game mechanic: lemons! … Wait, no that’s not the idea... Or is it?

Comments

  1. that state machine drawing gets me excited and also sad because logic is so complicated

    ReplyDelete
    Replies
    1. Totally! And that picture is just the end product, not even counting the failed attempts on a dozen other crumpled sheets of graph paper before it.

      Delete

Post a Comment

Popular posts from this blog

Demo is here!

Who Is This For?