Profile Views

Introduction

In technical terms, the view section checks a pre-defined space against the larger space defined in region and the attributes piped in from RAM through the peek bytes. If successful, Grid Cartographer starts to show an avatar that mirrors the actions you take in the game you're currently playing through DosBox (you look north, your avatar looks north -> you look right then your avatar in GC looks right, and so on).

In less technical terms, imagine you have a large dungeon map on paper, you glue an item representing your party/character at a certain position on the map. Then you take out another paper, cut out a square field and put it over the map. Now when you move the cover paper over your map, you see only a small view of your original map, and only when you reach the position where you glued your party/char item do you see yourself in the map.

This is similar to how views in GC work, only when three elements align (region definition = map, view definition = cut-out field of cover paper, peek bytes = player position) will Grid Cartographer work and show your avatar.

First View example

Okay, the below example from Wizardry is a very simple view. I go through each element step-by-step

  • views
  • /views

Opening and close statement for the whole view section

  • packetview region="1"
  • /packetview

Opening and close statement of individual views, the opening contains a reference to the correct region

  • check offset="0" length="4" value="315a4957"

This is basically a check for the packet header defined under header, and it needs to be the same, to make the view work

  • seq offset="4" length="1"

I let HiddenAsbestos explain that himself
I should explain byte '4', the 'seq' byte, as it's probably not obvious since I didn't end up using it, although it seemed like a good idea at the time. All it is, is a simple wrapping counter from 0 - 255 that loops around and it's just there as a way of telling if the packet is new or arrived out of order (since this is an extension of a UDP based system originally). It never came up so it just sits there unused and mysterious

  • <xpos offset="5" length="1" min="0" max="14" />

This special check tells your view that the x-axis (in this definition of the view) goes from 0 to 20 (which is 14 in Hexcode).

  • <ypos offset="6" length="1" min="0" max="14" />

This special check tells your view that the y-axis (in this definition of the view) goes from 0 to 20 (which is 14 in Hexcode).

  • <face offset="7" length="1" n="0" e="1" s="2" w="3" />

This special check tells your view that the four cardinal directions are encode with 0,1,2,3
And yes, there are games where these are encoded in a different sequence 1,2,3,0 or with different numbers 1,2,3,4 or 2,4,6,8.

  • <floor offset="8" length="1" min="1" max="a" dir="down" />

This special check tells your view that the region has ten level from 1 to 10 (A is 10 in Hex) and direction is down (so B1 to B10). If you're not entirely sure whether your last level of e.g. 10 will be internally encode as 0 to 9 or 1 to 10, you can always +1 to the max to make sure your views don't go out of bounds, if you don't actually playtest your profile.

<views>

        <!-- Maze -->
        <packetview region="1">
            <check offset="0" length="4" value="315a4957" />
            <seq offset="4" length="1" />
            <xpos offset="5" length="1" min="0" max="14" />
            <ypos offset="6" length="1" min="0" max="14" />
            <face offset="7" length="1" n="0" e="1" s="2" w="3" />
            <floor offset="8" length="1" min="1" max="a" dir="down" />
        </packetview>

    </views>

Obviously this view is very simple, it goes directly to the border of the map defined under region. For example you could defined a view who only shows a part of the map and otherwise your party/char avatar disappears. The below view only shows your avatar when it moves on the left site of each dungeon (up to position 10 on the x-axis).

<views>

        <!-- Maze -->
        <packetview region="1">
            <check offset="0" length="4" value="315a4957" />
            <seq offset="4" length="1" />
            <xpos offset="5" length="1" min="0" max="A" />
            <ypos offset="6" length="1" min="0" max="14" />
            <face offset="7" length="1" n="0" e="1" s="2" w="3" />
            <floor offset="8" length="1" min="1" max="a" dir="down" />
        </packetview>

    </views>

Class

The next example from the Dragon Wars profile introduces a number of new concepts (classes, general checks, move command).

  • class name="base"
  • /class

Opening and closure statement for class declarations.

If you have a lot of views for different or even the same region under views, it makes sense to take the elements that are the same in every view out and put them into their own class, which the views can then reference. Usually the elements that can be taken out are the first check offset, the seq offset and the facing offset.

If you use class, then you need to reference it in each individual view (packetview extends="base").

