Your Weirdest Game Design Hacks

I just made my game’s structure way less soggy and uninspiring by moving the climactic battle against the Big Bad to the midpoint.

What weird, counter-intuitive decisions have you made in your own design processes to create more engaging games? This stuff is fascinating to me, so I’d love to hear examples–whether they’re for WIPs or a finished game!


This wasn’t counter-intuitive in terms of narrative, but it was definitely long-winded and extraordinarily annoying to implement. But… it made complete sense in the story, and players liked it.

In Royal Affairs, if you get engaged, someone gives you a phone call, and someone else sends you a letter, to congratulate you and/or enquire about your intentions if you’re romancing them as well as/instead of the betrothed character. Getting the game to specify the correct character to call you, with different levels of prioritisation, and then someone else to send a letter was a huge pain. I did this snarly, horrible code that became ever more complex as more bugs arose:

Complicated code within
*label congrats
*if ((fiance = 1) or (fiance = 4))
	*gosub first_congrats
*elseif fiance = 2
	*if romance_expressed_javi
		*set firstcongrats 5
	*elseif ((romance_expressed_trevelyan) and (romance_expressed_beaumonttrevelyan = false))
		*set firstcongrats 6
	*elseif romance_expressed_dominique
		*set firstcongrats 3
		*gosub first_congrats
*elseif fiance = 3
	*if ((romance_expressed_javi) and (romance_expressed_dominiquejavi = false))
		*set firstcongrats 5
	*elseif romance_expressed_beaumont
		*set firstcongrats 2
	*elseif romance_expressed_trevelyan
		*set firstcongrats 6
		*gosub first_congrats
*elseif fiance = 5
	*if romance_expressed_beaumont
		*set firstcongrats 2
	*elseif romance_expressed_trevelyan
		*set firstcongrats 6
	*elseif ((romance_expressed_dominique) and (romance_expressed_dominiquejavi = false))
		*set firstcongrats 3
		*gosub first_congrats
	*bug fiance = ${fiance}
But you do receive piles of gifts and letters congratulating you,
*if firstcongrats = 1
	*gosub aCall
*elseif firstcongrats = 2
	*gosub bCall
*elseif firstcongrats = 3
	*gosub dCall
*elseif firstcongrats = 4
	*gosub hCall
*elseif firstcongrats = 5
	*gosub jCall
*elseif firstcongrats = 6
	*gosub tCall
*elseif firstcongrats = 7
	*gosub firstcongrats_nofriend
*gosub second_congrats
*if secondcongrats = 1
	*gosub aLetter
*elseif secondcongrats = 2
	*gosub bLetter
*elseif secondcongrats = 3
	*gosub dLetter
*elseif secondcongrats = 4
	*gosub hLetter
*elseif secondcongrats = 5
	*gosub jLetter
*elseif secondcongrats = 6
	*gosub tLetter
*elseif secondcongrats = 7
	*if firstcongrats = 7
		*comment empty
		*gosub firstcongrats_nofriend
*label first_congrats
*if ((fiance != 1) and (romance_breakup_asher = false))
	*set congrats_a true
*if (((fiance != 2) and (romance_breakup_beaumont = false)) and ((ch10_beaumontAngry = false) and (tSupporter != 1)))
	*set congrats_b true
*if (((fiance != 3) and (romance_breakup_dominique = false)) and ((fiance != 5) or (romance_expressed_dominiquejavi = false)))
	*set congrats_d true
*if ((fiance != 4) and (romance_breakup_hyacinthe = false))
	*set congrats_h true
*if (((fiance != 5) and ((romance_breakup_javi = false) and (tSupporter != 2))) and ((fiance != 3) or (romance_expressed_dominiquejavi = false)))
	*set congrats_j true
*if ((((fiance != 6) and (romance_breakup_trevelyan = false)) and ((speech != 2) or (lieToFabien))) and ((fiance != 2) or (romance_expressed_beaumonttrevelyan = false)))
	*set congrats_t true
*set firstcongrats 0
*if ((congrats_a) and (rel_asher > firstcongrats))
	*set firstcongrats rel_asher
*if ((congrats_b) and (rel_beaumont > firstcongrats))
	*set firstcongrats rel_beaumont
*if ((congrats_d) and (rel_dominique > firstcongrats))
	*set firstcongrats rel_dominique
*if ((congrats_h) and (rel_hyacinthe > firstcongrats))
	*set firstcongrats rel_hyacinthe
*if ((congrats_j) and (rel_javi > firstcongrats))
	*set firstcongrats rel_javi
*if ((congrats_t) and (rel_trevelyan > firstcongrats))
	*set firstcongrats rel_trevelyan
*if ((congrats_a) and (firstcongrats = rel_asher))
	*set firstcongrats 1
*elseif ((congrats_b) and (firstcongrats = rel_beaumont))
	*set firstcongrats 2
*elseif ((congrats_d) and (firstcongrats = rel_dominique))
	*set firstcongrats 3
*elseif ((congrats_h) and (firstcongrats = rel_hyacinthe))
	*set firstcongrats 4
