Adding a new Tileset - pret/pokered GitHub Wiki

Tilesets are used to determine how your map looks. RBY's are often made on an "add tile as needed" basis, so oftentimes, you'll feel very restrained when making your maps. Naturally, this is where you try to add tilesets...only to be utterly mystified. Here, I'll try to help you, using my...two added tilesets worth of experience.

Note that this isn't a map tutorial, it's just about getting your tilesets off the ground, into the game, and hopefully jazzed up enough for you to have a little fun. For a basic map tutorial, go here.

General

Tilesets are (up to) 128x48px images stocked with 8x8px map pieces. These 8x8 pieces are called "tiles", which we use to make up a block. They only work with 2-bit colour palettes, so when editing, make sure you're only using the greyscale you see on other tilesets. Otherwise, it can't build! As you can imagine, if making a tileset by scratch, you'll need to be very imaginative! You don't need anything like Asperite, MS Paint is more than enough, I promise.

Tileset images are stored in gfx/tilesets and made into 2bpp files when making the ROM. Assuming you have your tileset all ready and bursting with creativity, store it there before doing anything else. Take note of the filename, you'll need it for later. As a rule of thumb, always keep the name the same, so it's easier to edit later and you don't have any cross-threaded names confusing you. Also, save it as a PNG.

You'll also need a Blockset. Personally, I just copy overworld.bst from gfx/blocksets and rename it to the name of my tileset, aiming to edit it later. Truth be told, I only do it this way because I have no idea what the file format is, and pokered doesn't make one for you - no harm done, it works just fine anyway. That aside, to explain what blocks are: They are 4x4 grids of 16 tiles, making up the actual building blocks of your map.

So to be clear:

  • Tiles are 8x8px building pieces to make a block.
  • Blocks are 4x4 blobs of 16 tiles that are used to make your map. These comprise of 4 walking tiles for Red.
  • As a general rule, whenever you need to add a tile ID, you're looking for the bottom left tile of what's used on a block.
  • To find your tile IDs, I like to use Polished Map for this, which makes using and editing these easy. In Polished Map, you can go to Tools -> Edit Tileset and click on the tile you want, which will spit out the ID you need. It should be a $ sign followed by 2 numbers, such as $00.

It's not as complicated as it sounds, I'm just verbose.

Implementing the Tileset

Assuming you've stored your tileset PNG in gfx/tilesets, you're all ready to go! Let's get started.

Files gotta go somewhere!

While your file is in the folders, you need to make sure your files are actually going into the ROM when it's building. You'll need to make a new section in the bank if you're adding your first, but don't worry, you likely only need to do that once and there are no strings attached. In gfx/tilesets.asm...

Underground_GFX::   INCBIN "gfx/tilesets/underground.2bpp"
Underground_Block:: INCBIN "gfx/blocksets/underground.bst"

+SECTION "Tilesets 4", ROMX
+NameHere_GFX::		INCBIN "gfx/tilesets/namehere.2bpp"
+NameHere_Block::		INCBIN "gfx/blocksets/namehere.bst"

Don't worry about the .2bpp file, that's what pokered converts from your PNG!

Constants, people! Constants!

Now that you've done that, you need to define the new tileset in constants/tileset_constants.asm, at the bottom of the list. The all-caps name you use here, will be used whenever I say NAMEHERE. Otherwise, it's capitalised normally as NameHere. I don't know if that's important, but I'm too paranoid to find out.

	const PLATEAU      ; 23
+	const NAMEHERE	   ; 24
DEF NUM_TILESETS EQU const_value

Header for the goal!

Next, your tileset needs to be added to data/tilesets/tileset_headers.asm. Like before, slap it at the bottom. This is one of the most important steps.

	tileset Facility,    $12, -1, -1,  -1, TILEANIM_WATER
	tileset Plateau,      -1, -1, -1, $45, TILEANIM_WATER
	tileset PreGym,		 $3A, $3B, -1,  -1, TILEANIM_NONE
+	tileset NAMEHERE,	 -1,  -1,  -1, -1, TILEANIM_NONE
	assert_table_length NUM_TILESETS

There are a bunch of key things you'll need to know here. As you'll see, there are three -1s, and a TILEANIM thing. Let's talk about them!

  • The first -1s are counter tiles. These are tiles from your tileset that an NPC is intended to stand behind; when pressing A to talk to someone, it will presume you're a tile closer than you usually are. This is why you can talk to Nurse Joy at the Pokemon Centre.
  • The fourth -1 is the grass tile for the tileset you're working with.
  • The TILEANIM variable has three types: TILEANIM_NONE, TILEANIM_WATER, and TILEANIM_WATER_FLOWER. Use these as needed for your tilesets with water or flower features.

