Master Key Pocket - a text-less adventure

Master Key is a Zelda-like with a great focus on exploration and non-verbal dialogue. This is a demake (or a spin-off?) of said game with different elements to create a unique experience for playdate users.

How I discovered this game and why I decided to make a demake of it

Earlier this year, I made a demake of Tunic for the Game Boy Color with GB Studio and a huge inspiration from early Zelda games (especially Link's Awakening). This is the first time I ever made a game publicly available and although it wasn't as popular as other demakes made with GB Studio, it was well received by many who tried it, especially by the Tunic community as it served as an example of what it might look like.

I was flattered by the reception and decided to look further for any mentions. I ended up finding a comment on Reddit about a very similar game but with a greater focus on exploring called Master Key. I liked it, so I searched for more information and managed to find a Discord server where I could talk to the developer. The server is very small but the people are nice and the developer was happy to let me make a demake of Master Key. The suggested platform was the playdate since it was very requested.

Originally, I wanted to remake the game with the playdate's SDK but since it isn't currently compatible with arm-based Chromebooks, I decided to stick with pulp. At first, I didn't like the resolution and limited movement but later on, it made it easier to work anywhere, whenever I wanted.

What has been remade so far

  • Most of the essential tile sets have been faithfully downscaled thanks to the support of the dev.

Downscaled, need some polishing

Downscaled

  • HUD Most of the HUD elements work, except the life gauge (which is based on this thread).
    It's only limited to 4 full halves. Perhaps I could add 2 more variables that represent the second line. but then again it will continue drawing on the first line.

  • Look at it go!|120x80, %
    Basic Movement Animation

Conclusion

It only has been a month and a half since I started working on this, so there's not much to show as I'm still learning how to use pulp. There are still some things that need to be added, so I'll be more active in #pulp if I get stuck on something. This is the first time I have written a development log during development since I typically write one after finishing. I'll be making some updates by the end of every month but I can't guarantee that they will be as long as this one unless I would explain a certain feature.

11 Likes

This is looking really cool, the downscaled art looks great! Accurate but gets across clearly the "demake" vibe.

Thanks, that's the idea, but I'm trying not to make it feel like a worse version of something that already exists.

Never heard of Master Key before but I am ALWAYS down for a Zelda-like!

Update #1
It's been a while since I haven't posted anything here (apart from the Playdate Squad discord server). I've been working on a few major features like a combat system and health system (I'll explain how it works later). I'm almost done with the fundamentals to move on with the game's world. For now, I plan on recreating the demo as closely as possible and mixing things up with some new areas, items, and characters. The title of this demake may now be called Master Key Pocket to differ from the original game.

What has been made so far

  • Combat System & Enemy AI

Enemy Test Room
It's far from perfect, enemies can't handle more than one hit and they can only move in "black" tiles. I previously tested with a script that allows movement on any tile but it doesn't work with multiple enemies, so I'll save it for special enemies that only appear in one screen. As for the fox protagonist, needs to have a knockback and a hurt animation when being hit.

  • Coins

collect 'em all

There are 3 types of coins with greater value than the last. These can be collected and gain health until it reaches your maximum and it even changes the icon, just like in the original.

  • Non-verbal dialogue

sample text
This will be more focused once I start working on the game world. It's going to be challenging to see how many expressions I can make with no text whatsoever and with pictograms that are as small as 8x8.

  • Improved life gauge

Heart Test Room

The Life gauge can finally draw another line of hearts when the previous one is full. It's basically 4 different life gauges that sync with each other.

Here's how it works

First, it converts all empty heart halves to fill empty hearts, in addition to drawing all empty heart rows.

MaxHearts = HalfMax
	MaxHearts /= 2
	HPEmpty = ""
	HPEmpty2 = ""
	HPEmpty3 = ""
	HPEmpty4 = ""

Then, it determines how many hearts need to draw on each line. If the last line reaches its maximum amount of hearts (in this case 4), then the last line only draws its max and the next line adds the current value of MaxHearts and subtracts the amount of the last one. This number multiplies as many rows are added.

