Last week1 I mentioned that I'd be participating in the Acerola Jam 02 and things have gone swimmingly.

A departure from what is normal, usual, or expected, typically one that is unwelcome.
Immediate thoughts about specters and ghosts come to mind, as well as things that could be described as "cursed" like un-intuitive UI elements, statues in the middle of a jungle without any humans for miles, and various strange trees formations.
Nah, truth is, I knew what I wanted to do from going in. I wanted to make a Shin Megami Tensei-like RPG. Which was perfect for this theme with all of it's super natural elements, twists in settings, and just demon this and that, but it also got me thinking: what's more aberrant then erratic spell-casting? and suddenly the theme hit me.

Combine the erratic nature of Magicka's spells, with the dice-drafting mechanics of something like Sagrada, and finally toss in some story and visual inspiration from SMT IV, and we've got ourselves a high minded ambition! Worthy of two weeks of work!
My choice is from a position of experience, after all, I've done this before, well, MOSTLY I mean3 :

Last year, during Lent I made You Must Play, a deck-building card game where actions are sequenced by the player and the ordering matters. It was a huge learning experience where I found out things like:
Turns out making a card game is difficult, but having climbed that mountain once, I'm sure I can do it again, and better this time around. Also, Acerola has given a pretty unusual submission period of the jam, at least from my experience, of two weeks. This was in order to give people with busy schedules time to submit something, I however will be exploiting this to it's fullest extent in order to make a nice, sophisticated system.
Right now what exists is the battle system, a walkable 3D environment, some animations, and a lot of ideas that need implementing. Not a great place to be halfway through a jam? Think not! Allow me to explain.

This is an abstract view of what currently exists for the battle system.
Effect from the spell and enemy attacks is called in order over the BattleStats, which includes the data for the player, enemies, the ledger, and the world. This doesn't modify BattleStats, but instead produces a Changeapply_effect takes the Change object and runs it against the StatusEffects for the world, playerStats, and enemyStats to determine if any element of the Change is affected, like if resistances apply, damage should be doubled, or negated out right due to some effectapply_effect takes the Change and checks it against TriggeredEffects to see if any effects of the world, playerStats, or enemyStats are triggered, and if so, calls apply_effect for those TriggeredEffects such as the case where an enemy might hurt the player when they attack them in retaliation.apply_effect commits the change to BattleStats, adds it to the Ledger, and tells the AnimationSystem to play the ChangeChange contains a reference to the on-screen representation of the Stat it references, so the animation player can call whatever animation is appropriate for that repr.In retrospect, it was much harder to come up with this then explain it after the fact, but I believe I've addressed all the core concerns raised in You Must Play:
Ledger is a central fixture of the game's systems. No effect happens without their being a log entry for it4, so I know exactly how BattleStats got the way it did, show that information to the player, and any effect that wants to track something that has occured can just check the Ledger, which opens many unique possibilities for AI and effects like If the player did 75% damage in on hit, flee or when a fellow enemy is charging a big attack, buff them become very easy to write.AnimationPlayer can just throw some tweens and particle effects over to achieve it's aims.TriggeredEffects and StatusEffects, apart of a very explicit life-cycle before in You Must Play, are now just naturally part of the rest of the systems:TriggeredEffects are just Effects called recursively, making them easy to write whenever and and reason about.StatusEffects are a bit different in that they don't produce changes, but just alter them instead, this makes sense when you consider that anything you'd think to do with a status, like I reflect all attacks can be done with a combination of a I don't take damage StatusEffect to If I was about to take damage, do that amount back TriggeredEffectNothing in life is free. Especially composable, extensible, easy-to-work with systems. Thankfully, it's all just data and data is easy if you take a second to sit down and think about it.5

For the most part I hope this is self-evident if you understood how the BattleSystem is written above, but some fun key points should stand out:
EffectAndTarget is a sort of "glue" structure that holds together an Effect and it's TargetCandidates. When apply_effect is called, it also passed a list of targets for that Effect, either from the players selection or the enemies AI picks(currently just a random Attack), so the CombatManager reads TargetCandidate to know what targets to pass, like EVERYONE means everyone, ALL_ENEMIES and SELECTED_ENEMIES mean what they suggest, and so forth.Attack Can be just about anything, it's really just a grouping of Effects and their potential targets. This decoupling allows me to create attacks like deal 5 damage 5 times or deal 10 damage to everyone else, then deal 5 damage to self without having to write something custom.Spell is a list of attacks instead of one Attack because… I made a mistake and should fix that!LedgerEntries are not a whole taxonomy of log classes because my instincts told me it'd be better for it to be one flat class with all the possible fields. We'll see how that plays out.Flexibility, re-use, and rapid iteration:

This is everything I need to do in order to implement a simple enemy attack. If I wanted to, I could also have the enemy deal damage back to himself after dealing damage to the player, or propose whatever other change, with whatever targets he wants and all I have to do is specify in the attack definition within whatever parameters I need. It's pretty sweet and I'm looking forward to seeing what combinations I can create. TriggeredEffects and StatusEffects follow similar patterns.