Two additional notes on grass tiles:

  • If you're working with grass blocks, while you may be scared as there is only space for one tile, you only need to note the bottom-right one. The game checks that, and will then assume everything is fine. It doesn't even need to be symmetrical on the part that goes over Red, so you can make the grass more detailed if you want.
  • If you want multiple grass tiles, you're likely going to need to either hardcode how that tileset will work somewhere else, or cry as you change how every tileset works, likely editing the macro or something.

If you have issues getting encounters on elaborate grass blocks, you can always hardcode it to work in engine/battle/wild_encounters.asm! This also works for water, if you just swap wGrassRate to wWaterRate.

; determine if wild pokemon can appear in the half-block we're standing in
; is the bottom right tile (9,9) of the half-block we're standing in a grass/water tile?
	hlcoord 9, 9
	ld c, [hl]
	ld a, [wGrassTile]
	cp c
	ld a, [wGrassRate]
	jr z, .CanEncounter
+	
+	cp NAMEHERE
+	ld a, $?? ; Bottom-right tile ID
+	cp c
+	ld a, [wGrassRate]
+	jr z, .CanEncounter
+	
	ld a, $14 ; in all tilesets with a water tile, this is its id
	cp c
	ld a, [wWaterRate]
	jr z, .CanEncounter

Oh, and if your tileset has water, define it in water_tilesets.asm. Just do it.

; tilesets with water
WaterTilesets:
	db OVERWORLD
	db FOREST
	db DOJO
	db GYM
	db SHIP
	db SHIP_PORT
	db CAVERN
	db FACILITY
	db PLATEAU
+	db NAMEHERE
	db -1 ; end

Collision Chaos

Now for what is also super important: Defining collision. Go to data/tilesets/collision_tile_ids.asm.

Facility_Coll::
	coll_tiles $01, $10, $11, $13, $1b, $20, $21, $22, $30, $31, $32, $42, $43, $48, $52, $55, $58, $5e

Plateau_Coll::
	coll_tiles $1b, $23, $2c, $2d, $3b, $45

+NameHere_Coll::
+	coll_tiles $??
+

These demand the tile IDs of the tiles you want Red to WALK on. Anything that is NOT defined here isn't walkable. Makes sense given you're going to have more that stops you than what's walkable, but don't waste your time. I know I did. So once again, note the tiles you want to be WALKABLE like an American who can't afford a car.

Warp, warp, and away!

Next up, in data/tilesets/warp_tile_ids.asm, you need to make sure your warp tiles are correctly defined. These are your doors, your warp tiles, your pitfalls, and all that juicy stuff.

	dw .PlateauWarpTileIDs
+	dw .NameHereWarpTileIDs
	assert_table_length NUM_TILESETS

...diff
	.PlateauWarpTileIDs:
		db $1B, $3B
		; fallthrough
-	.ShipPortWarpTileIDs:
-	.ClubWarpTileIDs:

+	.NameHereWarpTileIDs:
		warp_tiles $?? ; note tile here

+	.ShipPortWarpTileIDs:
+	.ClubWarpTileIDs:
		warp_tiles ; end

Make sure to move the Ship and Club IDs to the bottom, or they'll act strangely. All non-warp tilesets should be defined there.

When noting your warp tile, remember that it only needs to be the bottom-right tile - that's all the game checks. You don't need to add all four tiles that make up your door! It's just wasting bytes!

Editing your Blockset

So now you have all the things you require for a tileset to be up and running. Your ROM will build - if you're using Cygwin, do so now with cd folderhere followed by make, or whatever is equivalent for you. Download Polished Map and follow the set-up instructions, it's great for map editing in general and works just as well here.

Before we get stuck in, you'll want to edit an appropriate map header to use with your new tileset. It'll be the same name as you've hopefully been using this whole time. In data/maps/headers...

-	map_header Screwthatistan, SCREWTHATISTAN, OLD_TILESET_HERE, 0
+	map_header MapNameHere, MAP_NAME_HERE, TILESET_HERE, 0
	end_map_header

Get it all hooked up as it should be! Hopefully, you've already got your map. If you haven't, go here.

Now open up Polished Map. If you did my method of copying a blockset and renaming it, you'll see the tileset in the list when opening the map. Upon loading, you'll also see a bunch of garbled textures on the left, which you'll be clicking like colours to MS Paint all over your map. Don't worry - this is your canvas! Right-click on a block, and you can add all those tiles you've been working on. Make sure to save regularly. Before you know it, you'll have a blockset done and dusted, and pokered will do the rest for you. Also, protip: right-clicking a block on a map acts like picking a colour, which makes the editing process much easier.

Blocksets can be expanded or contracted in Tools -> Resize Blockset, which can be handy to save space or get more bang for your tileset.

Extras