HP1Max = MaxHearts
    if MaxHearts>=5 then
		HP1Max = 4
		HP2Max = MaxHearts
		HP2Max -= 4
	end
	if MaxHearts>=9 then
		HP2Max = 4
		HP3Max = MaxHearts
		HP3Max -= 8
	end
	if MaxHearts>=13 then
		HP3Max = 4
		HP4Max = MaxHearts
		HP4Max -= 12
	end

it then draws the given amount of hearts to every line. The way loops work in Pulp is that while x is greater than 0, do y and substracts x and repeat until is no longer greater than 0.

	while HP1Max>0 do
		HPEmpty = "{HPEmpty}{embed:lifeempty}"
		HP1Max--
	end
	while HP2Max>0 do
		HPEmpty2 = "{HPEmpty2}{embed:lifeempty}"
		HP2Max--
	end
	while HP3Max>0 do
		HPEmpty3 = "{HPEmpty3}{embed:lifeempty}"
		HP3Max--
	end
	while HP4Max>0 do
		HPEmpty4 = "{HPEmpty4}{embed:lifeempty}"
		HP4Max--
	end

then, it does the same thing with the actual hearts as we did with the empty ones.

    HP = HeartHalf
	LifeString = ""
	LifeString2 = ""
	LifeString3 = ""
	LifeString4 = ""
	HP /= 2

It needs to find a way to know when the heart is in half or not. This is where a variable I dubbed as MasterHalf comes in. For some reason, when the HP reaches 3.5 or 4.5 the first line draws more than it should hence when HP reaches those values it does the same thing as it reaches 3

    HP1 = HP
	MasterHalf = HP
	MasterHalf = floor HP
	MasterHalf -= HP
	if MasterHalf!=0 then
		if HP<=3 then
			HP--
			HP1--
			//Bugfix starts here
		elseif HP<=3.5 then
			HP--
			HP1--
		elseif HP<=4.5 then
			HP--
			HP1--
			//Bugfix ends here
		elseif HP<=7 then
			HP--
			HP2--
		elseif HP<=11 then
			HP--
			HP3--
		elseif HP<=16 then
			HP--
			HP4--
		end
	end

repeat the same thing as we did with empty hearts but with full hearts.

	if HP>=4 then
		HP1 = 4
		HP2 = HP
		HP2 -= 4
	end
	if HP>=8 then
		HP2 = 4
		HP3 = HP
		HP3 -= 8
	end
	if HP>=12 then
		HP3 = 4
		HP4 = HP
		HP4 -= 12
	end
while HP1>0 do
		LifeString = "{LifeString}{embed:lifefull}"
		HP1--
	end
	while HP2>0 do
		LifeString2 = "{LifeString2}{embed:lifefull}"
		HP2--
	end
	while HP3>0 do
		LifeString3 = "{LifeString3}{embed:lifefull}"
		HP3--
	end
	while HP4>0 do
		LifeString4 = "{LifeString4}{embed:lifefull}"
		HP4--
	end

And finally, it draws the heart halves if there are any.

if MasterHalf!=0 then
		if HP<=3 then
			LifeString = "{LifeString}{embed:lifehalf}"
		elseif HP<=7 then
			LifeString2 = "{LifeString2}{embed:lifehalf}"
		elseif HP<=11 then
			LifeString3 = "{LifeString3}{embed:lifehalf}"
		elseif HP<=16 then
			LifeString4 = "{LifeString4}{embed:lifehalf}"
		end
	end

Here's the draw function

// Draw Empty Hearts
	if HPEmpty!="" then
		label "{HPEmpty}\n{HPEmpty2}\n{HPEmpty3}\n{HPEmpty4}" at 20,1,4,4
	end
// Draw Full (or half) hearts
	if LifeString!="" then
		label "{LifeString}\n{LifeString2}\n{LifeString3}\n{LifeString4}" at 20,1,4,4
	end

