Files
HeroesStone_PRC8/_module/nss/sj_tilemagic_i.nss
Jaysyn904 1eefc84201 Initial Commit
Initial Commit.
2025-09-14 15:40:46 -04:00

1400 lines
55 KiB
Plaintext

// -----------------------------------------------------------------------------
// sj_tilemagic_i
// -----------------------------------------------------------------------------
/*
Sunjammer's TileMagic System
============================
This system provides a more flexible and greatly expanded feature-set over
the original TileMagic functions. It offers a a variety of ways for a user
to take advange of TileMagic.
1. Scripted: can be used by a scripter or builder during design.
2. Markers: are waypoints (or placeables) that can be used by a builder
during design or a DM during a game to create tiles. Once placed they
will remain until converted into the appropriate tile(s) by a script or
by a Control object.
3. Autotiles: are placeables that can be used by a builder during design or
a DM during a game to create tiles. Once placed they automatically
convert themselves into the appropriate tile(s).
4. DM Widget: [work in progress] can be used by a DM to create or remove
tiles during a game by targeting the ground and using a conversation
interface.
Contents
========
- sj_debug_i - script, library of debug functions (cut-down)
- sj_tilemagic_dm - script, OnActivateItem event/tag script for DM item
- sj_tilemagic_ent - script, OnEnter event script for area/trigger
- sj_tilemagic_hb - script, OnHeartbeat event script for autotile/marker
- sj_tilemagic_i - script, library of TileMagic functions
- sj_utility_i - script, library of utility functions (cut-down)
- sj_tilemagic_autotile - placeable
- sj_tilemagic_control - placeable
- sj_tilemagic_dm - item
- sj_tilemagic_marker - waypoint
* all blueprints are located in their Custom > Special > Custom 3 palette.
The sj_tilemagic_* Controls
===========================
Various controls are available for both the "marker" and "autotile" methods.
These are stored as local variables on these object and influence aspects of
"tile" they create. Most are optional have a default value that will be used
if omitted.
- tile: an int value for a SJ_TILEMAGIC_ORDINARY_* or SJ_TILEMAGIC_FEATURE_*
constant. For example enter 402 for SJ_TILEMAGIC_ORDINARY_GRASS.
- z: a float value for the height in metres at which the tile will appear.
This can be used with both the normalise and the use_layer settings to
control where this value is measured from. Default: 0.0.
- normalise: an int value (FALSE or TRUE) to indicate if the z setting is to
be adjusted to compensate for the tile's built-in non-zero height. TRUE
means z will be adjusted to compensate and FALSE means the z will not.
Default: FALSE.
- use_layer: an int value (FALSE or TRUE) to indicate if the z setting is to
be adjusted to account for a marker's current position. (This is done
automatically for autotiles to aid alignment on uneven ground.) FALSE
means the z setting will be measured from 0m and TRUE means z will be
measured from the nearest 1m layer (rounded down). Default: FALSE.
- columns: an int value indicating the number of columns covered by a range.
This can be used with both the rows and corner settings to control the
extent and starting point of a TileMagic range. Default: 0
- rows: an int value indicating the number of rows covered by a range. This
can be used with both the columns and corner settings to control the
extent and starting point of a TileMagic range. Default: 0
- corner: an int value (1, 3, 7 or 9) indicating the position of the object
in relation to a range. This can be used when the default position is
unavailable or inappropriate. The numbers correspond to the position of
the keys on a numeric keypad. For example enter 3 to indicate that the
object is in the bottom-right or SE corner of a TileMagic range. Extra
care should be taken when range when one of the dimensions is 1 tile.
Default: 0 (interpreted as 1, i.e. bottom-left or SW)
- rotation: an int value (0 to 3) indicating the number of "quarter turns"
a tile effect should be rotated. A value of (-1) indicates that the
number of "quarter turns" should be determined randomly. The rotation is
applied counter-clockwise relative to the tile's default position.
Default: 0 (SJ_ROTATION_NONE)
Matrix and Palette
==================
A matrix is essentially a map of ASCII characters. Each matrix consists of
an index of rows, each row is described by a string of characters and each
character represents a style code which can be interpreted (using a palette)
to determine what TileMagic effect (if any) should be created for a given
tile in a given area. A matrix can contain up to 32 rows and a row can
contain up to 32 characters. A character can be alphanumeric, a symbol or a
space. The data used to create a matrix can be predefined in a 2da file or
can be defined at design time through scripting.
A palette is essentially a list of style information. Each palette consists
of an index of styles, each style is identified by a style code and
described by a variety of attributes which can be employed (using a matrix)
to determine what TileMagic effect (if any) should be created for a given
tile in a given area. Different palettes can associate the same style code
to describe different styles. A style code is a single character and can be
alphanumeric or a symbol. The data used to create a palette can be
predefined in a 2da file or can be defined at design time through scripting.
Duplicated visualeffect.2da Entries
===================================
Various entries in visualeffects.2da use the same model when generating the
TileMagic effect and two look the same but actually use different models.
When one of the former is encountered the corresponding SJ_TILEMAGIC_*
constant is use. The latter have individual SJ_TILEMAGIC_* constants.
- rows 347, 348 & 453 are all the same marble arch tile
- rows 404 & 441 are both the same ant-hill tile
- rows 428 & 431 are both the same waterfall tile
- rows 429 & 430 are both the same transporter tile
- rows 432 & 456 are both the same city market tile
- rows 401 & 437 are different blue water tiles
Hints and Tips
==============
- CoverTile and CoverSpot can be used to co-locate two tile nodes on exactly
the same spot. Otherwise tile nodes must be at least 0.01m apart.
- To ensure foward compatability only use alhpa-numeric characters for style
codes. Any special characters that may appear will use symbols.
Known Issues
============
There are several known issues with TileMagic:
- sound effects: the footsteps sound cannot be changes and so may break the
illusion of, for example, wading through water. I suggest that builders
use other sounds to mask this where possible.
- graphics: there are issues with redrawing tiles and with the interaction
of TileMagic with fog and/or skyboxes.
- cpu hit: some TileMagic effects are quite intensive.
- errors: row 440 in visualeffects.2da has a typo in the tile model so the
tile won't display. The SJ_TILEMAGIC_FEATURE_GRASS_GRAVES constant has
been included as a bug report has been submitted and it is hope it will
be fixed in 1.64 or later.
TODO @ 2.04
===========
- implement a check for empty 2DA files (eg: nIndex == 0 && sRow == "")
- implement a post datapoint creation data load
- implement tile effect specific delays
- implement GetColumn/RowFromLocation (for widget)
- implement Serialize/Deserialize
- provide support for GLOW/AURA effects
- addon: ill-effect triggers
- addon: DM widget
*/
// -----------------------------------------------------------------------------
/*
Version 2.03 - 20 May 2006 - Sunjammer
- fixed incorrect case in GetCorrectedConstant
- fixed prototype description for CoverSpot
- added CoverLargeArea function
- added CreateTile function
Version 2.02 - 10 Oct 2004 - Sunjammer
- added rotation component to all Cover* functions
Version 2.01 - 10 Aug 2004 - Sunjammer
- added various Palette functions
- added various Matrix functions
- added various Spot functions
- added unique node tagging
- removed "GetNearest" schema
Version 2.00 - 25 Jul 2004 - Sunjammer
- added placeables and waypoint systems
- added height normalisation and layer heights
- added constants for all unique effects.2da entries
- rewritten
Version 1.00 - 24 Dec 2003 - Sunjammer
- created
*/
// -----------------------------------------------------------------------------
#include "sj_utility_i"
// -----------------------------------------------------------------------------
// CONSTANTS: SETTINGS
// -----------------------------------------------------------------------------
// Sets the delay applied to all Clear functions to offset any delay inherrent
// in tile creation, default 0.0.
const float SJ_TILEMAGIC_DELAY_CLEAR = 0.0;
// Sets the ASCII character to be used to represent no style in a matrix.
// NOTE: single character, default "."
const string SJ_TILEMAGIC_STYLE_NONE = ".";
// Set to TRUE to render a matrix from the top (northern edge) of an area down
// (south) rather than from the bottom (sothern edge) up (north). This allows a
// matrix to visibly resemble the area more closely and makes defining it more
// natural. Default TRUE.
const int SJ_TILEMAGIC_TOPDOWN_MATRIX = TRUE;
// -----------------------------------------------------------------------------
// CONSTANTS: SYSTEM
// -----------------------------------------------------------------------------
// "keypad" corners
const int SJ_TILEMAGIC_CORNER_SE = 3;
const int SJ_TILEMAGIC_CORNER_SW = 1;
const int SJ_TILEMAGIC_CORNER_NE = 9;
const int SJ_TILEMAGIC_CORNER_NW = 7;
// constants for ordindary tiles
const int SJ_TILEMAGIC_ORDINARY_CAVE = 406; // Caves
const int SJ_TILEMAGIC_ORDINARY_GRASS = 402; // Mini
const int SJ_TILEMAGIC_ORDINARY_LAVA = 350; // Dungeon
const int SJ_TILEMAGIC_ORDINARY_ICE = 426; // Frozen
const int SJ_TILEMAGIC_ORDINARY_PIT = 506; // Underdark
const int SJ_TILEMAGIC_ORDINARY_WATER_BLUE_1 = 401; // Rural
const int SJ_TILEMAGIC_ORDINARY_WATER_BLUE_2 = 437; // Rural
const int SJ_TILEMAGIC_ORDINARY_WATER_GREEN = 461; // Sewer
const int SJ_TILEMAGIC_ORDINARY_JAIL = 511; // Castle Interior
// constants for feature tiles
const int SJ_TILEMAGIC_FEATURE_CAVE_MINING_PILE = 434; // Caves - Pile of earth & tools
const int SJ_TILEMAGIC_FEATURE_CAVE_STONE_COLUMN = 435; // Caves - Stone column & water
const int SJ_TILEMAGIC_FEATURE_CITY_GAZEBO = 457; // City Exterior - Gazebo
const int SJ_TILEMAGIC_FEATURE_CITY_HOUSE_1 = 449; // City Exterior - House, larger
const int SJ_TILEMAGIC_FEATURE_CITY_HOUSE_2 = 438; // City Exterior - House, smaller
const int SJ_TILEMAGIC_FEATURE_CITY_MARBLE_ARCH = 347; // City Exterior - Marble arch
const int SJ_TILEMAGIC_FEATURE_CITY_MARKET_1 = 432; // City Exterior - Market, square
const int SJ_TILEMAGIC_FEATURE_CITY_MARKET_2 = 455; // City Exterior - Market, angled
const int SJ_TILEMAGIC_FEATURE_CITY_RUBBLE = 433; // City Exterior - Wall section and rubble
const int SJ_TILEMAGIC_FEATURE_CITY_RUINED_HOUSE = 451; // City Exterior - House, ruined
const int SJ_TILEMAGIC_FEATURE_CITY_RUINED_ARCH = 452; // City Exterior - Marble arch, ruined
const int SJ_TILEMAGIC_FEATURE_CITY_RUINS_1 = 450; // City Exterior - Burnt out ruins, round
const int SJ_TILEMAGIC_FEATURE_CITY_RUINS_2 = 454; // City Exterior - Burnt out ruins, square
const int SJ_TILEMAGIC_FEATURE_CITY_WAGON = 458; // City Exterior - Wagon
const int SJ_TILEMAGIC_FEATURE_GRASS_CARAVAN = 439; // Rural - Caravan
const int SJ_TILEMAGIC_FEATURE_GRASS_GRAVES = 440; // Rural - Graves
const int SJ_TILEMAGIC_FEATURE_GRASS_ANT_HILL = 404; // Rural - Ant-hill
const int SJ_TILEMAGIC_FEATURE_GRASS_CRYSTAL = 405; // Rural - Crystal in sandpit
const int SJ_TILEMAGIC_FEATURE_ICE_BUMP = 442; // Frozen - Bump in ground
const int SJ_TILEMAGIC_FEATURE_ICE_CASTLE_WALL = 443; // Frozen - Evil castle wall, facing west
const int SJ_TILEMAGIC_FEATURE_ITHILLID_PILLAR = 427; // Ithillid - Pillar
const int SJ_TILEMAGIC_FEATURE_ITHILLID_TRANSPORTER = 429; // Ithillid - Transporter
const int SJ_TILEMAGIC_FEATURE_KITCHEN_LANTERN = 436; // City Interior - Kitchen floor & lantern
const int SJ_TILEMAGIC_FEATURE_LAVA_FOUNTAIN = 349; // Dungeon - Lava fountain
const int SJ_TILEMAGIC_FEATURE_UNDERDARK_WATERFALL = 428; // Underdark - Waterfall, facing west
// object blueprints
const string SJ_RES_INVISIBLE_OBJECT = "plc_invisobj";
// object tags
const string SJ_TAG_TILEMAGIC_AUTOTILE = "sj_tilemagic_autotile";
const string SJ_TAG_TILEMAGIC_CONTROL = "sj_tilemagic_control";
const string SJ_TAG_TILEMAGIC_MARKER = "sj_tilemagic_marker";
const string SJ_TAG_TILEMAGIC_WIDGET = "sj_tilemagic_dm";
// general purpose prefix
const string SJ_TILEMAGIC_PREFIX = "sj_tilemagic_";
// name of 2DA columns
const string SJ_TILEMAGIC_2DA_NORMALISE = "NORMALISE";
const string SJ_TILEMAGIC_2DA_ROTATION = "ROTATION";
const string SJ_TILEMAGIC_2DA_ROW = "ROW";
const string SJ_TILEMAGIC_2DA_STYLE = "STYLE";
const string SJ_TILEMAGIC_2DA_TILE = "TILE";
const string SJ_TILEMAGIC_2DA_Z = "Z";
// name of local variables
const string SJ_VAR_TILEMAGIC_AREA_ID = "sj_tilemagic_area_id";
const string SJ_VAR_TILEMAGIC_COLUMNS = "sj_tilemagic_columns";
const string SJ_VAR_TILEMAGIC_CORNER = "sj_tilemagic_corner";
const string SJ_VAR_TILEMAGIC_MATRIX = "sj_tilemagic_matrix";
const string SJ_VAR_TILEMAGIC_OFFSET = "sj_tilemagic_offset";
const string SJ_VAR_TILEMAGIC_NORMALISE = "sj_tilemagic_normalise";
const string SJ_VAR_TILEMAGIC_PALETTE = "sj_tilemagic_palette";
const string SJ_VAR_TILEMAGIC_ROTATION = "sj_tilemagic_rotation";
const string SJ_VAR_TILEMAGIC_ROWS = "sj_tilemagic_rows";
const string SJ_VAR_TILEMAGIC_TILE = "sj_tilemagic_tile";
const string SJ_VAR_TILEMAGIC_USE_LAYER = "sj_tilemagic_use_layer";
const string SJ_VAR_TILEMAGIC_Z = "sj_tilemagic_z";
// -----------------------------------------------------------------------------
// PROTOTYPES
// -----------------------------------------------------------------------------
// Registers sRow data string describing row nIndex with oMatrix. The string
// consists of a combination of up to 32 alphanumeric characters, symbols or
// spaces.
// - oMatrix: datapoint reference
// - nIndex: row number (0 to 31)
// - sRow: string of style codes
void SJ_TileMagic_AddRowToMatrix(object oMatrix, int nIndex, string sRow);
// Registers the style information describing style code sStyle with oPalette.
// The style code is a single alphanumeric character or symbol.
// - oPalette: datapoint reference
// - sStyle: single style code
// - nTile: SJ_TILEMAGIC_* / SJ_TILEMAGIC_FEATURE_* constant
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - nRotation: SJ_ROTATION_* constant
void SJ_TileMagic_AddStyleToPalette(object oPalette, string sStyle, int nTile, float fZ=0.0, int bNormalise=TRUE, int nRotation=0);
// Removes all TileMagic tiles in oArea.
// - oArea: area containing TileMagic tiles
void SJ_TileMagic_ClearArea(object oArea);
// Removes any TileMagic tiles in oArea that fall within the specified border.
// - oArea: area containing TileMagic tiles
// - nWidth: width of border to be cleared
void SJ_TileMagic_ClearBorder(object oArea, int nWidth=1);
// Removes any TileMagic tiles in oArea that have a non-blank style code within
// the specified matrix or the matrix registered with oArea if oMatrix is not
// specified.
// - oArea: area containing TileMagic matrix
// - oMatrix: datapoint reference
void SJ_TileMagic_ClearMatrix(object oArea, object oMatrix=OBJECT_INVALID);
// Removes any TileMagic tiles in oArea that fall within the specified range.
// - oArea: area containing TileMagic tiles
// - nColumn: column index of bottom left corner of range
// - nRow: row index of bottom left corner of range
// - nColumns: number of columns to be cleared
// - nRows: number of rows to be cleared
void SJ_TileMagic_ClearRange(object oArea, int nColumn=0, int nRow=0, int nColumns=1, int nRows=1);
// Removes a TileMagic tile in oArea that falls on the spot fX, fY.
// - oArea: area containing TileMagic matrix
// - fX: x co-ordinate (0.00 to 319.99)
// - fY: y co-ordinate (0.00 to 319.99)
void SJ_TileMagic_ClearSpot(object oArea, float fX, float fY);
// Removes a TileMagic tile from the specified location in oArea.
// - oArea: area containing TileMagic tile
// - nColumn: column index of tile
// - nRow: row index of tile
void SJ_TileMagic_ClearTile(object oArea, int nColumn=0, int nRow=0);
// Converts (and in the process destroys) a TileMagic object into an appropriate
// TileMagic tile in taking account of all the settings stored on oObject.
// - oObject TileMagic object to be converted
void SJ_TileMagic_ConvertObjectToTile(object oObject=OBJECT_SELF);
// Covers oArea with TileMagic tiles. The tiles have a vertical offset of fZ
// modified by nTile's internal offset unless using bNormalise.
// - oArea: area to be tiled
// - nTile: SJ_TILEMAGIC_ORDINARY_* / SJ_TILEMAGIC_FEATURE_* constant
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - nRotation: SJ_ROTATION_* constant
void SJ_TileMagic_CoverArea(object oArea, int nTile, float fZ=0.0, int bNormalise=TRUE, int nRotation=0);
// Covers a specified border-shaped range around oArea's with TileMagic tiles.
// The border extends nWidth tiles into oArea. The tiles have a vertical offset
// of fZ modified by nTile's internal offset unless using bNormalise.
// - oArea: area to be tiled
// - nTile: SJ_TILEMAGIC_ORDINARY_* / SJ_TILEMAGIC_FEATURE_* constant
// - nWidth: width of border (in number of tiles)
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - nRotation: SJ_ROTATION_* constant
void SJ_TileMagic_CoverBorder(object oArea, int nTile, int nWidth=1, float fZ=0.0, int bNormalise=TRUE, int nRotation=0);
// Covers oArea with TileMagic tiles in quarters to avoid TMI errors. The tiles
// have a vertical offset of fZ modified by nTile's internal offset unless using
// bNormalise.
// NOTE: if you have to use this function then try to restrict using it to the
// OnModuleLoad event as it may cause a very obvious pause in the game.
// - oArea: area to be tiled
// - nTile: SJ_TILEMAGIC_ORDINARY_* / SJ_TILEMAGIC_FEATURE_* constant
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
void SJ_TileMagic_CoverLargeArea(object oArea, int nTile, float fZ = 0.0, int bNormalise=TRUE);
// Covers a specified matrix of tiles within oArea with TileMagic tiles. The
// matrix defines which tiles should be covered while the palette defines what
// TileMagic effect should be used. The matrix has a vertical offset (fOffset)
// which is additional to the tile's fZ and bNormalise controls.
// - oArea: area containing TileMagic matrix
// - oMatrix: datapoint reference
// - oPalette: datapoint reference
// - fOffset: height of the matrix
void SJ_TileMagic_CoverMatrix(object oArea, object oMatrix, object oPalette, float fOffset=0.0);
// Covers a specified range of in oArea with TileMagic tiles. The range extends
// from the specified nColumn, nRow tile (which represents the bottom-left of
// the range) nColumns wide and nRows high. The tiles have a vertical offset of
// fZ modified by nTile's internal offset unless using bNormalise.
// - oArea: area to be tiled
// - nTile: SJ_TILEMAGIC_ORDINARY_* / SJ_TILEMAGIC_FEATURE_* constant
// - nColumn: column index of bottom left corner of range
// - nRow: row index of bottom left corner of range
// - nColumns: number of columns to be covered
// - nRows: number of rows to be covered
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - nRotation: SJ_ROTATION_* constant
void SJ_TileMagic_CoverRange(object oArea, int nTile, int nColumn=0, int nRow=0, int nColumns=1, int nRows=1, float fZ=0.0, int bNormalise=TRUE, int nRotation=0);
// Covers the equivalent of a single tile (10m x 10m) centred at the specified
// spot (x, y). The tile has a vertical offset of fZ modified by nTile's
// internal offset unless using bNormalise.
// - oArea: area containing spot
// - nTile: SJ_TILEMAGIC_* / SJ_TILEMAGIC_FEATURE_* constant
// - fX: x co-ordinate (0.00 to 319.99)
// - fY: y co-ordinate (0.00 to 319.99)
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - fRotation: 0.00 to 359.99
void SJ_TileMagic_CoverSpot(object oArea, int nTile, float fX, float fY, float fZ=0.0, int bNormalise=TRUE, float Rotation=0.0);
// Covers a single tile in oArea with a TileMagic tile. The tile has a vertical
// offset of fZ modified by nTile's internal offset unless using bNormalise.
// - oArea: area containing tile
// - nTile: SJ_TILEMAGIC_ORDINARY_* / SJ_TILEMAGIC_FEATURE_* constant
// - nColumn: column index tile
// - nRow: row index tile
// - fZ: height of the tile
// - bNormalise: TRUE (to compensate for tile's internal offset) or FALSE
// - nRotation: SJ_ROTATION_* constant
void SJ_TileMagic_CoverTile(object oArea, int nTile, int nColumn, int nRow, float fZ=0.0, int bNormalise=TRUE, int nRotation=0);
// Creates and returns a uniquely identified datapoint to contain a matrix. A
// matrix is essentially a map of ASCII characters. Each matrix consists of an
// index of rows, each row is described by a string of characters and each
// character represents a style code which can be interpreted (using a palette)
// to determine what TileMagic effect (if any) should be created for a given
// tile in a given area. A matrix can contain up to 32 rows and a row can
// contain up to 32 characters. A character can be alphanumeric, a symbol or a
// space. The data used to create a matrix can be predefined in a 2da file or
// can be defined at design time through scripting.
// - sName: unique identifier, upto 16 characters
// - bUse2DA: TRUE (to load from sName.2da) or FALSE
// * Returns: datapoint reference
// * OnError: OBJECT_INVALID
object SJ_TileMagic_CreateMatrix(string sName, int bUse2DA=FALSE);
// Creates and returns a uniquely identified datapoint to contain a palette. A
// palette is essentially a list of style information. Each palette consists of
// an index of styles, each style is identified by a style code and described by
// a variety of attributes which can be employed (using a matrix) to determine
// what TileMagic effect (if any) should be created for a given tile in a given
// area. Different palettes can associate the same style code to describe
// different styles. A style code is a single character and can be alphanumeric
// or a symbol. The data used to create a palette can be predefined in a 2da
// file or can be defined at design time through scripting.
// - sName: unique identifier, upto 16 characters
// - bUse2DA: TRUE (to load from sName.2da) or FALSE
// * Returns: datapoint reference
// * OnError: OBJECT_INVALID
object SJ_TileMagic_CreatePalette(string sName, int bUse2DA=FALSE);
// Creates a tile node with the specified tag at the specified location and
// applies the appropriate TileMagic effect.
// NOTE: this function has no error checking and is designed to be called by
// functions that can ensure the validity of the parameters they are passing.
object SJ_TileMagic_CreateTile(int nTile, location lTile, string sTag);
// Returns oArea's TileMagic ID. If no ID has been assigned to the area the next
// available ID is assigned and that is returned.
int SJ_TileMagic_GetAreaID(object oArea);
// Returns an SJ_TILEMAGIC_* constant for values matching duplicate entries in
// visualeffects.2da. Returns nTile unmodified in all other circumstances.
int SJ_TileMagic_GetCorrectedConstant(int nTile);
// Returns the column index oObject.
int SJ_TileMagic_GetColumn(object oObject);
// Returns the layer index oObject.
int SJ_TileMagic_GetLayer(object oObject);
// Returns fZ modified to account for a TileMagic tile's built-in non-zero
// height if it has one. Returns fZ unmodified in all other circumstances.
float SJ_TileMagic_GetNormalisedHeight(int nTile, float fZ);
// Returns the row index oObject.
int SJ_TileMagic_GetRow(object oObject);
// Returns a unique tag for a TileMagic spot node.
string SJ_TileMagic_GetTagForSpot(object oArea, float fX, float fY);
// Returns a unique tag for a TileMagic tile node.
string SJ_TileMagic_GetTagForTile(object oArea, int nColumn, int nRow);
// Switches the matrix used to cover oArea with TileMagic tiles.
// - oArea: area containing TileMagic matrix
// - oMatrix: datapoint reference
// - fOffset: height of the matrix
void SJ_TileMagic_SwitchMatrix(object oArea, object oMatrix, float fOffset=0.0);
// Switches the palette used to cover oArea with TileMagic tiles.
// - oArea: area containing TileMagic matrix
// - oPalette: datapoint reference
void SJ_TileMagic_SwitchPalette(object oArea, object oPalette);
// Returns a string description of a TileMagic tile.
string SJ_TileMagic_TileToString(object oArea, string sTag, int nTile=0);
// -----------------------------------------------------------------------------
// DEPRECATED
// -----------------------------------------------------------------------------
void TLChangeAreaGroundTileRange(object oArea, int nTile, int nColumn, int nRow, int nColumns=1, int nRows=1, float fZ=-0.4)
{
// this is included for backwards compatability only and should not be used
SJ_TileMagic_CoverRange(oArea, nTile, nColumn, nRow, nColumns, nRows, fZ, FALSE);
}
void TLResetAreaGroundTileRange(object oArea, int nColumn, int nRow, int nColumns=1, int nRows=1)
{
// this is included for backwards compatability only and should not be used
SJ_TileMagic_ClearRange(oArea, nColumn, nRow, nColumns, nRows);
}
// -----------------------------------------------------------------------------
// FUNCTIONS
// -----------------------------------------------------------------------------
void SJ_TileMagic_AddRowToMatrix(object oMatrix, int nIndex, string sRow)
{
string sDebug;
string sIndex = IntToString(nIndex);
// sanity checks
if(sRow == "")
{
// check if row exists
sDebug = "SJ_TileMagic_AddRowToMatrix: failled as row data for row ( " + sIndex + ") was null.";
}
else if(nIndex < 0 || nIndex > 32)
{
// check if row is in limits
sDebug = "SJ_TileMagic_AddRowToMatrix: failled as row ( " + IntToString(nIndex) + ") exceeded lower or upper bounds.";
}
else if(GetIsObjectValid(oMatrix) == FALSE)
{
// check if datapoint exists
sDebug = "SJ_TileMagic_AddRowToMatrix: failled as datapoint was invalid.";
}
else if(GetLocalString(oMatrix, sIndex) != "")
{
// check if row exists
sDebug = "SJ_TileMagic_AddRowToMatrix: failled as row ( " + sIndex + ") already exisited in datapoint.";
}
// result
if(sDebug == "")
{
// valid matrix and no collisions: register row data
SetLocalString(oMatrix, sIndex, sRow);
}
else
{
// log an error
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
void SJ_TileMagic_AddStyleToPalette(object oPalette, string sStyle, int nTile, float fZ=0.0, int bNormalise=TRUE, int nRotation=0)
{
string sDebug;
// sanity checks
if(sStyle == "")
{
// check if style is a single character
sDebug = "SJ_TileMagic_AddStyleToPalette: failled as style was null.";
}
else if(GetStringLength(sStyle) > 1)
{
// check if style is a single character
sDebug = "SJ_TileMagic_AddStyleToPalette: failled as style ( " + sStyle + ") exceeded 1 character and is invalid.";
}
else if(GetIsObjectValid(oPalette) == FALSE)
{
// check if datapoint exists
sDebug = "SJ_TileMagic_AddStyleToPalette: failled as datapoint was invalid.";
}
else if(GetLocalString(oPalette, sStyle) == sStyle)
{
// check if style exits
sDebug = "SJ_TileMagic_AddStyleToPalette: failled as style ( " + sStyle + ") already exisited in datapoint.";
}
// result
if(sDebug == "")
{
// valid palette and no collisions: register style data
SetLocalString(oPalette, sStyle, sStyle);
SetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_TILE, nTile);
SetLocalFloat(oPalette, sStyle + SJ_VAR_TILEMAGIC_Z, fZ);
SetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_NORMALISE, bNormalise);
SetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_ROTATION, nRotation);
}
else
{
// log an error
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
void SJ_TileMagic_ClearArea(object oArea)
{
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
// clear each tile in area
int nCurColumn, nCurRow;
for(nCurColumn = 0; nCurColumn < nColumns; nCurColumn++)
{
for(nCurRow = 0; nCurRow < nRows; nCurRow++)
{
SJ_TileMagic_ClearTile(oArea, nCurColumn, nCurRow);
}
}
}
void SJ_TileMagic_ClearBorder(object oArea, int nWidth = 1)
{
// sanity check
if(nWidth < 1)
{
// log an error
string sDebug = "SJ_TileMagic_ClearBorder: failled as border width (" + IntToString(nWidth) + ") was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
// clear each tile in border
int nCurColumn, nCurRow;
for(nCurColumn = 0; nCurColumn < nColumns; nCurColumn++)
{
for(nCurRow = 0; nCurRow < nRows; nCurRow++)
{
// A tile will fall within the border if its column or row index is
// less than the border's width OR if its column index is the same
// or greater than the number of columns less the border width OR if
// its row index is the same or greater than the number of rows less
// the border width.
if(nCurColumn < nWidth
|| nCurRow < nWidth
|| nCurRow >= (nRows - nWidth)
|| nCurColumn >= (nColumns - nWidth))
{
SJ_TileMagic_ClearTile(oArea, nCurColumn, nCurRow);
}
}
}
}
void SJ_TileMagic_CoverLargeArea(object oArea, int nTile, float fZ = 0.0, int bNormalise = TRUE)
{
// get width and height
int nCols = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
// get the mid-point of each (rounded down)
int nCol = nCols / 2;
int nRow = nRows / 2;
// cover each quarter of the area using AssignCommand to give each call to
// CoverRange call a separate instruction count and thus avoid a TMI
// NOTE: use (nCols - nCol) and (nRows - nRow) to handle areas where height
// and/or width is an odd number of tiles, e.g. 25x25.
AssignCommand(OBJECT_SELF, SJ_TileMagic_CoverRange(oArea, nTile, 0, 0, nCol, nRow, fZ, bNormalise));
AssignCommand(OBJECT_SELF, SJ_TileMagic_CoverRange(oArea, nTile, nCol, 0, (nCols - nCol), nRow, fZ, bNormalise));
AssignCommand(OBJECT_SELF, SJ_TileMagic_CoverRange(oArea, nTile, 0, nRow, nCol, (nRows - nRow), fZ, bNormalise));
AssignCommand(OBJECT_SELF, SJ_TileMagic_CoverRange(oArea, nTile, nCol, nRow, (nCols - nCol), (nRows - nRow), fZ, bNormalise));
}
void SJ_TileMagic_ClearMatrix(object oArea, object oMatrix = OBJECT_INVALID)
{
// check if a matrix was supplied
if(GetIsObjectValid(oMatrix) == FALSE)
{
// if no matrix is supplied use
oMatrix = GetLocalObject(oArea, SJ_VAR_TILEMAGIC_MATRIX);
// sanity check
if(GetIsObjectValid(oMatrix) == FALSE)
{
// log an error
string sDebug = "SJ_TileMagic_ClearMatrix: valid matrix could not be located.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
}
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
int nColumn, nRow;
for(nRow = 0; nRow < nRows; nRow++)
{
// if using the TOPDOWN setting then start at the top row and work down
// otherwise start at the bottom row and work up as normal
// NOTE: use (nRows - 1) because rows are numbered from 0
int nIndex = (SJ_TILEMAGIC_TOPDOWN_MATRIX) ? (nRows - 1) - nRow : nRow;
// get row data for the current row, skip row if null
string sRow = GetLocalString(oMatrix, IntToString(nIndex));
// skip row if row data null
if(sRow == "")
{
// NOTE: do not log an error as this is permitted
continue;
}
// if there is an entry for this row then
for(nColumn = 0; nColumn < nColumns; nColumn++)
{
// get the style code for the current column
string sStyle = GetSubString(sRow, nColumn, 1);
// skip if style is blank (space or user-defined)
if(sStyle != " " && sStyle != SJ_TILEMAGIC_STYLE_NONE)
{
SJ_TileMagic_ClearTile(oArea, nColumn, nRow);
}
}
}
// unregister matrix and palette with area
DeleteLocalObject(oArea, SJ_VAR_TILEMAGIC_MATRIX);
DeleteLocalObject(oArea, SJ_VAR_TILEMAGIC_PALETTE);
DeleteLocalFloat(oArea, SJ_VAR_TILEMAGIC_OFFSET);
}
void SJ_TileMagic_ClearRange(object oArea, int nColumn = 0, int nRow = 0, int nColumns = 1, int nRows = 1)
{
// sanity check
if(nColumns < 1 || nRows < 1)
{
// log an error
string sDebug = "SJ_TileMagic_ClearRange: failed as the number of columns (" + IntToString(nColumns) + ") or rows (" + IntToString(nRows) + ") was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
// clear tiles in range
int nCurColumn, nCurRow;
for(nCurColumn = nColumn; nCurColumn < nColumn + nColumns; nCurColumn++)
{
for(nCurRow = nRow; nCurRow < nRow + nRows; nCurRow++)
{
SJ_TileMagic_ClearTile(oArea, nCurColumn, nCurRow);
}
}
}
void SJ_TileMagic_ClearSpot(object oArea, float fX, float fY)
{
// use unique tag to ID tile node
string sTag = SJ_TileMagic_GetTagForSpot(oArea, fX, fY);
object oTile = GetObjectByTag(sTag);
if(GetIsObjectValid(oTile))
{
// clear tile
DestroyObject(oTile, SJ_TILEMAGIC_DELAY_CLEAR);
}
else
{
// log an error
string sDebug = "SJ_TileMagic_ClearSpot: failed as target (" + SJ_TileMagic_TileToString(oArea, sTag) + ") did not exist.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
void SJ_TileMagic_ClearTile(object oArea, int nColumn = 0, int nRow = 0)
{
// use unique tag to ID tile node
string sTag = SJ_TileMagic_GetTagForTile(oArea, nColumn, nRow);
object oTile = GetObjectByTag(sTag);
if(GetIsObjectValid(oTile))
{
// remove tile node
DestroyObject(oTile, SJ_TILEMAGIC_DELAY_CLEAR);
}
else
{
// log an error
string sDebug = "SJ_TileMagic_ClearTile: failed as target (" + SJ_TileMagic_TileToString(oArea, sTag) + ") was not a valid object.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
void SJ_TileMagic_ConvertObjectToTile(object oObject = OBJECT_SELF)
{
// get positional data for object
int nColumn = SJ_TileMagic_GetColumn(oObject);
int nRow = SJ_TileMagic_GetRow(oObject);
object oArea = GetArea(oObject);
// get data from object
int bNormalise = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_NORMALISE);
int bUseLayer = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_USE_LAYER);
int nColumns = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_COLUMNS);
int nCorner = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_CORNER);
int nRotation = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_ROTATION);
int nRows = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_ROWS);
int nTile = GetLocalInt(oObject, SJ_VAR_TILEMAGIC_TILE);
float fZ = GetLocalFloat(oObject, SJ_VAR_TILEMAGIC_Z);
// dimensions can be omitted but we cannot have 0 columns or rows so correct
nColumns = (nColumns == 0) ? 1 : nColumns;
nRows = (nRows == 0) ? 1 : nRows;
// adjust height for autotile (always) or marker (if flagged)
if(GetTag(oObject) == SJ_TAG_TILEMAGIC_AUTOTILE || bUseLayer)
{
// height increases by 5m per layer
fZ += IntToFloat(SJ_TileMagic_GetLayer(oObject));
}
// the default position for an autotile or marker is the bottom-left or SW
// corner of a range. If nCorner has been used to change it's position then
// nColumn and nRow must be adjusted to take account for this.
switch(nCorner)
{
case SJ_TILEMAGIC_CORNER_NE:
nColumn -= (nColumns - 1);
nRow -= (nRows - 1);
break;
case SJ_TILEMAGIC_CORNER_NW:
nRow -= (nRows - 1);
break;
case SJ_TILEMAGIC_CORNER_SE:
nColumn -= (nColumns - 1);
break;
}
// cover a tile or each tile in as required
int nCurColumn, nCurRow;
for(nCurColumn = nColumn; nCurColumn < nColumn + nColumns; nCurColumn++)
{
for(nCurRow = nRow; nCurRow < nRow + nRows; nCurRow++)
{
SJ_TileMagic_CoverTile(oArea, nTile, nCurColumn, nCurRow, fZ, bNormalise, nRotation);
}
}
// immediate self-destruct
DestroyObject(oObject);
}
void SJ_TileMagic_CoverArea(object oArea, int nTile, float fZ=0.0, int bNormalise=TRUE, int nRotation=0)
{
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
// cover each tile in area
int nCurColumn, nCurRow;
for(nCurColumn = 0; nCurColumn < nColumns; nCurColumn++)
{
for(nCurRow = 0; nCurRow < nRows; nCurRow++)
{
SJ_TileMagic_CoverTile(oArea, nTile, nCurColumn, nCurRow, fZ, bNormalise, nRotation);
}
}
}
void SJ_TileMagic_CoverBorder(object oArea, int nTile, int nWidth=1, float fZ=0.0, int bNormalise=TRUE, int nRotation=0)
{
// sanity check
if(nWidth < 1)
{
string sDebug = "SJ_TileMagic_CoverBorder: failled as border width (" + IntToString(nWidth) + ") was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
// cover each tile in border
int nCurColumn, nCurRow;
for(nCurColumn = 0; nCurColumn < nColumns; nCurColumn++)
{
for(nCurRow = 0; nCurRow < nRows; nCurRow++)
{
// A tile will fall within the border if its column or row index is
// less than the border's width OR if its column index is the same
// or greater than the number of columns less the border width OR if
// its row index is the same or greater than the number of rows less
// the border width.
if(nCurColumn < nWidth
|| nCurRow < nWidth
|| nCurRow >= (nRows - nWidth)
|| nCurColumn >= (nColumns - nWidth))
{
SJ_TileMagic_CoverTile(oArea, nTile, nCurColumn, nCurRow, fZ, bNormalise, nRotation);
}
}
}
}
void SJ_TileMagic_CoverMatrix(object oArea, object oMatrix, object oPalette, float fOffset=0.0)
{
// sanity check
if(GetIsObjectValid(oMatrix) == FALSE)
{
// log an error
string sDebug = "SJ_TileMagic_CoverMatrix: matrix supplied was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
else if(GetIsObjectValid(oPalette) == FALSE)
{
// log an error
string sDebug = "SJ_TileMagic_CoverMatrix: palette supplied was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
// register matrix and palette with area for easy retrival
SetLocalObject(oArea, SJ_VAR_TILEMAGIC_MATRIX, oMatrix);
SetLocalObject(oArea, SJ_VAR_TILEMAGIC_PALETTE, oPalette);
SetLocalFloat(oArea, SJ_VAR_TILEMAGIC_OFFSET, fOffset);
// get area dimensions
int nColumns = SJ_GetAreaWidth(oArea);
int nRows = SJ_GetAreaHeight(oArea);
int nRow, nColumn;
for(nRow = 0; nRow < nRows; nRow++)
{
// if using the TOPDOWN setting then start at the top row and work down
// otherwise start at the bottom row and work up as normal
int nIndex = (SJ_TILEMAGIC_TOPDOWN_MATRIX) ? (nRows - 1) - nRow : nRow;
// get row data for the current row,
string sRow = GetLocalString(oMatrix, IntToString(nIndex));
// skip row if row data null
if(sRow == "")
{
// NOTE: do not log an error as this is permitted
continue;
}
// if there is an entry for this row then
for(nColumn = 0; nColumn < nColumns; nColumn++)
{
// get the style code for the current column
string sStyle = GetSubString(sRow, nColumn, 1);
// skip if style is null or blank (space or user-defined)
if(sStyle != "" && sStyle != " " && sStyle != SJ_TILEMAGIC_STYLE_NONE)
{
// skip column if style is null
if(GetLocalString(oPalette, sStyle) == "")
{
// log an error but continue
string sDebug = "SJ_TileMagic_CoverMatrix: style (" + sStyle + ") did not exist in palette.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
continue;
}
// get style information
float fZ = GetLocalFloat(oPalette, sStyle + SJ_VAR_TILEMAGIC_Z);
int nTile = GetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_TILE);
int bNormalise = GetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_NORMALISE);
int nRotation = GetLocalInt(oPalette, sStyle + SJ_VAR_TILEMAGIC_ROTATION);
// NOTE: add matrix height (fOffset) to the style height (fZ)
SJ_TileMagic_CoverTile(oArea, nTile, nColumn, nRow, fOffset + fZ, bNormalise, nRotation);
}
}
}
}
void SJ_TileMagic_CoverRange(object oArea, int nTile, int nColumn=0, int nRow=0, int nColumns=1, int nRows=1, float fZ=0.0, int bNormalise=TRUE, int nRotation=0)
{
// sanity check
if(nColumns < 1 || nRows < 1)
{
// log an error
string sDebug = "SJ_TileMagic_CoverRange: failed as the number of columns (" + IntToString(nColumns) + ") or rows (" + IntToString(nRows) + ") was invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return;
}
// cover tiles in range
int nCurColumn, nCurRow;
for(nCurColumn = nColumn; nCurColumn < nColumn + nColumns; nCurColumn++)
{
for(nCurRow = nRow; nCurRow < nRow + nRows; nCurRow++)
{
SJ_TileMagic_CoverTile(oArea, nTile, nCurColumn, nCurRow, fZ, bNormalise, nRotation);
}
}
}
void SJ_TileMagic_CoverSpot(object oArea, int nTile, float fX, float fY, float fZ=0.0, int bNormalise=TRUE, float fRotation=0.0)
{
// use unique tag to ID tile node
string sTag = SJ_TileMagic_GetTagForSpot(oArea, fX, fY);
object oTile = GetObjectByTag(sTag);
// is location valid?
if(SJ_GetIsSpotValid(oArea, fX, fY))
{
// is there already tilemagic tile?
if(GetIsObjectValid(oTile))
{
// clear existing tile to minimise impact/issues
DestroyObject(oTile, SJ_TILEMAGIC_DELAY_CLEAR);
}
if(bNormalise)
{
// get normalised height as required
fZ = SJ_TileMagic_GetNormalisedHeight(nTile, fZ);
}
// generate location
location lTile = Location(oArea, Vector(fX, fY, fZ), fRotation);
// create tile
oTile = SJ_TileMagic_CreateTile(nTile, lTile, sTag);
}
else
{
// log an error
string sDebug = "SJ_TileMagic_CoverSpot: failed as target (" + SJ_TileMagic_TileToString(oArea, sTag, nTile) + ") was outwith area.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
void SJ_TileMagic_CoverTile(object oArea, int nTile, int nColumn, int nRow, float fZ=0.0, int bNormalise=TRUE, int nRotation=0)
{
// use unique tag to ID tile node
string sTag = SJ_TileMagic_GetTagForTile(oArea, nColumn, nRow);
object oTile = GetObjectByTag(sTag);
// is location valid?
if(SJ_GetIsTileValid(oArea, nColumn, nRow))
{
// is there already tile node?
if(GetIsObjectValid(oTile))
{
// remove existing tile node to minimise impact/issues
DestroyObject(oTile, SJ_TILEMAGIC_DELAY_CLEAR);
}
// calculate centre of actual tile
float fX = 10.0 * nColumn + 5.0;
float fY = 10.0 * nRow + 5.0;
if(bNormalise)
{
// get normalised height as required
fZ = SJ_TileMagic_GetNormalisedHeight(nTile, fZ);
}
// convert rotation from number of quarters to number of degrees
nRotation = (nRotation < 0) ? Random(4) * 90 : (nRotation % 4) * 90;
// generate location
location lTile = Location(oArea, Vector(fX, fY, fZ), IntToFloat(nRotation));
// create tile
oTile = SJ_TileMagic_CreateTile(nTile, lTile, sTag);
}
else
{
// log an error
string sDebug = "SJ_TileMagic_CoverTile: failed as target (" + SJ_TileMagic_TileToString(oArea, sTag, nTile) + ") was outwith area.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
}
}
object SJ_TileMagic_CreateMatrix(string sName, int bUse2DA=FALSE)
{
// sanity check: 2DA filenames are 16 chracters max
if(GetStringLength(sName) > 16)
{
// log an error
string sDebug = "SJ_TileMagic_CreateMatrix: resource name (" + sName + ") exceeded 16 characters and is invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return OBJECT_INVALID;
}
// check if datapoint already exists
if(SJ_GetIsDatapointValid(sName))
{
// log an error
string sDebug = "SJ_TileMagic_CreateMatrix: resource (" + sName + ") aready exists.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return OBJECT_INVALID;
}
// create a datapoint
object oMatrix = SJ_CreateDatapoint(sName);
// populate datapoint
if(bUse2DA)
{
int nIndex = 0;
// read each row data string in turn, interpret "" as EOF
string sRow = Get2DAString(sName, SJ_TILEMAGIC_2DA_ROW, nIndex);
while(sRow != "")
{
// add data to datapoint
SJ_TileMagic_AddRowToMatrix(oMatrix, nIndex, sRow);
// read next row
// NOTE: use pre-increment to avoid duplicating the first row
sRow = Get2DAString(sName, SJ_TILEMAGIC_2DA_ROW, ++nIndex);
}
}
// return datapoint
return oMatrix;
}
object SJ_TileMagic_CreatePalette(string sName, int bUse2DA=FALSE)
{
// sanity check: 2DA filenames are 16 chracters max
if(GetStringLength(sName) > 16)
{
// log an error
string sDebug = "SJ_TileMagic_CreatePalette: resource name (" + sName + ") exceeded 16 characters and is invalid.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return OBJECT_INVALID;
}
// check if datapoint already exists
if(SJ_GetIsDatapointValid(sName))
{
// log an error
string sDebug = "SJ_TileMagic_CreatePalette: resource (" + sName + ") aready exists.";
SJ_Debug(SJ_DEBUG_PREFIX_ERROR + sDebug, TRUE);
return OBJECT_INVALID;
}
// create datapoint
object oPalette = SJ_CreateDatapoint(sName);
// populate datapoint
if(bUse2DA)
{
int nIndex;
// read each style code and if valid add style information to palette,
// interpret "" as EOF
string sStyle = Get2DAString(sName, SJ_TILEMAGIC_2DA_STYLE, nIndex);
while(sStyle != "")
{
// read style information
int nTile = StringToInt(Get2DAString(sName, SJ_TILEMAGIC_2DA_TILE, nIndex));
int bNormalise = StringToInt(Get2DAString(sName, SJ_TILEMAGIC_2DA_NORMALISE, nIndex));
float fZ = StringToFloat(Get2DAString(sName, SJ_TILEMAGIC_2DA_Z, nIndex));
int nRotation = StringToInt(Get2DAString(sName, SJ_TILEMAGIC_2DA_ROTATION, nIndex));
// add data to datapoint
SJ_TileMagic_AddStyleToPalette(oPalette, sStyle, nTile, fZ, bNormalise, nRotation);
// get the next style code
// NOTE: use pre-increment to avoid duplicating the first style
sStyle = Get2DAString(sName, SJ_TILEMAGIC_2DA_STYLE, ++nIndex);
}
}
// return datapoint
return oPalette;
}
object SJ_TileMagic_CreateTile(int nTile, location lTile, string sTag)
{
object oTile = CreateObject(OBJECT_TYPE_PLACEABLE, SJ_RES_INVISIBLE_OBJECT, lTile, FALSE, sTag);
effect eTile = EffectVisualEffect(SJ_TileMagic_GetCorrectedConstant(nTile));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eTile, oTile);
// store the VFX so we can subsquently recover all of the tile's properties
SetLocalInt(oTile, SJ_VAR_TILEMAGIC_TILE, nTile);
// raise the plot flag to prevent destruction by AOE spells
SetPlotFlag(oTile, TRUE);
return oTile;
}
int SJ_TileMagic_GetAreaID(object oArea)
{
// check area for ID
int nID = GetLocalInt(oArea, SJ_VAR_TILEMAGIC_AREA_ID);
if(nID == 0)
{
// no ID: generate a new ID by incrementing last ID assigned, update the
// gobal value and assign to area for future use
nID = GetLocalInt(GetModule(), SJ_VAR_TILEMAGIC_AREA_ID) + 1;
SetLocalInt(GetModule(), SJ_VAR_TILEMAGIC_AREA_ID, nID);
SetLocalInt(oArea, SJ_VAR_TILEMAGIC_AREA_ID, nID);
}
// return area ID
return nID;
}
int SJ_TileMagic_GetCorrectedConstant(int nTile)
{
// correct values corresponding to duplicated 2DA entries
switch(nTile)
{
// parse nTile for special cases
case 348: nTile = SJ_TILEMAGIC_FEATURE_CITY_MARBLE_ARCH; break;
case 430: nTile = SJ_TILEMAGIC_FEATURE_ITHILLID_TRANSPORTER; break;
case 431: nTile = SJ_TILEMAGIC_FEATURE_UNDERDARK_WATERFALL; break;
case 441: nTile = SJ_TILEMAGIC_FEATURE_GRASS_ANT_HILL; break;
case 453: nTile = SJ_TILEMAGIC_FEATURE_CITY_MARBLE_ARCH; break;
case 456: nTile = SJ_TILEMAGIC_FEATURE_CITY_MARKET_1; break;
}
// return corrected tile constant
return nTile;
}
int SJ_TileMagic_GetColumn(object oObject)
{
// tiles are 10m wide
vector vObject = GetPosition(oObject);
return FloatToInt(vObject.x) / 10;
}
int SJ_TileMagic_GetLayer(object oObject)
{
// layers are 1m high
vector vObject = GetPosition(oObject);
int nRet = FloatToInt(vObject.z);
// lower limit is enforced at 0
return nRet = (nRet > 0) ? nRet : 0;
}
float SJ_TileMagic_GetNormalisedHeight(int nTile, float fZ)
{
// compensate for tiles with built-in non-zero heights
switch(nTile)
{
// parse nTile for special cases
case SJ_TILEMAGIC_ORDINARY_LAVA: fZ += 1.00; break;
case SJ_TILEMAGIC_ORDINARY_PIT: fZ += 2.40; break;
case SJ_TILEMAGIC_ORDINARY_WATER_GREEN: fZ += 2.00; break;
case SJ_TILEMAGIC_ORDINARY_WATER_BLUE_1: fZ += 1.00; break;
case SJ_TILEMAGIC_ORDINARY_WATER_BLUE_2: fZ += 1.00; break;
case SJ_TILEMAGIC_FEATURE_LAVA_FOUNTAIN: fZ += 1.00; break;
}
// return normalised height
return fZ;
}
int SJ_TileMagic_GetRow(object oObject)
{
// tiles are 10m high
vector vObject = GetPosition(oObject);
return FloatToInt(vObject.y) / 10;
}
string SJ_TileMagic_GetTagForSpot(object oArea, float fX, float fY)
{
// format: AAA_XXXXXxYYYYY
// fX and fY range from 0.00 to 319.99
string sTag = SJ_TILEMAGIC_PREFIX;
sTag += SJ_IntToPrePaddedString(SJ_TileMagic_GetAreaID(oArea), 3) + "_";
sTag += SJ_IntToPrePaddedString(FloatToInt(fX * 100), 5) + "x";
sTag += SJ_IntToPrePaddedString(FloatToInt(fY * 100), 5);
return sTag;
}
string SJ_TileMagic_GetTagForTile(object oArea, int nColumn, int nRow)
{
// format: AAAA_CCxRR
// nColumn and nRow range from 0 to 31
string sTag = SJ_TILEMAGIC_PREFIX;
sTag += SJ_IntToPrePaddedString(SJ_TileMagic_GetAreaID(oArea), 3) + "_";
sTag += SJ_IntToPrePaddedString(nColumn, 2) + "x";
sTag += SJ_IntToPrePaddedString(nRow, 2);
return sTag;
}
void SJ_TileMagic_SwitchMatrix(object oArea, object oMatrix, float fOffset=0.0)
{
// TODO: it may be possible to make a more efficient version from the point
// of view of comparing effects rather than creating and destroying objects
// however it is not worth pursuing at this time.
// get palette registered with area
object oPalette = GetLocalObject(oArea, SJ_VAR_TILEMAGIC_PALETTE);
// clear and recover
SJ_TileMagic_ClearMatrix(oArea);
SJ_TileMagic_CoverMatrix(oArea, oMatrix, oPalette, fOffset);
}
void SJ_TileMagic_SwitchPalette(object oArea, object oPalette)
{
// TODO: it may be possible to make a more efficient version from the point
// of view of comparing effects rather than creating and destroying objects
// however it is not worth pursuing at this time.
// get matrix registered with area
object oMatrix = GetLocalObject(oArea, SJ_VAR_TILEMAGIC_MATRIX);
float fOffset = GetLocalFloat(oArea, SJ_VAR_TILEMAGIC_OFFSET);
// clear and recover
SJ_TileMagic_ClearMatrix(oArea);
SJ_TileMagic_CoverMatrix(oArea, oMatrix, oPalette, fOffset);
}
string SJ_TileMagic_TileToString(object oArea, string sTag, int nTile=0)
{
string sRet;
// format = area name | tile tag | tile VFX constant
sRet += GetName(oArea) + " | " + SJ_RemoveSubString(sTag, SJ_TILEMAGIC_PREFIX);
if(nTile) sRet += " | " + IntToString(nTile);
// return description
return sRet;
}