<views>

        <class name="base">
            <check offset="0" length="4" value="534F4444" />
            <seq offset="4" length="1" />
            <face offset="7" length="1" n="0" e="1" s="2" w="3" />
         </class>

        <!-- Dilmun -->
        <packetview extends="base" region="900">
            <xpos offset="5" length="1" min="0" max="2f" />
            <ypos offset="6" length="1" min="1" max="1f" />
            <move x="0" y="-1" />
            <check offset="8" length="1" value="00" />
         </packetview>

        <!-- Purgatory -->
        <packetview extends="base" region="1">
             <xpos offset="5" length="1" min="0" max="21" />
            <ypos offset="6" length="1" min="0" max="21" />
            <check offset="8" length="1" value="01" />
        </packetview>

    </views>

General checks

  • check offset="8" length="1" value="00"

Often, beyond facing and x-/y-axis, you need further checks to differentiate parts of maps, and often they are not as nicely structured as with the floor check like in the Wizardry example. In this case you need to identify RAM addresses that uniquely represent a floor or even part of a map and change when you change floor or map structure. And then use a check to make a view identify this part. You can have more than one general check (I think from my own profiles the most were three general checks, but it could go up further depending on how the game structures maps internally).

Move

  • move x="0" y="-1"

This command shifts your view 0 steps on the x-axis (-1 is west, +1 is east) and 1 step north (-1 is north, +1 is south). This is needed if the internal technical representation of the map is unlike how it looks like in the game (a lot of game maps with different levels are technical trickery where all of the game is actually on one plane and often far apart sections are actually close together, technically). For example the Wizardry 6 and the Wizardry 7 profiles use the move command extensively. Sometimes you also need it to just align the map somewhat, not just shift individual map pieces around.

Match pts

  • match pts="22,14 23,14"

Move allows you to shift a part of a map (or a whole map) on the x-/y-axis, but what if your object isn't that well formed. In that case you can use "match pts" as from the example below. This allows you to define an area by naming all the x/y-axis square of the object, and then shifting it.

        <!-- Secret Area -->
        <packetview region="1" extends="forest" tag="2">
            <match pts="22,14 23,14 24,14 25,14 22,15 23,15 25,15 26,15 27,15 28,15 29,15" />
            <move x="-10" y="-3" />
        </packetview>

        <!-- Forest -->
        <packetview region="1" extends="forest" tag="1">
            <move x="-8" y="4" />
        </packetview>

Mask pts

  • mask pts="25,18"

Another command that allows you to define irregular objects is mask. Unlike match, mask is used when you don't want your avatar to appear on certain squares in your map. In the case from the example below (Wasteland), I needed mask because there was some behind-the-scene technical trickery that made your avatar jump to a certain space in the Quartz map, which was highly annoying when playing.

Another use is if you actually don't want the player to see your character in certain spaces, maybe because you want to emulate darkness fields, and the specific area is irregular and not easily cut out of the larger map.

<!-- Quartz -->
        <packetview extends="base" region="7">
            <xpos offset="5" length="1" min="1" max="1e" />
            <ypos offset="6" length="1" min="1" max="1e" />
            <check offset="9" length="1" value="01" />
            <mask pts="25,18" />
            <const_floor>G</const_floor>
        </packetview>

Const_floor

The following example from the Captive profile, shows how to shift around two pieces of one map to create a map with two levels. Pelphi Inside is actually one map, with ladders that go up and down just transporting you to different parts of the map. Instead we define two different views which act as two different levels, and then via the move command align them with each other (so that they are on top of each other).

  • const_floor>F1</const_floor

To make sure Grid Cartographer knows on which floor you are at any time, it assigns a floor to each view

<views>

        <!-- Classes -->
        <class name="base">
            <check offset="0" length="4" value="54504143" />
            <seq offset="4" length="1" />
            <xpos offset="5" length="1" min="0" max="3c" />
            <ypos offset="6" length="1" min="0" max="3c" />
            <face offset="7" length="1" n="0" e="3" s="2" w="1" />
        </class>

        <!-- Pelphi Outside -->
        <packetview extends="base" region="2">
            <check offset="8" length="1" value="1" />
            <check offset="9" length="1" value="57" />
            <const_floor>G</const_floor>
        </packetview>

        <!-- Pelphi Inside F1 -->
        <packetview extends="base" region="2">
            <xpos offset="5" length="1" min="0" max="e" />
            <ypos offset="6" length="1" min="0" max="7" />
            <check offset="8" length="1" value="1" />
            <check offset="9" length="1" value="e1" />
            <const_floor>F1</const_floor>
        </packetview>

        <!-- Pelphi Inside F2 -->
        <packetview extends="base" region="2">
            <xpos offset="5" length="1" min="10" max="3f" />
            <ypos offset="6" length="1" min="0" max="e" />
            <check offset="8" length="1" value="1" />
            <check offset="9" length="1" value="e1" />
            <move x="-16"/>
            <const_floor>F2</const_floor>
        </packetview>

    </views>