Edit: After some optimizations to the draw code I realized that I could put multiple strings in one line.

3 Likes

Update #2
Unfortunately, I wasn't able to work on this project as much as I wanted to. This month's plan was originally focused on improving the game's combat system but I drifted away from that and ended up making the inventory screen (or subscreen). Unlike some of the major additions, the inventory screen is nearly complete, and is very different from the original since it uses the crank. Here's a small side-by-side comparison (all of the icons are placeholders excepy the key).

Original (may look outdated)

demake
demake

Some things had to be cut to have space like the hud and the whole selection screen had to be reinvented to take advantage of the crank but looks good enough to me. The crank concept is inspired by The Legend of Zelda: Twilight Princess for the Gamecube where you use the C-Stick to select the item from a wheel and use the face buttons. I decided to use the D-pad to do other actions like manually saving the game (if autosaving isn't trustworthy), opening a fullscreen map, exiting the game, and other options by double-taping on the D-pad; these don't work at the moment. Here's the crank wheel in action.

I also made some other small additions like the GIF below but the inventory screen is the bigger showcase as this is a concept I had since I started thinking about how Master Key would work on the playdate. I've been busy these past few weeks and I can't promise that I'll be able to post an update for the next month.
ezgif-2-47de1338ff

Also, Happy Halloween to those who celebrate it as we don't celebrate it here.

2 Likes

Update #3

As I mentioned in my previous update, I couldn't guarantee that I'd make another major update. Still, I improved the existing combat system (especially by adding player knockback).

Shared enemy health
two of them
Pulp doesn't use local variables to make every tile have its variable. To solve this, all common enemies share the same health points but it resets when one is killed. For example, if you hit one enemy and then another, it will die quicker, but the rest will be slightly stronger.

Player knockback
tw
This took longer than it needed to, as it took a lot of trial and error to get this to work with still and moving tiles. It uses the player's current position and the position of where it was attacked to check if the tiles behind are not solid (to prevent any exploits) and moves the player to the non-solid location. @ncarson9 helped with playtesting this on real hardware (which I must admit looks gorgeous) and providing feedback.

And that's pretty much this update. I have big plans for the next couple of months but I won't spoil it all.

1 Like

Update #4

A new year, a new update

Last month was a big update and probably the biggest one yet. This time, I'll list them with hidden text. I want to try to do something different for this year, so let me know if you prefer to have it listed this way.

Added a title screen & logo

Current Title Screen

Master Key's logo is symmetrical which, it wasn't too difficult to recreate it with the new subtitle. The logo you have seen in the gif above is downscaled by hand to meet Pulp's half-resolution. In the title screen, some hidden tiles handle the interaction of the buttons for the "play" and "settings" buttons and even the "load screen." The settings menu is simple and uses menu, so it can work with the inventory screen. I wish that 'menu' could work horizontally and could skip lines.

Added lightning

"Let there be light"

This was something I wanted to add since I made my first post. It's copypasted from this. It essentially changes the tile frame nearby from 0 to 1 in a dark room. I made a new set of tiles with only a single frame, so they're always bright. The only thing left is to make sprites bright near another light source.

Added swimming

MKP_PREVIEW3

Previously, the player could only walk in shallow water, but now it can swim in deep water (and I plan to make it able to dive). The ability to swim and dive will be separate items, and the player cannot attack while swimming, making it vulnerable to projectiles.

Improved combat

After three months of improving this, I can safely say it's finished. Every time you hit a sprite, it calls a function instead of having a list of conditions for every sprite in every weapon, and so does the player. It can now die and respawn. It even saves when you do.

Added snow

screenshot (5)

This is just a detail and shouldn't be considered a feature, but it's winter, and I find this kind of fun. You can reset the path by opening and closing the inventory or moving to the next room.

Animations for trailers

I want to make the trailers have something more to show than just gameplay, and the same person who made the cover art for the Tunic demake is willing to make very short animations for the trailer. Here's one of the illustrations used for the storyboard of one of those animations with only promotional art of the original game as a reference.

Sketch

3 Likes

Update #5

It's been a couple of months since my last update. I went back to the drawing board and I'm focusing more on adding new content instead of adding it when the player discovers it for the first time. I made sure these fit in the game's context.

About the reveal trailer

For those who don't know, I made a trailer for the 2023 Playdate Community Awards. I didn't see it live as I didn't know it would play so early. This is the first time I ever made a trailer. I'll admit, it wasn't the greatest comparing it to other trailers shown because it was rushed. I had to remove the intro because of the song of choice (which was a sample song that was converted with xm2pulp), made some scenes from the original game and some made just for the trailer. Also, the animations I promised in my previous post couldn't make it because he broke his drawing tablet and lost everything regarding the animation. Nevertheless, It's not bad for my first time and there's room for improvement.

Added a new "Key" mechanic

mkp_crank_concept
This will be one of the core mechanics, you insert the key and lock (no pun intended) the player's position and buttons to allow use of the crank for almost anything that requires it. This will be useful to open shortcuts or doors. I think this gives the key a better role than just a weapon comparing it to the original game and gives a whole new meaning to the word "Master Key"

Added three new weapons

These are similar code-wise as they use projectiles but they should be balanced. I think these are familiar enough to need a GIF.

Asset1ㅤㅤㅤㅤBow and Arrow

A simple bow and arrow that depletes your money every time you use it. I could easily create an inventory for arrows but knowing how easy it is to collect coins, it adds another use to them.

Asset2ㅤㅤㅤㅤWand

It creates a flame (might change it to something else) that can be controlled by the player's direction but slowly vanishes. I accidentally made this while making the bow and arrow follow the player instead of a fixed direction. The wand doesn't use any coins but it has an extra cooldown.

Obj7 ㅤㅤㅤㅤGrappling Hook

This lets you quickly travel to specific objects like trees, chests, and other things. This one has the most functions that are called when launching, updating the chain, grabbing the object, and returning the grappling hook.

Added diving

ezgif-5-6eb2e304a4
To add another layer of exploration, the player can now dive into the water. It temporarily makes the player invisible to projectiles and can travel to new areas.

Sorry for the lack of updates. Some unexpected things have happened in the last couple of months which are too personal to discuss. Regardless, I was able to resume this. For now, I can only make much progress on the weekends.

1 Like

Update #6

I know it only has been half a month since my last update but I had an entire week off, so I made the most of it by making another update.

Added Upgrades

141x120
Pretend this is an NPC

In the original game, the key can be upgraded by purchasing a better one. I believe having a place where you can upgrade your existing items is a much better idea. You'll also need some crystals to upgrade them, which can be found in chests or bought in shops.

New enemies

ezgif-7-77b76300d8 ezgif-7-2a990f1aea

These new enemies are based on the original. They behave a bit differently compared to the original. Crow only strikes when it has the same horizontal or vertical direction and can only go to two points. The axolotl-looking creature has two sprites that can only attack horizontally or vertically, it also spawns at a random water tile.

Added Item Shops

screenshot (4) (1)

I already had a mock-up for this since September of last year but was too inexperienced to finish it in time, until now. In the original, prices are shown below the item. I didn't want to create too many tiles that were only there to display the prices. To solve this, I made a little sign that not only tells the prices but also knows what item is above and then does the rest. The item in question has an event that has the price, a small description, and if it is limited.

Coins can now be picked up by weapons

ezgif-2-a54d152bc6

You can now pick up coins from any distance with any weapons, including the ranged ones.

There are still stuff I wish I could cover like sound effects but I'm not sure how that would work. With this many features, I could recreate the demo at this point.

3 Likes

Update #7

Recently, I made some improvements to the crow from the previous update. By far the most complex enemy AI I made (which fits the fact that crows are smart creatures). This update will be a breakdown of how it works.

ezgif-5-489223da38

The first thing it does is to create a perimeter and use another event called from the player's update that makes sure that is inside it. Note that the values on each side of the perimeter can be changed to modify its size or position.

on initPerimeter do
	CrowX = event.x
	CrowY = event.y
	PerimMaxX = CrowX
	PerimMaxX += 2
	PerimMaxY = CrowY
	PerimMaxY += 2
	PerimMinX = CrowX
	PerimMinX -= 2
	PerimMinY = CrowY
	PerimMinY -= 2
end

//called from the player's update
on nearCrow do
	if event.px>=PerimMinX then
		if event.px<=PerimMaxX then
			if event.py>=PerimMinY then
				if event.py<=PerimMaxY then
					CanCrowFly = 1
                   // This will make sense later
					// hide crow
					FirstCrowX = CrowX
					FirstCrowY = CrowY
					frame 1
					// init Crow Diagonals
					CrowDiagX = CrowX
					CrowDiagY = CrowY
					CrowDiagonal = random 3
					TreeFind = 0
					call "flyattack"
				end
			end
		end
	end
end

With this, it can know if the player is nearby

ezgif-1-a6d47dfaff

Now that the crow knows when to act, it needs to know where to go.

This loop aims to find the nearest tree tile (in this case tree 1 and only that tile). This works by choosing a diagonal direction and moving to said diagonal. If it goes out of bounds, it resets to the crow's position and chooses another diagonal and it repeats until it finds said tile.

on flyattack do
	if CanCrowFly==1 then
		while TreeFind!="tree 1" do
			TreeFind = name CrowDiagX,CrowDiagY
			if CrowDiagonal==0 then
                                //southeast
				CrowDiagX++
				CrowDiagY++
				CrowMoveX = 1
				CrowMoveY = 1
			elseif CrowDiagonal==1 then
                                //northwest
				CrowDiagX--
				CrowDiagY--
				CrowMoveX = -1
				CrowMoveY = -1
			elseif CrowDiagonal==2 then
                               //southwest
				CrowDiagX--
				CrowDiagY++
				CrowMoveX = -1
				CrowMoveY = 1
			elseif CrowDiagonal==3 then
                                //northeast
				CrowDiagX++
				CrowDiagY--
				CrowMoveX = 1
				CrowMoveY = -1
			elseif CrowDiagonal>=4 then
				CrowDiagonal = 0
			end
			// prevent going out of bounds
			while CrowDiagY<0 do
				call "DiagReset"
			end
			while CrowDiagX<0 do
				call "DiagReset"
			end
			while CrowDiagY>14 do
				call "DiagReset"
			end
			while CrowDiagX>18 do
				call "DiagReset"
			end
[...]
end

on DiagReset do
	CrowDiagX = CrowX
	CrowDiagY = CrowY
	CrowDiagonal++
end

Here's a better representation of what it does except it's done in one frame.

screenshot (1)

The rest of the code is a "loop" to move the crow. If you haven't noticed it yet, before flyattack is called in the first place, the current crow tile is disguised as part of the tree and its location is stored in FirstCrowX and FirstCrowY. CrowX and CrowY are used to draw a fake crow that will follow the steps of the diagonals and will attack the player if it's on its way.

When the fake crow reaches its goal, the old position will be swapped for the actual part of the tree and the new position will swap the Crow tile and it will repeat from the beginning.

    on draw do
	// for crow flying
	if CanCrowFly==1 then
		draw "Crow fly {CrowMoveX}" at CrowX,CrowY
	end

on flyattack do
       [...]
		// fly
		wait 0.25 then
			NextCrowTile = name CrowX,CrowY
			if NextCrowTile=="tree 1" then
				// old position
				tell FirstCrowX,FirstCrowY to
					call "initPerimeter"
					swap "tree 1"
				end
                // new position
				tell CrowX,CrowY to
					swap "Crow"
					// swap horizontal direction
					if CrowMoveX==-1 then
						frame 2
					elseif CrowMoveX==1 then
						frame 0
					end
				end
				CrowDiagonal = 0
				CanCrowFly = 0
			else
				CrowX += CrowMoveX
				CrowY += CrowMoveY
				// attack player
				if CrowX==event.px then
					if CrowY==event.py then
							tell event.player to
								call "playerdmg"
							end
						end
					end
				end
				call "flyattack"
			end
		end
	end

And that's pretty much it. please take a closer look if you are planning on using it on your project.

Happy workers day

3 Likes

Update #8

It has been nearly a year since I began working on this project that originally started with a smaller goal in mind. Most of these updates were made to keep track of my own progress by showcasing all of the things I made so far or sometimes explain one of them in great detail. This one is going to be a little different.

A week after my previous post, the release date trailer of the original game came out and that made me realize about one thing. Mine lacked a story and a goal, and without any access to the source material, I couldn't think about what it could be. I told Achromi about this and offered me a steam key a few days before its release. I was surprised about this, but it also made sense because of my efforts in bringing this game to this console.

I took some notes and made some changes as I was playing. Most of those notes were about the things I would change or make it different and trying to come up with a story. Here are some of my favorite ones along with some of the things made so far that are a bit self explanatory.

Screen recording 2024-06-27 9.04.01 PM mkp_betterlightning

/!\ minor spoilers from here /!

The story I ended up loosely follows the same as the original but with a twist of being separated by a giant door that hasn't been opened in a long time (hence the picture below). Won't go into much detail but the point is that the game's structure is divided in two with 3 (or 2) dungeons . The player can go to any dungeon without any particular order but with some inaccessible areas that the other items. These places might be pieces of a larger puzzle or small rewards for backtracking.

All of the items/weapons are complete and all that's left is finding a way to allow more enemies (like the crow and axolotl) in one screen and making some bosses (and some music). The GIF of the left is a snake that moves slightly faster when being hit and it's half complete. The right one is just a template for larger sprites, this one is not solid but it can detect collision.

I'm a bit concerned that this might be too ambitious and it might take me another year to finish it. But with the original being released last month, I think I have plenty of time.

Edit: something broke some of the images, I uploaded them to the site to fix them

4 Likes

Update #9

A lot of things happened during my 2-week long summer vacation, mostly assisting with other pulp projects but still kept my focus on this one.

Yet another combat rewrite

Recently @orkn started working on a new project, and one of things that caught my attention was his method of moving tiles with unique variables. I started experimenting with this but instead of having one tile that had the script and another one for sprites, it's just one tile that moves itself and updates the entity script. The rest of the code that does all the work remains intact!

With this I can add more than one enemy that has special variables and move when the player is close. The only downside is that all entities have to have different times between delays and entities that use draw can only move one at a time.

More Music

I mentioned in my previous post I wanted to add music. I contacted Noé Guiton, the composer of the original game (you can listen to it here), to ask him if he could provide me with some of the score of the soundtrack. He sent me more than just the tracks that i needed, in midi.

I have two options: I could arrange the music for the midi by hand, which is very time consuming; or I could convert them to pulp music by putting the midis through different converters and arrange those for each channel. The second option worked pretty well, I converted the midis to xm with openMPT and used xm2pulp convert those as exported pulp music. I still needed to move some parts to different channels in order to sound right. I also had to split the songs In different parts and create a script to play each part for longer songs like the over world theme which has six parts.

I want to make some tracks of my own for this (even if I don't know much about making music), especially for new areas. But with this, I won't have to worry much about having to make music.

World Design

One of the things that I put last was the level design. Frankly, I haven't made any levels for the game since January and it's because making rooms was frustrating. Previously, I carefully took screenshots of every room, edited out the player, and put them in a spreadsheet.

It was frustrating because I had to make sure that every room had to be aligned with the other rooms and there was a small mistake, repeat the whole process.

I decided to try the same tool that I used to make rooms in tunic gbc and it works better than I thought it would. I can connect rooms and design the world before putting it in pulp.


This is not a work-in progress, I was playing around with it

Just realized that giving the player too much freedom defeats the purpose of the items

Aside from some bug fixes, these are the most notable changes.

6 Likes