Difference between revisions of "Map Mode"

From Hero of Allacrost Wiki
Jump to: navigation, search
(Camera)
m (Camera)
Line 93: Line 93:
 
=== Camera ===
 
=== Camera ===
  
The map camera is always focused on a single map object. And is nothing more than a pointer to an active map object. Each map screen is drawn such that the focus is on whatever object the camera is pointing to, which is most often the character sprite that the player is controlling. The camera attempts to keep that object in the center of the screen, but if the object is too close to the edges of the map then the camera adjusts so that the view does not show any part of the screen beyond the map boundaries.
+
The map camera is always focused on a single map object. The camera is nothing more than a pointer to an active map object. Each map screen is drawn such that the focus is on whatever object the camera is pointing to, which is most often the character sprite that the player is controlling. The camera attempts to keep that object in the center of the screen, but if the object is too close to the edges of the map then the camera adjusts so that the view does not show any part of the screen beyond the map boundaries.
  
 
Each map has a special VirtualSprite object (called the virtual focus) created to serve as a focal point for the map when the camera needs to focus on an arbitrary location where no visible sprite exists. This is useful when trying to pan the map to observe a far away event that occurs off the screen from the current camera location. While setting the camera to focus on a specific object is simple to do, it can be visually jarring to the player for the camera to instantly jump to a new position. Therefore we often use a timed move, which is instructing the map code to move the camera to a new object over a defined period of time. The map code will then automatically handle a smooth movement from the old camera focus to the new focus.
 
Each map has a special VirtualSprite object (called the virtual focus) created to serve as a focal point for the map when the camera needs to focus on an arbitrary location where no visible sprite exists. This is useful when trying to pan the map to observe a far away event that occurs off the screen from the current camera location. While setting the camera to focus on a specific object is simple to do, it can be visually jarring to the player for the camera to instantly jump to a new position. Therefore we often use a timed move, which is instructing the map code to move the camera to a new object over a defined period of time. The map code will then automatically handle a smooth movement from the old camera focus to the new focus.
 
  
 
=== Contexts ===
 
=== Contexts ===

Revision as of 14:57, 13 August 2016

Map mode is responsible for a large portion of the game experience and the source code is the largest and most complex of any other game mode. Because of this size and the number of components in play to make map mode function properly, it is vital that we as keep this code organized so that it is easy to understand, maintain, and extend. The purpose of this page is to provide an overview of how this code works. Links to other pages of map mode documentation can be found below. These pages go into great detail about their inner workings and assume that the reader has a general understanding of map mode.


The rest of this page is broken up into three sections. First, we explain the concepts of map mode. Concepts are the fundamental ideas that went into the design of the map mode code and help the programmer to get an overview of the various elements that are found in Allacrost maps. The structures section goes into more specifics, providing a list and brief explanation of all classes and files in map mode, and how those structures are organized together. Finally we cover the operation of map mode. Here we describe the major steps that transpire when a map is loaded, updated, or drawn to the screen.


Concepts

The concepts of Allacrost maps that are important to understand in order to make sense of how the map code operates,and why it is designed in the manner that it is. Many of these concepts are intertwined with each other, which makes it difficult to explain them individually. First let's briefly outline them together as a group.

  • The most common three visual elements found on maps are tiles, objects, and the GUI.
    • The map environments (terrain, structures, etc.) are constructed by the map tiles.
    • Each tile is 32x32 pixels in size and are stored in tileset images that hold 256 tiles each (512x512 pixels).
  • Map objects are any entity on a map which are not stored in a tileset and they can be virtually any size.
    • Map sprites are a type of map object which move around on the screen.
    • All map objects have an invisible collision rectangle that is not allowed to overlap the collision grid or the collision rectangle of other objects
  • Tiles and objects are organized into map layers. A layer can contain either tiles or objects, but not both. A map can have any number of layers.
    • Map layers are drawn in a defined to construct the map and are always present on the screen.
  • The GUI elements are drawn above all tiles and objects.