There are some other bits and bobs that RBY has regarding tilesets, what I've given is just what you need to make it work. So if you're looking to flesh it out more, you've come to the right place.

Making the Bike ride-able

If you want a bike to be ride-able in your tileset, you need to note it in data/tilesets/bike_riding_tilesets.asm.

BikeRidingTilesets::
	db OVERWORLD
	db FOREST
	db UNDERGROUND
	db SHIP_PORT
	db CAVERN
+	db NAMEHERE
	db -1 ; end

Dungeon Tilesets

Likewise, if you want it to be a dungeon tileset, such as a cave or interior, you want to edit data/tilesets/dungeon_tilesets.asm.

DungeonTilesets:
	db FOREST
	db MUSEUM
	db SHIP
	db CAVERN
	db LOBBY
	db MANSION
	db GATE
	db LAB
	db FACILITY
	db CEMETERY
	db GYM
+	db NAMEHERE
	db -1 ; end

Making the Escape Rope usable

If you want to escape with the Escape Rope, you need to do a similar thing in data/tilesets/escape_rope_tilesets.asm.

EscapeRopeTilesets:
	db FOREST
	db CEMETERY
	db CAVERN
	db FACILITY
	db INTERIOR
+	db NAMEHERE
	db -1 ; end

Bookshelf blocks, etc

If you have bookshelf blocks and want common quotes without having to add events and such, you can use data/tilesets/bookshelf_tile_ids.asm.

	bookshelf_tile SHIP,         $36, BookOrSculptureText
	bookshelf_tile TILESETHERE,		 $??, TEXTTHINGHERE
	db -1 ; end

This uses the tile ID, which should be the bottom-left tile on your block.

The game gives you five possible texts, which you replace TEXTTHINGHERE with;

  • IndigoPlateauStatues
  • TownMapText
  • BookOrSculptureText
  • ElevatorText
  • PokemonStuffText

The text can be edited in text_2.asm - note that each line has a 17-character limit. You can add more text and define it in text_predefpointers.asm if that strikes your fancy.

Ledges

If your tileset uses ledges and the like, you'll want to edit data/tilesets/ledge_tiles.asm and data/tilesets/pair_collision_ids.asm. Pair collision IDs refers to areas where you cannot pass between different tiles: so if they're next to each other, you can't pass through.

; FORMAT: tileset number, tile 1, tile 2
; terminated by -1
; these entries indicate that the player may not cross between tile 1 and tile 2
; it's mainly used to simulate differences in elevation

TilePairCollisionsLand::
	db CAVERN, $20, $05
...
	db FOREST, $5F, $2E
+	db NAMEHERE, $??, $??
	db -1 ; end

TilePairCollisionsWater::
	db FOREST, $14, $2E
	db FOREST, $48, $2E
	db CAVERN, $14, $05
+	db NAMEHERE, $??, $??
	db -1 ; end

If you need a unique area for this, you'll need ledge_tiles.asm.

LedgeTiles:
	; player direction, tile player standing on, ledge tile, input required
	db SPRITE_FACING_DOWN,  $2C, $37, D_DOWN
	db SPRITE_FACING_DOWN,  $39, $36, D_DOWN
	db SPRITE_FACING_DOWN,  $39, $37, D_DOWN
	db SPRITE_FACING_LEFT,  $2C, $27, D_LEFT
	db SPRITE_FACING_LEFT,  $39, $27, D_LEFT
	db SPRITE_FACING_RIGHT, $2C, $0D, D_RIGHT
	db SPRITE_FACING_RIGHT, $2C, $1D, D_RIGHT
	db SPRITE_FACING_RIGHT, $39, $0D, D_RIGHT
+	db SPRITE_FACING_DIRECTIONHERE, $??, $??, D_DIRECTIONHERE
	db -1 ; end

I don't completely understand how these work, so you'll need to do some fiddling.

Warp Pads and Holes

Warp pads and holes can spice up your tileset. You'll want to edit data/tilesets/warp_pad_hole_tile_ids.asm.

WarpPadAndHoleData:
	; tileset id, tile id, value for [wStandingOnWarpPadOrHole]
	db FACILITY, $20, 1 ; warp pad
	db FACILITY, $11, 2 ; hole
	db CAVERN,   $22, 2 ; hole
	db INTERIOR, $55, 1 ; warp pad
+	db NAMEHERE,   $??, X ; hole
	db -1 ; end

If you want a warp pad, change X to 1, and if you want a hole, change X to 2. Again, remember that the bottom-left tile is what you're after.

Things I haven't noted

  • You can add spinners with spinner_tiles.asm, but it's a little more involved and I've never done it before.
  • You can probably make more animated tiles, but I've never done this either.
  • You can expand the tileset headers to include more counter tiles, grass tiles, and so on, but this requires a pretty significant restructuring that I am personally not familiar with.