Skeetendo

’Cause all games were better on the GBC

You are not logged in.

#1 2015-02-11 18:34:38

Crystal_
Member
From: Spain
Registered: 2012-09-16
Post 243/450
Website

GSC Map Connection tutorial

Translated from my spanish generation II mapping guide: http://wahackforo.com/t-24385/


In order to connect two adjacent maps properly we need to make a connection between the two maps, taking into account the following things:
preparse_list_tag('
[*]Group id of connected map[/*]
[*]Map id of connected map[/*]
[*]Strip pointer[/*]
[*]Srip destination[/*]
[*]Strip length[/*]
[*]Connected map width[/*]
[*]Vertical align[/*]
[*]Horizontal align[/*]
[*]Window[/*]
', '*', $errors)
If the two maps are connected through a warp, the following are irrelevant:
preparse_list_tag('
[*]Vertical align[/*]
[*]Horizontal align[/*]
[*]Window[/*]
', '*', $errors)
If the connected map can't be seen from anywhere in the origin map, then there's no need to create a map connection for these two maps at all.


Locating the Strip

The strip covers the part of the connected map that can be seen anywhere from the origin map. Due to the gameboy window size, it may be necessary to take a margin of up to 3 blocks in order to cover all the viewing range.

Strip Pointer

Pointer to the block inside the strip with the lowest ROM address.

Strip Destination

It's calculated using one of the following formulas, depending on the type of connection:
preparse_list_tag('
[*]north: C703 + X + V[/*]
[*]west: C700 + (w+6) * (Y+3) + V[/*]
[*]south: C703 + (h+3) * (w+6) + X + V[/*]
[*]east: C6FD + (w+6) * (Y+4) + V[/*]
', '*', $errors)
Where:
preparse_list_tag('
[*]h: Origin map height[/*]
[*]w: Origin map width[/*]
[*]Y: Vertical offset[/*]
[*]X: Horizontal offset[/*]
[*]V: 0 for Pokemon Gold / Silver and 100 for Pokemon Crystal[/*]
', '*', $errors)
Vertical align

It's calculated using one of the following formulas, depending on the type of connection:
preparse_list_tag('
[*]north: (h*2) - 1[/*]
[*]west: (-2) * Y[/*]
[*]south: 0[/*]
[*]east: (-2) * Y[/*]
', '*', $errors)
Where:
preparse_list_tag('
[*]h: Origin map height[/*]
[*]w: Origin map width[/*]
[*]Y: Vertical offset[/*]
[*]X: Horizontal offset[/*]
', '*', $errors)
Horizontal align

It's calculated using one of the following formulas, depending on the type of connection:
preparse_list_tag('
[*]north: (-2) * X[/*]
[*]west: (2*w) - 1[/*]
[*]south: (-2) * X[/*]
[*]east: 0[/*]
', '*', $errors)
Where:
preparse_list_tag('
[*]h: Origin map height[/*]
[*]w: Origin map width[/*]
[*]Y: Vertical offset[/*]
[*]X: Horizontal offset[/*]
', '*', $errors)
Window

It's calculated using one of the following formulas, depending on the type of connection:
preparse_list_tag('
[*]north: C701 + (h) * (w+6) + V[/*]
[*]west: C706 + (2*w) + V[/*]
[*]south: C707 + w + V[/*]
[*]east: C707 + w + V[/*]
', '*', $errors)
Where:
preparse_list_tag('
[*]h: Origin map height[/*]
[*]w: Origin map width[/*]
[*]Y: Vertical offset[/*]
[*]X: Horizontal offset[/*]
[*]V: 0 for Pokemon Gold / Silver and 100 for Pokemon Crystal[/*]
', '*', $errors)
Example

Let's now see an example for every type of connection, using Pokemon Crystal as the base:
preparse_list_tag('
[*]from: Cherrygrove City - to: east a Route 29[/*]
[*]from: Route 29 - to: west a Cherrygrove City[/*]
[*]from: Cherrygrove City - to: north a Route 30[/*]
[*]from: Route 30 - to: south a Cherrygrove City[/*]
', '*', $errors)
We are going to use this picture as the reference:

QDaxCLb.png

from: Cherrygrove City - to: east to Route 29 (1)

preparse_list_tag('
[*]Strip and strip length

We identify the strip as the area of Route 29 that can be seen from Cherrygrove City, making southe that we keep a margin of three blocks where necessary. (Area 1). Therefore, the strip length is the area that the strip covers vertically, which macthes Route 29's height: 0x09.[/*]
[*]Strip Pointer

The block with the lowest ROM address in the strip (SP1). This block's address is 0xA92D5. Thus, the strip pointer is 0xD5 0x52.[/*]
[*]Strip Destination

In a map editor for instance, we can easily see that Cherrygrove City's width (w) is 0x14 and that its height (h) is 0x09. As this is an horizontal connection and both maps have the same height (and are perfectly aligned), offset Y will be null.

Applying the formula:

C6FD + (w+6) * (Y+4) + V
C6FD + (14+6) * (0+4) + 100 = C6FD + 1A * 4 + 100 = C865

Thus, Strip Destination is 0x65 0xC8.[/*]
[*]Vertical align

We have already said that Y is 0. Then, applying the formula

(-2) * Y

:

(-2) * 0 = 0

This, the vertical align byte is 0x00.[/*]
[*]Horizontal align

As was stated before, horizontal align is always 0x00 for east-bound (east type) connections.[/*]
[*]Window

We apply the formula

C707 + w + V

where w is the connected map width, that is, Route 29's width. Hence, w = 0x1E.

C707 + 1E + 100 = C825

Swapping the resulting bytes we get 0x25 0xC8.[/*]
', '*', $errors)

from: Route 29 - to: west to Cherrygrove City (2)

preparse_list_tag('
[*]Strip and strip length

The strip is the area marked by (Area 2) in the reference picture. You can easily notice that its length (strip length) is 0x09, like in the previous connection.[/*]
[*]Strip Pointer

Block marked by (SP2). Its ROM address is 0xACE20. Therefore, Strip Pointer = 0x20 0x4E.[/*]
[*]Strip Destination

We have to take Route 29's height (h=0x09) and width (w=0x1E). There's no offset either, and if we apply the formula we saw for west-bound connections we'll get:

C700 + (1E+6) * (0+3) + 100 = C86C

Which translates to a Strip Destination of 0x6C 0xC8.[/*]
[*]Vertical align

The corresponding formula is

(-2) * Y = (-2) * 0 = 0

So the vertical align byte is 0x00.[/*]
[*]Horizontal align

We take

(w*2) - 1

with w = 0x14 (Cherrygrove City's width).

The resulting horizontal align byte is 0x27[/*]
[*]Window

We apply the formula

C706 + (2*w) + V

where w es 0x14 and V is considered 0x100 because it is Pokemon Crystal.

We then swap the bytes and get 0x2E 0xC8.[/*]
', '*', $errors)

from: Cherrygrove City - to: north to Route 30 (3)

QDaxCLb.png
preparse_list_tag('
[*]Strip and Strip length

Strip length (Area 3) matches the width of Route 30: 0x0A[/*]
[*]Strip Pointer

0xE7 0x60 (SP3)[/*]
[*]Strip Destination

The formula we have to apply is:

C703 + X + V

where X is the horizontal offset.

To calculate the X offset we take the horizontal position of the leftmost block of the origin map minus the horizontal position of the leftmost block of the connected map's strip. Then, we count the number of blocks the latter is displaced relative to the former (see reference picture). In this case, the number of blocks of displacement is 5.

The formula is then

C703 + 5 + 100

which outputs a Strip Destination of 0x08 0xC8.

If we had a horizontal connection instead and we had to calculate the Y (vertical) offset, we'd take the vertical position of the upmost block of the origin map minus the vertical position (in blocks) of the upmost block of the connected map's strip, and finally count the displacement between the two.

The remaining paramenters are calculated in a similar way as shown in the previous two connections, but bearing in mind that the formulas to pick this time are those corresponding to north-bound connections, and that the horizontal offset has a value of 5. Notice how the vertical offset is never present in the north | south formulas, and vice versa.

If any formula output a negative result,(lower than 0x00), we must add 0x100 (256 in decimal) to the obtained result.[/*]
', '*', $errors)from: Route 30 - to: south to Cherrygrove City (4)

For this last connection, we are only going to expand on the significative differences with the previous connections.

When determining the strip, we have to keep in mind that the connected map is wider than the origin map. Thus, the strip not only has to cover the 3-block vertical margin, but also a 3-block horizontal margin in both sides to cover all the viewing range from Route 30. (see reference picture). Knowing that, the strip length will turn out to be Route 30's width + 6, that is, 0x10.

To calculate the horizontal offset, just like in the previous connection, we have to take the horizontal position of the leftmost block of the origin map minus the horizontal position of the leftmost block of the connected map's strip, and then, we count the number of blocks the latter is displaced relative to the former (see reference picture). In this case, the connected map's strip (Cherrygrove City) begins three blocks more to the left ("before") than the origin map (Route 30). This means that we are going to work with a negative X offset of -3.

This offset value of -3 will serve us calculate the Strip Destination, but to calculate the horizontal align, vertical align, and window (parameters that aren't related to the strip), the horizontal offset will be different. In these cases, we don't have to take the leftmost block of the connected map's strip, but the leftmost block of the connected map, which translates to an offset of -5 (see reference picture). In the connection (3) there was no difference between the offset used for the Strip Destination and the offset used for the subsequent parameters, since the position where the strip began matched the position where the whole map began (see reference picture).

Last edited by Crystal_ (2015-02-11 18:42:09)

Offline

#2 2015-02-21 10:37:26

Rocket Grunt
Member
Registered: 2014-12-27
Post 37/75

Re: GSC Map Connection tutorial

Thanks for this tutorial, that is very neat and handy and I seem to be able to connect different maps.

Last edited by Rocket Grunt (2015-02-21 10:37:51)


Today I won't steal Pokemon

Offline

#3 2015-02-23 10:52:44

comet
Member
Registered: 2012-04-09
Post 532/679

Re: GSC Map Connection tutorial

pokecrystal now has connection macros like pokered.

If you're still trying to ack rohms, here's an online calculator. In case it disappears into the ether, the html is below:

<title>Map connections</title>

<style>

    body {
        font-family: arial;
        font-size: 13px;
        text-align: center;
    }

    #content {
        display: inline-block;
    }

    #directions {
    }

    #form {
        display: inline-block;
        text-align: left;
        background-color: #eeeeee;
        padding: 8px;
        box-shadow: 5px 5px 10px #cccccc;
    }

    #output {
        display: inline-block;
        text-align: left;
        background-color: #f8e8e0;
        padding: 30;
        width: 100%;
        box-sizing: border-box;
    }

</style>

<script>

    var forms = {}
    var direction = 'north'

    var version_names = {
        'rby': 'Red/Blue/Yellow',
        'gs':  'Gold/Silver',
        'c':   'Crystal',
    }

    function setTitle(version) {
        var title = 'Map connections'
        var version_name = version_names[version]
        if (!!version_name) {
            title += ' ' + '(' + version_name + ')'
        }
        document.title = title
    }

    function serializeForm(form) {
        var json = {}
        for (var k in form.elements) {
            json[k] = form.elements[k].value
        }
        return json
    }

    function jsonToForm(form, json) {
        for (var k in form.elements) {
            form.elements[k].value = json[k]
        }
        return form
    }

    function saveDirection(direction) {
        var form = document.getElementById('form')
        var data = serializeForm(form)
        forms[direction] = data
    }

    function restoreDirection(direction) {
        var form = document.getElementById('form')
        var data = forms[direction]
        if (data) {
            form = jsonToForm(form, data)
        }
        return form
    }

    function init() {
        var form = document.getElementById('form')

        var interpretForm = function (event) {
            var data = serializeForm(form)

            // shoddy hack for non-chrome browsers
            attributes = [
                'width_1',
                'height_1',
                'width_2',
                'height_2',
                'strip_length',
                'alignment',
                'blockdata',
            ]

            var i
            for ( i = 0 ; i < attributes.length; i++ ) {
                data[attributes[i]] = data[i]
            }

            data.version = form.elements.version.value

            setTitle(data.version)
            calc(data)
        }

        interpretForm() // default values

        form.oninput  = interpretForm
        form.onchange = interpretForm

        var dirform = document.getElementById('directions')
        dirform.onchange = function () {
            saveDirection(direction)
            direction = dirform.elements.direction.value
            restoreDirection(direction)
            interpretForm()
        }
    }

    function calc(data) {

        var ram_start = {
            rby: 0xc6e8,
            gs:  0xc700,
            c:   0xc800,
        }[data.version]

        var blockdata = parseInt(data.blockdata, 16)

        var k, v
        for ( k in data ) {
            v = parseInt(data[k])
            if (!Number.isNaN(v)) {
                data[k] = v
            }
        }

        var connection = {

            north: {
                blockdata_offset: (data.width_2 * (data.height_2 - 3)),
                destination: ram_start + data.alignment + 3,
                strip_length: data.strip_length,
                other_width: data.width_2,
                y_align: data.height_2 * 2 - 1,
                x_align: data.alignment * -2,
                window: ram_start + data.height_2 * (data.width_2 + 6) + 1,
            },

            south: {
                blockdata_offset: 0,
                destination: ram_start + (data.height_1 + 3) * (data.width_1 + 6) + data.alignment + 3,
                strip_length: data.strip_length,
                other_width: data.width_2,
                y_align: 0,
                x_align: data.alignment * -2,
                window: ram_start + data.width_2 + 7,
            },

            west: {
                blockdata_offset: (data.width_2 - 3),
                destination: ram_start + (data.width_1 + 6) * (data.alignment + 3),
                strip_length: data.strip_length,
                other_width: data.width_2,
                y_align: data.alignment * -2,
                x_align: data.width_2 * 2 - 1,
                window: ram_start + data.width_2 + 7,
            },

            east: {
                blockdata_offset: 0,
                destination: ram_start + (data.width_1 + 6) * (data.alignment + 3 + 1) - 3,
                strip_length: data.strip_length,
                other_width: data.width_2,
                y_align: data.alignment * -2,
                x_align: 0,
                window: ram_start + data.width_2 + 7,
            },

        }[direction]



        if (!!blockdata) {
            if (blockdata > 0x4000) {
                blockdata = blockdata % 0x4000 + 0x4000
            }
            connection.blockdata = '0x' + (blockdata + connection.blockdata_offset).toString(16)
        } else {
            blockdata = '[blockdata address]'
            connection.blockdata = blockdata
            if (connection.blockdata_offset > 0) {
                connection.blockdata += ' + ' + connection.blockdata_offset
            }
        }


        function toText(items) {
            var text = ''
            var i, name, value
            for ( i = 0 ; i < items.length; i++ ) {
                name = items[i][0]
                value = items[i][1]
                text += '<strong>' + name + ':</strong> ' + value
                text += '<br>'
            }
            return text
        }

        var text = toText([
            ['Blockdata', connection.blockdata],
            ['Strip destination', '0x' + connection.destination.toString(16)],
            ['Strip length', connection.strip_length],
            ['Connected map width', connection.other_width],
            ['Y align', connection.y_align],
            ['X align', connection.x_align],
            ['Window', '0x' + connection.window.toString(16)],
        ])

        document.getElementById('output').innerHTML = text
    }

</script>

<body onload="init()">

    <div id="content">

        <form id="directions">
            <input type="radio" name="direction" value="north" checked> North
            <input type="radio" name="direction" value="south"> South
            <input type="radio" name="direction" value="east"> East
            <input type="radio" name="direction" value="west"> West
        </form>

        <form id="form">
            <strong>Map 1</strong>
            <br>
            Width: <input name="width_1" placeholder="Width" value=10>
            <br>
            Height: <input name="height_1" placeholder="Height" value=10>

            <br>
            <br>

            <strong>Map 2</strong>
            <br>
            Width: <input name="width_2" placeholder="Width" value=10>
            <br>
            Height: <input name="height_2" placeholder="Height" value=10>

            <br>
            <br>

            Strip length: <input name="strip_length" placeholder="Strip length" value=10>
            <br>

            Alignment: <input name="alignment" placeholder="Alignment" value=0>

            <br>
            <br>

            Blockdata address: <input name="blockdata" placeholder="(optional)">

            <br>
            <br>

            <strong>Version</strong>
            <br>
            <input type="radio" name="version" value="rby"> Red/Blue/Yellow
            <br>
            <input type="radio" name="version" value="gs" checked> Gold/Silver
            <br>
            <input type="radio" name="version" value="c"> Crystal
        </form>

        <br>

        <div id="output"></div>

    </div>

</body>

Last edited by comet (2015-02-23 10:56:55)

Offline

#4 2015-02-23 14:21:29

Mateo
Member
From: The Sims 4
Registered: 2009-11-25
Post 2,991/3,578

Re: GSC Map Connection tutorial

I'll check them out better after work, but that looks like it will be really helpful... both the switch to macros, and the calculator.

Offline

#5 2015-02-23 19:38:30

Miksy91
Member
Registered: 2010-10-16
Post 2,162/2,339

Re: GSC Map Connection tutorial

Nice tutorial, and in many ways better than mine. :)
One thing I picked up that you should probably add here would be an example of calculating values in a situation when the result is negative. It's self-explanotary when you understand how substractions with the processor work but before that... not so much.

As an example to others, -6 would be written as 256 - 6 = 250 (FA)

Edit:
Noticed that you actually had that information there - didn't really read the thread carefully enough.

Last edited by Miksy91 (2015-02-23 19:41:16)

Offline

Board footer

Powered by FluxBB