As stated previously, a TriggeredEffect is just an Effect that has some trigger, a condition. The target for the effect is decided by the TriggeredEffect. I'll probably refactor it to be a list of effects instead for greater flexibility, but this is fine for now. If the trigger isn't activated, apply_triggered_effect just returns an empty list. Also notice, I've re-used the DealDamage effect! Already removing redundancy!

StatusEffects are more like MutateEffect since they don't produce new LedgerEntries like TriggeredEffect they just mutate whatever LedgerEntry was passed to it and call it a day. In that sense, they're not true Effects and don't extend the Effect class. And yes, if you stack the same status effect on an enemy over and over again it will apply each time:

Some future things I'm thinking about implementing:
StatusEffect that decrease the effectValue of a DAMAGE effect and appends an effect afterwards for ARMOR_LOST per damage taken.Stat per stack at the end of a turn. Effects can basically be applied at anytime so this isn't a huge lift.Okay, you got me, all of this isn't free even if I have grand plans that have worked out. Having Effects create Changes and LedgerEntries means that I still have to use those changes to, you know, change the state?
# Matching the LedgerEntry type
Enums.EntryType.EFFECT_APPLIED:
    # Matching the change effect in the LedgerEntry
        match change_entry.effect_type:
                Enums.EffectType.DAMAGE:
                    # Writing the change to battleStats
                        for stat : Stats in change_entry.effect_target:
                                stat.health -= change_entry.effect_value
# but wait, there's more!
# Add the entry to the ledger
ledger.append(change_entry)
# Tell everyone about the new change
EventBus.new_ledger_entry.emit(change_entry)
# Play the change
# Animation can't just wait for the `new_ledger_entry` signal because we have to
# Wait for the animation player to finish
await animation_player.play_ledger_entry(change_entry)
# Take care of any Stats that died and should no longer be considered
# Also, we emit death LedgerEntries here that may also need to be played
await cleanup_dead_targets(battle_stats,targets,caster,ledger,animation_player)
Yup, the dirty part of all this is that there exists an abstraction below Effect called EffectType that actually determines the mutation applied to the BattleStats. In some sense, a good 90% of the game logic is going to go in one big switch statement. But before you think this is the worst thing ever, consider how many different types of state changes actually occur in card games?
All things considered, once you've reasoned your way through how the life cycle of how cards operate with applying/mutating/triggering effects, the actual changes that take place to state mostly boil down to changing health values, shuffling things(quite literally sometimes) around, and ticking some clocks up and down. So, I really don't mind having all this stuff in one switch. Frankly, I think it's simpler this way.
Now animations are a bit of a different story. Each LedgerEntry has to be played in some way so the player can see things exploding and how their decisions are affecting the world, so for every case in that switch, we also need a case in the animation player with an accompanying animation.
    func death_animation(enemy_repr: Node2D) -> Signal:
            """Play the death animation for an enemy"""
            var tween : Tween = create_tween().set_trans(Tween.TRANS_CUBIC)
        # Just fade the enemy into nothingness
            tween.tween_property(enemy_repr,"modulate",Color(0.0,0.0,0.0,0.0),0.5)
        # Pass the 'finished' value back so the caller knows when the animation is done.
            return tween.finished
Now the truth is, I can do a fair bit of massive cheating here by just using programmatic animations over a single enemy texture and then it doesn't matter what enemy it is, I just automatically have all the animations I created thus far.6 You can also see that the enemy_repr is there, passed from the LedgerEntry, so no weird reach around had to occur for the AnimationPlayer to do it's job, it just HAS that data from the log entry.
The one annoying limitation is that animations are always played after an effect is applied and then waited on so I can't, for example, combine a lightning strike with a death animation to create a struck by lightning so hard the enemies skeleton shows, then they crumble to ash, dead animation because the DAMAGE and the DEATH entries are two separate logs! I'm considering potential fixes for this, such as having buffering logic for certain animations like all attacks and critical damage, but I believe I need to start making some content because I have until Wednesday evening to make something complete with these systems and this is where I draw the line.
My plan is for this jam is to have a simple 3D environment the player explores, picks of shiny rocks, finds points of interest, and encounters enemies in.

My experience with 3D is slightly above beginner at this point. I have a Blender layer for my ergo keyboard and I don't really struggle to make shapes anymore, but that doesn't mean I can design good spaces, in my mind, that takes a lot of continuous iteration and time I don't have. I've seen what alleged "hobbyist" modders can do with GZdoom, let alone the Counter Strike 2 brush editor, I can't compete with that at the moment7. My hack is going to be fog, akward textures, low light, and a future screen-space shader8 that'll hopefully push the aesthetic bar high enough to be creepy.
For movement, in order to pay homage to previous SMT games9, I've restricted it to the four cardinal directions, plus strafing left and right, this means environments have to be designed on an explicit grid, 2x2 meters here. I don't support stairs at the moment and don't think I'll bother, rather just teleport the player after some footstep sfx.
It's gonna be a grind until Wednesday baby. My do outs are:
I have a play test Saturday morning, so I'm compelling myself to finish a playable demo by then and plan to spend roughly eight hours a day on this. Time to ball.
StatusEffect doesn't have a log entry. I'm waiting to rename it to MutateEffect since that's more accurate to it's role, and I want to mature the animations a bit before I do this as well as experiment with how much info to show to the player.