*elseif ((congrats_j) and (firstcongrats = rel_javi))
	*set firstcongrats 5
*elseif ((congrats_t) and (firstcongrats = rel_trevelyan))
	*set firstcongrats 6
	*set firstcongrats 7
*label second_congrats
*if (((fiance != 1) and (firstcongrats != 1)) and (romance_breakup_asher = false))
	*set secondcongrats_a true
*if ((firstcongrats != 2) and (((fiance != 2) and (romance_breakup_beaumont = false)) and ((ch10_beaumontAngry = false) and (tSupporter != 1))))
	*set secondcongrats_b true
*if (((firstcongrats != 3) and (fiance != 3)) and ((romance_breakup_dominique = false) and ((fiance != 5) or (romance_expressed_dominiquejavi = false))))
	*set secondcongrats_d true
*if (((fiance != 4) and (romance_breakup_hyacinthe = false)) and (firstcongrats != 4))
	*set secondcongrats_h true
*if ((((fiance != 5) and (firstcongrats != 5)) and ((romance_breakup_javi = false) and (tSupporter != 2))) and ((fiance != 3) or (romance_expressed_dominiquejavi = false)))
	*set secondcongrats_j true
*if ((((fiance != 6) and (firstcongrats != 6)) and (romance_breakup_trevelyan = false)) and ((speech != 2) or (lieToFabien))) and ((fiance != 2) or (romance_expressed_beaumonttrevelyan = false))
	*set secondcongrats_t true
	*set secondcongrats 7
*set secondcongrats 0
*if ((secondcongrats_a) and (rel_asher > secondcongrats))
	*set secondcongrats rel_asher
*if ((secondcongrats_b) and (rel_beaumont > secondcongrats))
	*set secondcongrats rel_beaumont
*if ((secondcongrats_d) and (rel_dominique > secondcongrats))
	*set secondcongrats rel_dominique
*if ((secondcongrats_h) and (rel_hyacinthe > secondcongrats))
	*set secondcongrats rel_hyacinthe
*if ((secondcongrats_j) and (rel_javi > secondcongrats))
	*set secondcongrats rel_javi
*if ((secondcongrats_t) and (rel_trevelyan > secondcongrats))
	*set secondcongrats rel_trevelyan
*if ((secondcongrats_a) and (secondcongrats = rel_asher))
	*set secondcongrats 1
*elseif ((secondcongrats_b) and (secondcongrats = rel_beaumont))
	*set secondcongrats 2
*elseif ((secondcongrats_d) and (secondcongrats = rel_dominique))
	*set secondcongrats 3
*elseif ((secondcongrats_h) and (secondcongrats = rel_hyacinthe))
	*set secondcongrats 4
*elseif ((secondcongrats_j) and (secondcongrats = rel_javi))
	*set secondcongrats 5
*elseif ((secondcongrats_t) and (secondcongrats = rel_trevelyan))
	*set secondcongrats 6
	*set secondcongrats 7

In the end it results in a page or two of text per playthrough. Was it a proportionate amount of time to how much players see? No. Was it load-bearing plotwise? Also no. Was it satisfying to code? The bug-fixing was too arduous for me to say yes to that. But once I started I was determined to make it work, and I really wanted there to be a sense of befriended characters (or ones with whom you had a more complicated romantic life) noticing player actions.


Yikes, that is a lot!

In The Bread Must Rise, we struggled a lot with ranking highest scores in each round of the bakeoff, and ended up with about 350 lines of code that compared each competitor to the others to rank them and then checked the ranking of each to determine who got first, second, and third place. It was kind of a nightmare, so I definitely feel your pain. :joy:


Both A Kiss from Death and The Gray Painter pull off a magic trick where the game remembers your previous playthroughs.

  1. Fake end screen. It mimics the usual end screen but actually just sends you back to the beginning, resetting all stats except the ones it needs to remember.
  # Play again.
  # Play more games like this.
  # Share this game with friends.
  # Email me when new games are available.

*gosub reset_stats
*goto_scene startup
  1. Achievement tracking. If the player’s uncovered an enormous mystery, it’s accompanied with an achievement. The game checks if the achievement’s active at certain points and changes the text if it is, because if the player’s gotten that achievement, then it’s proof they already know the twist.
*if not(choice_achieved_twist)
  Your friend seems perfectly normal.
*if (choice_achieved_twist)
  Your friend's ghost raises an accusatory finger and screams.
  1. Fully in the player’s hands: if they know the twist, they can make choices to indicate that they’re aware of it ahead of time. This one risks ignorant players reaching the twist accidentally, but that itself can be a cool moment.

In The Gray Painter, one of the prologue scenes has a main character speaking with their mother. If you play the scene already knowing the mother is dead, you can coax the monster to reveal itself by challenging the mother on inaccuracies. The game also at some point shows you that you can select choices even if you don’t have the “stat requirements” which means a first-timer might not even realize they can ignore those requirements and make choices they thought they weren’t able to before. What this amounts to is a totally different ending to the scene for players who are in the know.