After months of evening-and-weekend effort I've finally built an incredibly janky, barely comprehensible, super-buggy vertical slice of some Groovelet gameplay.
It just barely works and I feel unstoppable.
this trash is MY trash
the core game loop works like this:
there's a global turn cycle every 45 seconds, this is shared across all players all the time forever
every turn you can play a card from your Backpack deck, or draw a card from the Forage deck
cards drawn from the Forage deck go into the Discard pile
cards played from the Backpack deck do stuff ... and then go into the Discard pile
some cards can be played in tandem with other cards (I can play a “Mill”, then a “Log”, which will make “Planks”)
if you play your "Rest" card, you draw your cards back into your hand
The current build is up at https://groovelet.com/ . If you poke it too hard there’s a very good chance that it will fall over entirely.
And that’s… well, that’s it, so far. You can draw cards, you can play cards, you can make a powerful combo out of three of the maybe 10 cards that exist to produce infinite logs. That’s all the gameplay, so far.
But it… works! It’s the beating heart of a server-authoritative real-time browser-based card game engine!
And look at how pretty this card description language is:
Mmm, satisfyin’.
Oh, while we’re here, check this out:
This is a trick I stole from Robert Nystrom’s quite good book “Game Programming Patterns”. (Look for the part marked “Prototypes for Data Modeling”, tell it I sent you, it’ll treat you nice.)
The tl;dr of Prototypes For Data Modelling is if you’re using a data modelling language (like JSON) to describe “stuff in your game”, it’s fairly likely that you’re going to have objects with a lot of overlapping properties.
When we declare that “card_one” here has “card_zero” as a prototype, what happens is that when we look up “card_one”, we can create it with all of the properties of “card_one” written on top of all of the properties of “card_zero”.
As a result, if they share a lot of properties, we only have to write the different ones to “card_one”, meaning less code overall (hopefully). Importantly, this is not the same thing as Javascript’s built-in prototypal inheritance. It’s a totally different thing, just a handy shortcut for poopin’ together cards quickly!
Stuff That’s Not In There Yet: Midnight & Winter
There's a time cycle that's not implemented yet - it’s half built on my computer: every 80 turns (every human hour) a Groovelet Day passes, and at midnight almost all of your cards are destroyed (so, for example, it doesn't matter how many stupid Log cards you accumulate, it only matters if you get your hands on a Chest)
Every 336 days (2 human weeks) a Groovelet Year passes, and as Winter begins, all of your cards are destroyed, starting the game again from the very beginning (all you get to keep are the memories and a few mementos to indicate that you did well, if you did well)
I CAN CRACK IT, I CAN BUILD AN ETERNAL CARD GAME THAT FORCES USERS TO ENGAGE WITH IT EVERY 30 SECONDS UNTIL THEY DIE OF EXHAUSTION.
Okay, this Day and Year cycle has a real purpose beyond “being an endless treadmill”.
One of the fun things about deck-building games is using the cards that you draw to eventually build a engine of interlinked effects to create powerful effects. Games like Slay the Spire and Dominion are definitely, entirely about being able to do this, reproducibly, and repeatably. The problem is, though, that once these engines are going full-steam ahead, the game is essentially over - you’ve built the combo, you’re running the combo, there’s nothing left to see here. So these games force you to tear everything down, stack up a new set of starting conditions, and start all over again.
Currently the dozen or so cards in the Groovelet engine can be used to construct an exponential plank-generating system, which is cool and fun… and you can do it for a while, and then you have infinity planks. Great. Now we need to somehow design a game that can handle permanent, exponentially growing planks, forever. Maybe it’s better that we just take that away from you, once you’ve found it. Maybe you can only get the infinite plank combo off every once in a while.
My hope here, then, is that a “day” of Groovelet is a kind-of like an entire game, played in somewhere between 15 minutes and an hour, and uses whatever powerful engine that the player can cook up in that time to accomplish some small, permanent goal. Maybe if you’re on your grind, you can use your infinite lumber engine to crank out a bed to sleep on before the day ends and all of your cards are discarded.
Hopefully, I can make that 1-hour-long loop enjoyable and varied enough that people will want to come back and try out loads of new and interesting deck compositions and combos.
And then the Winter - well, that’s much worse, absolutely eradicating everything and starting again from scratch. The only thing the player gets to keep after the Winter is over is their collection of cards-they’ve-seen and maybe some vanity metrics. And this for similar reasons: by continuously resetting progress I can hope to maintain some semblance of a game loop rather than having to build a progression system that goes on forever.
This starts to lead towards an ultimate goal for the player, across multiple sessions: one crucial, long-term goal for players should be to see All Of The Cards In The Game. To that end, the “Collection” window is going to have to look… idk, something like one of these:
Lots of empty spaces to hook in players who want to see everything the game has to offer.
And then, we’re going to tie narrative development to those cards, too, like another game I’m stealing liberally from.
It’s wonderful how flavor-text and art on cards have a real ability to suggest a whole universe.
So, “midnight” and “winter” are big up-coming features. Properly integration testing them is also going to require some server time-travel mocking shenanigans.
More Things That Need To Happen Soon
After that, I’m thinking that I’d like to give users some ability to un-play cards, once played. Oh, and maybe “unplayable” cards should actually be unplayable in the UI. Maybe I should cook up some developer art that isn’t just “google image search results”. There’s so much more to do! We haven’t even introduced the idea of the little Groovelet that you’re trying to keep alive, yet!
Okay, so I can introduce that one a little bit: you’re not just playing cards in a total void, you will manage a little Groovelet who is trying to survive in whatever environment that you’re playing cards in. We’re going to build a stat block for your Groovelet with survival variables like “hunger” and “warmth”. Beyond simply keeping your Groovelet happy, your Forage deck will be populated based on how happy your Groovelet is (and your location, and maybe also based on the time).
Huph huph huph.
There’s so much stuff to add and so little time.