Once you've got a firm grasp on the above, read on for an overview of the more advanced concepts.

  • An invisible collision grid determines where map objects may and may not go.
    • Each grid element is 16x16 pixels, meaning there are four grid elements for each tile location on the map
    • The grid state is set according to the properties of the tiles that cover each grid element.
  • A map context is a different view within the same map (for example, the exterior of a town is context #1, and the interior of a house is context #2)
    • Only one context is visible to the player at a time
    • Every context has the same layers and layer ordering, but different tiles
      • Likewise, every context effectively has its own collision grid
    • Map objects can only exist in one context at a time, and are allowed to move between contexts
    • A map has a minimum of one context and a maximum of 32 contexts

There's much more to maps than what was described, but this covers the core functionality of what elements are visible on the map and how those elements interact with one another. Read on to learn a little more about these specific concepts, as well as dialogues, events, zones, and other features.


Coordinate System

Map mode uses a coordinate system that is 64.0 in length and 48.0 in height. In a 1024x768 resolution, this system divides the screen up into 16x16 pixel squares, the same dimensions as the collision grid. The coordinates increase from left to right and from top to bottom, so the coordinates of the top left and bottom right corners of the screen are (0.0, 0.0) and (64.0, 48.0) respectively. Having the coordinate system correspond to the same dimensions of the collision grid greatly simplifies many drawing operations.

A separate 1024x768 coordinate system is used for drawing GUI elements on the map, such as the stamina meter and the dialogue window.


Tiles

All tiles are 32x32 pixel images that are extracted from a larger tileset image. Each tileset image is 512x512 pixels and can therefore hold 256 tile images. Both tiles and tilesets do not deviate from these styles. Both images use the PNG image format and native transparency is used. Each tileset image has a corresponding Lua file created called a tileset definition file (TDF). The TDF defines a number of properties about the images in the tileset, including which of the four quadrants are collidable (can not be walked on) and the frames and timings of animated tiles.

A map has at least one layer of tiles, and almost always have several. A tile image can be drawn on any layer and at any position on the layer. Most maps employ either three or four tile layers, depending on the needs of the map. As a typical example, a map may have a "ground" tile layer to place the surfaces that sprites and other objects are standing upon, a "detail" layer to place embellishments on the ground such as flowers or stones, and an "upper" layer for tiles that are to be drawn above sprites, like the tops of trees and buildings. The number and ordering of both tile and object layers is defined by the map script, so any combination of layers is possible. Most tiles are static, but some may be animated. Animated tiles have all their frames in the same tileset image and are built when the tileset is loaded.

One unique property to tile layers is that a tile layer may have all of it's collision data disabled. That is, any tiles placed on such a layer are declared to be fully un-collidable, even if they have collision data set in their corresponding TDF. This is done so that tiles that exist in a layer above objects (the tops of buildings, for instance) can be walked on, since the tiles are drawn over the objects instead of underneath them.

Objects

Objects encompass all tangible map entities which are not tiles. There are many types of map objects and include sprites, treasures, or stationary structures such as a statue. Each object's position is represented as the X/Y coordinates for the bottom-center point of the object. All objects can only exist in a single map context at a time, but may change contexts during the course of a map.

Objects have two rectangles aligned to their bottom-center coordinate: an image rectangle for the object's graphics, and an invisible collision rectangle. These two rectangles are not always the same size, and often the collision rectangle is made slightly smaller than the image rectangle. The size of these rectangles is normally static throughout the course of the map, but may be modified under different circumstances. The object holds the height and half-width of both of these rectangles, allowing us to compute all of the boundaries for each. There is no limit to the size of objects.

Specific types of objects have a number of different properties that determine their behavior on a map. All objects have these three base properties. - Updatable: objects can only update if they have this property enabled. Updating an object usually involves changing their position during movement or which animation frame to draw - Visible: objects can only be seen by the player when this property is enabled - Collidable: if disabled, this object does not collide with anything and it turns off any collision detection with other objects as well

Like tiles, map objects also exist on layers. Whereas a tile layer contains only tiles, an object layer contains only objects. A map may contain any number of object layers, but is guaranteed to have at least one. Objects may transition from one layer to another through events or movement on the map, such as walking up a staircase. Objects can only collide and interact with one another if they are on the same layer. Most commonly, object layers are ordered in between tile layers, so that objects are drawn above elements on the ground, but are covered by structures such as archways and rooftops.

An overview of the various object classes is provided below. Sprites are a common type of map object seen on a map, representing the player, NPCs, and enemies. The size of a typical adult human sprite is 32x64 pixels, or one tile wide and two tiles tall.

Type Description
MapObject An abstract base class inherited by all other object classes
PhysicalObject A simple and stationary object, such as a statue
TreasureObject A stationary object (like a treasure chest) that contains drunes and/or inventory for the player to procure
VirtualSprite A mobile object with no image data that is never seen by the player
MapSprite A mobile object that can be animated and interacted with through dialogue or other means
EnemySprite A mobile object representing an enemy, which may causes a battle to occur should the player's sprite come into contact with it

next

Camera

The map camera is always focused on a single map object. The camera is nothing more than a pointer to an active map object. Each map screen is drawn such that the focus is on whatever object the camera is pointing to, which is most often the character sprite that the player is controlling. The camera attempts to keep that object in the center of the screen, but if the object is too close to the edges of the map then the camera adjusts so that the view does not show any part of the screen beyond the map boundaries.

Each map has a special VirtualSprite object (called the virtual focus) created to serve as a focal point for the map when the camera needs to focus on an arbitrary location where no visible sprite exists. This is useful when trying to pan the map to observe a far away event that occurs off the screen from the current camera location. While setting the camera to focus on a specific object is simple to do, it can be visually jarring to the player for the camera to instantly jump to a new position. Therefore we often use a timed move, which is instructing the map code to move the camera to a new object over a defined period of time. The map code will then automatically handle a smooth movement from the old camera focus to the new focus.

Contexts

Map contexts are one of the more complicated and unique concepts of Allacrost maps. A map context is essentially "a map within a map" without having to create and load and entirely different map. For example, the outside area of a town may be one context, while the interior of a home in that town may be another context. The purpose of contexts is to allow us to quickly and efficiently transition to a different area of the map without having to represent it as its own unique map. This way, we can continue to simulate and update the environments of all of the contexts in a map and avoid any additional loading or re-loading of map data. Sprites and other map objects are allowed to traverse between one context and another through context switch points, such as the doorway to a home. Each map must have at least one context and can have a maximum of 32 contexts. The primary context in a map is called the base context, and is also referred to as context #01. Each context has its own separate tile grid and collision grid. This means that when switching from one context to the next, all the map tiles visible on the screen will be changed to represent the view of the new context.


All map objects exist on exactly one context and can not interact with one another unless both objects are in the same context. The context of the object that is pointed to by the map camera is called the active context. It determines which tiles and map objects are visible on the screen. Typically, map objects which exist on a different context than the active context are not visible, but some maps may allow objects of different contexts to appear on the map. It is not uncommon for a map sprite to change their context multiple times on a map, while stationary map objects will likely never change contexts. The most common manner in which sprites change their context is for them to move into what is called a context zone. A context zone is a designated area of a map where objects may transfer between two different contexts (and only two contexts). Context zones are a type of map zone, which is explained below.


One final feature of contexts is the concept of context inheritance. If a context inherits from one another, this means that it uses all the tiles from the inheriting context in its own tile grid, then overwrites some of the tiles to create a derivation of that original tile grid. This is done, for example, to make it easy to create a context that represents the interior of a house while still showing the environment outside the home. Without context inheritance, the map maker would have to recreate the entire outdoor area of the map for the new context. Context inheritance eases the burden of map designers by allowing a context to show portions of another context.


Zones

A map zone is nothing more than an area of the map that has a special property. The programmer can create a map zone on a granularity of 16x16 square pixel elements (the same as the collision grid). They may be of any size and shape that can be described in rectangular sections, and the sections may overlap (although it is inefficient code for them to do so). Map zones are used for a variety of purposes. One such purpose is to trigger scripted events, such as when the player moves their character sprite over a visible floor switch. A context zone allows sprites to transition between two contexts and are usually found near the entrances of doorways and stairwells. An enemy zone defines the area where enemy sprites will spawn and roam on a map. Many more special purpose zones exist to enable other common functionality such as defining the location of an audio source or making the player move more slowly through a marsh area.


Dialogues

Dialogues are an important part of maps as they are the primary means through which the player interacts with other characters and learns the information needed to proceed through the game. A single dialogue window is drawn on the bottom of the screen when a dialogue is active. Dialogues are constructed in such a way that they can have any form of flow from one line to the next (including circular flows), so do not falsely assume that they must be linear. Dialogues may also be injected with options to present to the player for them to select. The set of options allow the dialogue to "branch" in different directions based upon the option that the player selected and trigger different events. Programmers have the ability to define a script function to execute after each line is read and/or each option is selected. This grants us with an almost limitless flexibility to make dialogues an integral part of action scenes and other events.


Dialogues can occur between any number of characters, or may be a single character monologue. Most dialogues are between two sprites, usually an NPC and the player's character. All map sprites can reference any dialogue. By referencing a dialogue, a sprite indicates that it is one of the possible dialogues that should be executed if the sprite is spoken to by the player or through another means that begins a dialogue. A dialogue may be referenced by more than one sprite, although this situation is rare. Many sprites may reference more than one dialogue. All dialogue references are stored in a vector container (in the order in which the references were added) and the sprite contains a pointer to the next dialogue to be displayed and is incremented whenever a dialogue with the sprite has finished.


Dialogues additionally have the ability to set the maximum number of times that it may be viewed before becoming unavailable. This is used, for example, for dialogues that we only want the player to be presented with a single time. Once a dialogue becomes unavailable, it can no longer be viewed by the player. It may be made available again by changing the max view count or resetting the number of times that the dialogue has been seen. We may initially choose to set dialogues to a maximum view count of zero, and only set it to a non-zero value after a particular event on the map or elsewhere has transpired. When a sprite references a dialogue that the player has never read before (and is also not unavailable), a small animated "chat" icon will float above the sprite's head if the player moves their character near the sprite. The chat icon will only disappear once all available dialogues have been read at least once by the player.

Treasures

Treasures may either be plainly visible (like a chest) or be partially or fully hidden (like an occasional sparkle on the ground). When the player interacts with a treasure object for the first time, a small menu window pops up. This window displays the contents of the treasure which the player may view the details of.


Actions

Map sprites that are not controlled by the player need to be given instructions on what to do so that they do not simply remain stationary and motionless. The solution to this problem is what we call sprite actions. All map sprites have a vector for holding actions and will execute each action one at a time, and then repeat the cycle. Actions may include path movement (moving from a source to a destination), random movement for a period of time, displaying special animation frames for the sprite, or other forms of action.


Events

TODO: In map code terminology, an "event" is any action which takes control of the game away from the player in order to let a scripted sequence play out.


Audio

TODO: outline audio sources (mostly sounds) and how they work


Map File

Every single map has associated with it a Lua script file that defines the properties and provides facility for customizing the map via script functions. This file is typically referred to as the map file, but is also sometimes referred to as the map script file or script file. The data it contains includes the dimensions of the map, the tiles and tilesets used, all map objects and sprites that are used, dialogues spoken, the number of contexts in the map and associated tile and collision grid data for each, the audio files that are used on the map, and many other things. There is a wiki page dedicated to explaining in more detail about the contents and format of the map file at this location Map_File.


Every map file contains at least three functions called Load, Update, and Draw. The load function is called only once when the map is constructed and assists the map in loading external data (images, audio files) needed and properly initializing the map for use. The Update and Draw functions are both called once per execution of the main game loop when the map they represent is the active game mode. These functions perform any custom operations necessary to construct the map, such as updating scripting events or drawing graphical effects.

Structures

Now that we have a grasp of the major map concepts, lets discuss how we represent them in the code.


Supervisors

The highest level class in the map code is the MapMode class, which derives from the GameMode class in the mode management engine. Because there is so much to manage and process for each map, there are a number of "supervisor" classes that handle a specific task set so that the MapMode class can remain a reasonable size. The MapMode class creates an instance of each of these supervisor classes upon construction. The table below lists all supervisor classes and what types of data and operation that they are responsible for. Additional data and responsibilities of the map code not listed in the table are usually managed by the MapMode class directly.


Supervisor Class Name Data and Responsibilities
TileSupervisor Holds all map tile data and is responsible for all tile update and drawing operations
ObjectSupervisor Holds the collision grid and manages all map objects, including collision detection, pathfinding, and drawing
DialogueSupervisor Contains all map dialogues and processes dialogues when they are activated
EventSupervisor Responsible for processing all map events and event timings
TreasureSupervisor Displays the contents of procured treasures and manages user input while this window is active

Files

The map code is split into several C++ files (and many more map script files are written in Lua). Each C++ file begins with the word "map" to make it clear that the file belongs to map mode. The table below lists each file name (minus .h/.cpp extension) and what map classes that file contains. A brief explanation of each class is also provided.

File Name Class Name Class Purpose
map MapRectangle Represents a rectangular section of a map
MapLayer An abstract class representing a single draw layer of map visuals (tiles or objects)
MapFrame Retains information about how the next map frame should be drawn
MapMode Handles the game execution while the player is exploring maps
map_actions SpriteAction An abstract class for representing a sprite action
ActionPathMove Moves a sprite from a source position to a destination
ActionRandomMove Action for causing random movement of sprites
ActionAnimate Action that displays specific sprite frames for a certain period of time
map_dialogue MapDialogue Represents dialogues between characters on a map
MapDialogueOptions A container class for option sets presented in dialogue
DialogueWindow A display window for all GUI controls and graphics necessary to execute a dialogue
DialogueSupervisor Manages dialogue execution on maps
map_events EventLink Small container class holding information about how an event is linked to one of its child events
MapEvent Abstract class that represents a single event that occurs on a map
DialogueEvent Event which activates one of the map's dialogues
ShopEvent Causes the player to enter a newly created instance of ShopMode
SoundEvent Plays a sound file exactly one time
MapTransitionEvent Causes a new map to be loaded, ends the current map, and sends the player to the new map
JoinPartyEvent Occurs when a game character joins the player's party
BattleEncounterEvent Creates a BattleMode object and sends the player off to fight the battle
CustomEvent An event that is scripted entirely in Lua
SpriteEvent Abstract class that performs an action on a single sprite
ChangeDirectionSpriteEvent Changes the direction that a sprite is facing
AnimateSpriteEvent Displays a specific animation for the sprite
RandomMoveSpriteEvent Moves a sprite in a random direction for a certain amount of time
PathMoveSpriteEvent Moves a sprite from their current location to destination coordinates using a computed path
CustomSpriteEvent A sprite event that is scripted entirely in Lua
EventSupervisor A helper class to MapMode responsible that manages, processes, and launches all map events
map_sprites VirtualSprite A special type of sprite with no physical image
MapSprite A mobile map object with which the player can interact with
EnemySprite A mobile map object that induces a battle to occur if the player touches it
map_tiles MapTile Represents a single image tile on the map
TileLayer An object to represent one of the draw layer of map tiles
TileSupervisor A helper class to MapMode responsible for all tile data and operations
map_treasures MapTreasure Represents a treasure on the map which the player may access
TreasureSupervisor Displays the contents of a discovered treasure in a menu window
map_zones ZoneSection Represents a rectangular area on a map
MapZone Represents a zone on a map that can take any shape
EnemyZone Represents an area where enemy sprites spawn and roam in
ContextZone Represents an area where the active map context may switch

Operation

Map States

The MapMode class has a state member that determines how to update and draw the map and how to process input commands from the player. The current state also determines which of the supervisor classes should be called to update the state of the map and draw graphics to the screen. The MapMode class state member can be set at any time from anywhere in the map code or map script file. The table below lists all map states, the active supervisors in each (active indicating that it has update/draw calls being made to it by MapMode), and an explanation of each state.


Supervisor Class Name Data and Responsibilities
Explore State TileSupervisor, ObjectSupervisor The default map state where the player is free to explore and interact with the map
Dialogue State TileSupervisor, ObjectSupervisor, DialogueSupervisor Active when a dialogue is taking place. The dialogue window is present on the screen during this state.
Treasure State TileSupervisor, ObjectSupervisor, TreasureSupervisor Active after the player has procured a treasure. The treasure menu is present on the screen during this state.


Typically a map starts in the explore state, although this is not always the case. The other states are entered from the explore state by the user triggering an interaction with the map either intentionally (by talking to a NPC sprite) or unintentionally (by moving their character over a hidden switch). The state that is triggered will run its course and then usually returns to the explore state, although it may choose another state to put the map in once it is finished. As a quick example, imagine we are in the explore state when the user talks to a nearby NPC, moving the map to the dialogue state. In the middle of the dialogue, we may be sent to the event state due to a sudden occurrence like an explosion. A second dialogue to occur immediately after the event, sending the map back into the dialogue state before finishing and returning to the explore state.


Loading

To create and load a new map instance, first a new MapMode object is created. The MapMode constructor requires a string argument, which represents the name of the map file. The constructor will verify that it can access and open the map file and then create instances of all supervisor classes as well as any miscellaneous setup for its other members. The MapMode class will extract the basic properties from the map, such as the name of the map and the number of contexts it uses, load all audio files used on the map, and create objects to represent each type of enemy that the player may encounter on the map. The supervisor classes may also have their Load functions called if available to extract their relevant data from the map file. Finally, MapMode will call the Load function defined in the map file. This allows each map to perform any custom load operations that may be necessary, such as changing the state of map objects or dialogues from their defaults.


The TileSupervisor has a particularly complex load function. It has to open every tileset definition file for every tileset used by the map. It first determines which tiles of the tileset are used and which should be discarded (so that we don't waste video memory with tile images that will not be used). It also determines if any tiles are animated and collects all the animation frames and timing into a single animated image. And then it must perform a somewhat complex index translation so that the map tiles are referencing the correct images in the image vector. The ObjectManager has a much easier task of loading the collision grid from the map file data.


Load operations for map can take a long time (a few seconds) depending on the size of the map. To mask this latency, we perform a couple tricks. First, we do a gradual fade to blackness out of the current map to buy us some time. Second, we have the ability to pre-load maps in a different thread while the current map is active. So if the player exits the current map to another, we may already have the map pre-loaded and ready to be used immediately.


Update

The steps that the Update function performs vary based on the current map state. In general, the following operations are performed in this order.

  1. Call the map file's Update script function
  2. Accept and process user input
  3. Update the display status of all animated tiles
  4. Update all zones and objects on the map


User input is handled in multiple places in the map code. In the explore state, it is handled by the MapMode class. In the dialogue and treasure states, it is handled in the DialogueSupervisor and TreasureSupevisor class respectively. The most complex portion of the update operation is that of updating all map sprites. Map sprites in motion have to be constantly re-examined for collision detection and to process the next action in the case of sprites not controlled directly by the player. We also have to figure out if their path is blocked and needs to be re-computed, which direction they are facing and which animation should be drawn for that sprite in the next frame display. Each map object has its own implementation of an Update function and each object is updated in significantly different ways.


Draw

Before any draw operations begin, the very first thing that the draw code does is to figure out the position of the map camera. This information allows the draw code to know which tiles and objects would be visible on the screen should they be drawn. After this information is determined, the map file's Draw function is called. Usually this will do nothing more than draw the tile and object layers, although it may do additional drawing operations. For example, a map with fog would require the script to call a function in the video engine to generate that visual effect. After the map file completes its draw code, all active GUI displays are drawn.

The standard order of draw layers in map mode is below. Note, however, that a map script does have the ability to draw these layers in any order that it wishes (very rare) or insert other draw calls in between any of the draw layers (not uncommon). Recall that ground objects are drawn in two passes to allow them to go over and under certain structures.

  1. Tiles: lower layer
  2. Tiles: middle layer
  3. Objects: ground layer (pass 1)
  4. Objects: pass layer
  5. Objects: ground layer (pass 2)
  6. Tiles: upper layer
  7. Objects: sky layer


Mode Interoperation

MapMode is really the cornerstone mode of operation for the game. Most other game modes are entered from map mode and they return control of the game to map mode when they are finished. The table below illustrates how map mode is entered from other game modes and under what conditions it exits to other game modes.

TODO: create the table showing battle mode, menu mode, shop mode, boot mode, map mode, pause/quit mode transitions.