View checks overriding class checks

The above example also shows something else, that if you put in checks inside of view (for x-/y-axis), then they override the checks in the class definition. Basically the class definition established the total limit (0 to hex 3c for both x-/y-axis) for all views that use it, but each individual view can override it so that only the view definition is valid when the individual view catches your party/char in a game. That said, the checks in the view cannot be higher than those in the class definition (so if class goes 0 to hex 3c, a view cannot do 0 to 3d).

Check_or

The following example from the Metroid profile shows the usage of

  • check_or

Let's say you have a view, where one of your check addresses switches later, because your party has moved beyond a certain plot point and internally this is represented by switching that RAM address to a different value. In that case, you can use the check_or command as seen in the example below to check for different values to make the view still work even with the switch.

<!-- Classes -->
        <class name="base">
            <check offset="0" length="4" value="4247324d" />
            <seq offset="4" length="1" />
        </class>

        <!-- Landing Site -->

        <packetview extends="base" region="1">
            <xpos offset="5" length="1" min="5" max="7" />
            <ypos offset="6" length="1" min="5" max="7" />
            <check_or>
                <check offset="7" length="1" value="64" />
                <check offset="7" length="1" value="69" />
            </check_or>
            <check offset="8" length="1" value="10" />
            <const_floor>G</const_floor>
        </packetview>

Scalex & scaley

The following example from the Zeliard profile shows the usage of

  • scalex div="5"

This is for the case when you want to add a map to something like a platformer, and don't have well-formed map coordinates, but rather real coordinates of your player character (for example they go from 0 to 250 or from 0 to 5000). With scalex you scale down the real number to a number more easily manageable, in this example the real number (240) is divided by 5 and scaled down to something more easily managed (48).

So when you use scalex, the "xpos check" will still use the real numbers (0 to 239), but the map shown in Grid Cartographer only shows position downscaled (0 to 47).

The same for scaley, the "ypos check" will still use real numbers, but the map shown in Grid Cartographer only shows positions downscaled.

<views>

        <!-- CLASSES -->
        <class name="base">
            <check offset="0" length="4" value="534F445A" />
            <seq offset="4" length="1" />
         </class>

        <!-- Malicia -->
        <packetview extends="base" region="1">
            <scalex div="5" />
            <scaley div="5" />
            <xpos offset="6" length="1" min="0" max="ef" />
            <ypos offset="8" length="1" min="0" max="3f" />
            <check offset="9" length="1" value="4" />
            <const_floor>G</const_floor>
        </packetview>

Face

attributes: offset, length, mask [optional], n, e, s, w
meaning: Reads a value from the packet from byte [offset,offset+length) up to 32-bits, bitwise 'and' with the optional mask. Then matches it against one of four constants specified in the n,e,s,w attributes that correspond to the four cardinal directions. If one matches then the direction is applied to the avatar, if none match then avatar direction defaults to 'unknown' state with no direction.

Xypos

attributes: offset, length, mask, stride
meaning: Reads a value from the packet from byte [offset,offset+length) up to 32-bits, bitwise 'and' with the optional mask. This value is then converted into an Y position using value / stride and an X position is taken from the remainder of this division.

Used by Eye of the Beholder.

<xypos offset="6" length="2" stride="20" />

It's saying read 2 bytes and get Y by dividing the value by 32 (Hex 20) and X is the remainder.

Below is a visualization of the concept, basically a X/Y grid is sliced into individual rows which then are connected together to an array.

Untitled.png

Regname

attributes: offset, length, op [optional]
meaning: Reads a string of ASCII characters from the packet from offset to offset+length-1. The optional op attribute can be set to 'UP' to force the string into upper case. The string is used to change the region name, with the "prefix" attribute of the region used as a prefix.

Used by Forgotten Realms: Unlimited Adventures

Regw & regh

attributes: offset, length, mask
meaning: Reads a value from the packet from byte [offset,offset+length) up to 32-bits, bitwise 'and' with the optional mask. This value is then clamped between 0 and 255 and used to update the region width (regw) or height (regh).

Used by Forgotten Realms: Unlimited Adventures as the size of the maps can be edited.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License