confirm() it!

One piece of code you’ll see integrated into Evidyon in many places–and may strike you as odd–is confirm().  First, an example:

bool isEqualToZero(int* ptr) {
  confirm (ptr != NULL) else return false;
  return (*ptr) == 0;
}

This code acts like an assert with a conditional expression that can be attached to it when triggered.  If the pointer is null, an assertion is triggered when in debug mode.  When in release mode, however, the app doesn’t crash.  Instead, it returns false from the method.  So why not just assert(ptr != NULL)?

Consider this:  you have a giant program (Evidyon) with hundreds of essentially independent processes (NPCs, players) running in parallel.  There are many, many places a pointer can be null when it’s not supposed to be.  If an NPC finds itself outside of the map’s boundaries–which can happen for a number of reasons–then when it goes to look up its location the map manager will return NULL.  Should the entire server crash just because this one NPC went bonkers?  Probably not.  The programmer still wants to know what happened so the problem can be fixed, but keeping the game running is top priority.  The NPC can just be deleted or moved without causing any more problems.

In most cases, when an assertion fails there is some logical thing the program can do to fix or ignore the problem after reporting it.  This is why confirm() is so useful.

There is one more trick to using confirm().  Sometimes you want to make sure a variable does have a certain value, then do something.  This is done in the following example:

void setPointerValue(int* ptr, int value) {
  confirm (ptr != NULL) then {
    *ptr = value;
  }
}

The syntax makes it very clear that you want the pointer to not be null before entering the conditional block.  In this case, the code just doesn’t do anything if that condition isn’t met (other than logging an error).

Unlike assert(), confirm() also presents the user with the option to ignore errors on a given line any time they occur so that the program can continue.  For release mode, there is an option for confirm() that allows it to remain enabled, but always ignore errors–this makes any failed conditions show up in stdout and an attached debugger, but prevents prompting the user (since, for Evidyon’s server app, there is not usually someone sitting there waiting for a dialog to come up).

To check out the implementation for yourself, see ./common/fracture/confirm.h & .cpp in the released code.

Tags: , , , ,

Admin Console & Running an Internet Server

mcmcgoo asked:

Great game. I wanted to ask you if there was any way to create a custom character, level etc in the client admin version with the server running on my computer, or any tips or commands you have for it.  Also, is there a way to easily configure this version so I can connect to the server running on my computer on another computer over the internet? Thanks for your help!

Short answer to both: Yes!

The Administrator Console

To the first, there are various commands available via the admin console. However, to access them you’ll need to be logged into a admin character on the server. There is only one way to do this: you have to manually edit the server’s SQLite database.

Download the SQLite Database Browser and use it to open up the ‘evidyon.sqldb’ file. Go to the “browse data” tab and pick the “characters” table from the drop-down menu as below:

Characters table in the SQLite database browser

Next, pick the row containing the character you want to make an administrator. Scroll right to the ‘is_admin’ column and set the value to 1. Finally, save the database.

Of course, you could make any adjustments to characters that you want from here–changing levels, stats, etc.  However, this would require the server to restarted every time you want to adjust something.  Keep reading to find out how to do more via the admin console.

Now, when you log into this character you will notice that the character’s name and spoken text is green.  If this is so, the admin console will be functional.

Each instruction is used by entering “COMMAND > PARAMETERS.” into the admin console.  Below are some of the commands:

sql>QUERY

Executes an SQL query on the database to make modifications.

create item>item name

Gives your character a new (legit!) item of any kind–just type the name.

exit>

Closes the Evidyon client

backup>

Forces the server to run a backup routine immediately

turn off server>

Does exactly what you would think!

online>

Returns a list of all online accounts & characters

characters> account#

Displays a list of every character on the account number entered as the parameter.

Running an Internet-Accessible Server

This is fairly simple if you are not behind a router.  Go to no-ip.org and sign up for a domain.   Install the client on the same computer where you’ll be running the server.  This associates your IP address with some host name so that people can connect to “yourserver.no-ip.org” instead of an IP address.  It’s important to do this because non-dedicated IP addresses (anything at home!) change every so often, and it keeps people from having to constantly update their config file.

To allow people to connect, have them enter your host name (yourserver.no-ip.org) into the Server1= line of the config.ini client configuration file.

Now, if you are behind a router there is one more step:  forwarding packets to the computer where you are running the server.  Log into your router by typing in its IP address in your web browser (usually 192.168.0.1, but it varies by maker) and it will have a section where you can set up port forwarding.

You’ll need the IP address of the computer where you are running the server.  Usually there are connection logs in the router’s admin area where you can pick this information up.  If not, just open up a console window on the server computer, type “ipconfig” and read off the IP address.  It should be something like 192.168.0.4.

Back in the router admin port forwarding page, tell the router to send UDP packets on port 12601 to the IP address of the server computer.  Once that’s done, launch server.exe and you’re ready to go!

Tags: , , , ,

Resource Pre-processing

Describing every component of Evidyon’s design is going to take a while, so I’m starting a series where I’ll highlight major patterns that were the most useful during development.

After the first few iterations of the game, I realized that the client and server would be significantly easier to write if they were given uniform input data that matched perfectly what was actually used at runtime.  The server’s map would be big, flat arrays of structs that could be loaded straight from disk without any interpretation; the client could assume that all 3d models were in the format actually used to render the meshes.  This was the original inspiration for the game editor.  The purpose of the editor is to take a wide array of different media types, link them together, then compile them down into an optimized, consistent format.

This has several advantages:

  1. The client and server can be faster and more simple when loading, since they can make more assumptions about the format of their data
  2. It’s easier to introduce support for new formats
  3. The data can be optimized for their final purpose
  4. Debugging and resource validation can be extensive without impacting end-user performance (because it’s a compile-time operation)
  5. Resources can be organized in any way you like during development because they’re packed into a flat file for end use

Pre-processing resources was a big win for Evidyon and on a future resource-heavy project it would be one of the first things I would consider implementing.  However, in hind-sight I would have done things a bit differently on this project.

The editor really performs two phases that could be logically separated: converting resources to the uniform format and organizing them.  Both are preprocessing steps, but the former can be done before the editor is even started.  Having the editor perform the conversions from source format into the compiled format is a big source of the delay during compilation.  Most of the files don’t change between versions, and having all that translation code inside the editor itself does make it more complex than it needs to be.  Plus, this would prevent something that always annoyed me: if the very last mesh fails to compile, you have to fix the problem then run the editor again from the start in order to see if it works.

If I were doing this again, I would write separate command-line utilities for each of the resource formats to do the translations.  One of these is already implemented–the animated mesh converter.  However, I’d like to have a utility for images, static meshes, maps, etc. plus a single “compile everything in all subdirectories” utility.  This would make it a snap to add new content–just drop in a new folder somewhere in the resources directory, run the compilation utility and check for any issues.

I guess the summary of all this would be, “preprocess as early and often as you can.”  It’s a lot easier to deal with well-formed, valid data right from the start.

Tags: , , , ,

Client Application

This post describes the general structure of Evidyon’s client program.

The client program starts up in the WinMain method in “winmain.cpp” by creating a new instanceof the EvidyonClient class and invoking its execute() method.

This method runs the primary state-machine for the client that moves it between execution modes.  Each mode represents something like loading the game’s resources, waiting for input at the account-login menu or simulating the game world.  One interesting thing to note is that the state-machine object is built like a queue, so one can push states to be executed in sequence into the machine.  This is how the program first enters the VCS_STARTUP state (specified in EvidyonClient’s constructor), where it loads up the game’s resource file.  Each state has its own implementation file, so the code that gets run here is in statestartup.cpp.

Over in the stateStartup method, the client goes through loading each of its major components: graphics, input, network, system and resources.  They refer to the following definitions:

  • Graphics - creates a Win32 window for the game and initializes Direct3D
  • Input - Would have initialized DirectInput, but this isn’t used so the method is empty
  • Network - Initializes the WinSock library via ENet
  • System - Misc. initialization; now used to set up the admin console if it is enabled
  • Resources - loads the client’s game file.  This one is huge!

Each of these components is implemented in a file “acquire___.cpp” (in the project’s Initialization folder) where ___ is the component’s name.

After each of these has completed, the execution path proceeds back to the execute() method and the next state is run.  If the game successfully initialized, the client will attempt to connect to the remote server in the VCS_CONNECT state.  As is logical, this is implemented in the stateconnect.cpp file.

Once a connection is obtained, the connect state pushes the account-login state.  Unfortunately, stateaccountlogin.cpp is a mess.  This state is responsible for creating accounts, validating passwords and logging in.  From here, the user could go to the credits screen (VCS_CREDITS) be disconnected (VCS_CONNECT) or be successfully logged in (VCS_LOGGED_INTO_ACCOUNT).

After successfully logging in, stateLoggedIntoAccount() will display the list of characters specified by the server and allow the user to create a new one, delete an existing character or enter the world with a character.  Entering the world is where the good stuff is, and since this is a quick overview let’s just go to VCS_WORLD_MAIN (stateworldmain.cpp).

Entering the world is half handled by the logged-into-account state and the actual stateWorldMain method.  The former will try to be sure existing objects are removed and initialize them with new values contained in the reply from the server–for example, the current time, the character’s stats/appearance, attributes, and world location.  The latter sets up all the state-specific GUI components and other data.

There is so much state-specific data in the main world execution that, at some point, I pulled it all out into a StateWorldMainContext structure so that it wouldn’t all be allocated on the stack.  Look there for all of the game components.

The first section of the “main game loop” is a giant message-processor.  Since I started out with only a dozen or so messages and added them as I went along, having if…else if… else if… else if… didn’t seem like such a bad thing at the time.  As this grew I didn’t refactor and what you see here is the result.  Right around line 1000 is where the madness ends and the next section starts.  It’s a big switch() based on user actions that turns requests into network messages or results. Action::TYPE_MOVE, for example, is generated by the input subsystem whenever the user is trying to move their character around.  However, I didn’t like this way of doing things and tried to implement stuff in ActionProcessor for all of the newer actions.  Being on a huge time restriction, it wasn’t feasible to rewrite the old code to use ActionProcessor (even though it would have been much cleaner) but that’s where things were going.   At ~1350 you’ll see the ActionProcessor being updated.

// Handle all user events
context->action_queue.process(&context->action_processor);

The final section, guarded by allowFrameToBeRendered(), is the section that invokes all of the draw calls for the scene.  The following bit of code is so small it could easily be lost among the thousands of other lines, but it is what renders the entire 3d world:

// Render all of the objects in the world
size_t number_of_textures = renderstate_manager_.numberOfTextures();
for (Texture::TextureIndex texture = 0; texture < number_of_textures; ++texture) {
map_renderer_.render(texture, &renderstate_manager_, client_x, client_z, light_radius);
visual_fx_renderer_.render(texture);
scenery_renderer_.render(texture, &renderstate_manager_);
skinned_mesh_renderer_.render(texture, &renderstate_manager_);
}

This section goes through each texture in the game, in order, and renders the geometry that uses that texture.  I’ll write a full post on how this works later, but just know that it is implemented fairly efficiently–the textures are sorted in a specific way, unused ones are ignored, and so on.

From this main gameplay state, the program can be disconnected or return to the login page using the state machine.

That’s it for our whirlwind tour of the client!  Let me know what you’d like to see more of over on the EviTales Forums.

Distributing Visual Studio Applications

One of the early frustrations I ran into with Evidyon was the dreaded “Application configuration incorrect” error that occurred when the game was run on some peoples’ computers.

From what I remember finding in a search for a solution, this is caused when a program built with Visual Studio is distributed on another computer without the correct SxS (side-by-side) dynamic link libraries.  Long story short, there’s a direct way to solve this: make an installer program for your app.

It’s annoying in that I actually would prefer an app that just ran without having to be installed, but I banged my head against this forever and it was the easiest way to solve the problem.  Once you make your installer app in Visual Studio, be sure to right-click on the project, pick  “Add > Merge Module” then select the .msm files for the CRT (C Run-Time) 9.0 and its corresponding policy.  It should look something like the following:

Selecting the Visual Studio CRT Merge Modules to add to an installer project to prevent "Application Configuration Incorrect"

That’s it.  Compile the installer for your app, then install the program on whatever computer wasn’t working before.  Now when the program is run, it will have the correct versions of its libraries and the error won’t come up anymore.

Afterthought:  I’m not sure if this is a problem when you statically link the CRT. I had problems with conflicting symbols when static linking Evidyon to the CRT early on in the project so I stuck with dynamic linking ever since.

“Network Game Skeleton” or Evidyon, in 3500 lines of code

This is a project I wrote a long time ago (2007-ish) in the learning process of developing Evidyon. This project’s only prior release was … anticlimactic … but now that Evidyon has been released, maybe you all would be interested in something a bit easier to sink your teeth into.  Features include:

  • Fully-functional client
  • Fully-functional server (yes, it works over the internet)
  • Support for up to 16 players simultaneously (although this is only limited by a #define statement)
  • OPTIMIZED, ANIMATED .X MESHES!
  • DirectInput keyboard and mouse recognition
  • Camera control
  • Only ~3500 lines of code
  • Simple, funtion-oriented design
  • Precompiled demos in the /Bin/ directory

Three players online in the Network Game Skeleton

Check out the project on SourceForge.net

You might notice this code is written in my old style, so it looks much different from Evidyon’s main codebase.  If you look at the DirectCraft project, however, you’ll recognize it.

What is Direct Craft?

DirectCraft is a library that generates object serialization code and stores it in a way that can be easily saved/loaded and converted.  “dcx”, the DirectCraft Extensions library, wraps useful functionality into interfaces that are easy to use.

DirectCraft’s “resource” framework is used by the Evidyon game editor to store game files.  Using this library is a big reason that the editor was fairly straightforward to develop, even though it has a ton of different objects that all need to be serialized.  It gets really tedious to write serialization/loading code for every single member of every class–this was the primary inspiration for the DC library.

The definition of a WavSound is simply:

class WAVSound : public dc::dcResource<WAVSound> {
public:
static std::string staticTypeName() { return "WAVSound"; }
public:
WAVSound();
bool compile(Sound* sound);
void setSourceFile(const std::string& source_file);
private:
dc::dcFileName source_file_; // this is a DirectCraft resource type
};

With a single line of code in the constructor to define source_file_ as a member of the class, WAVSound is now a class that can be serialized and inflated without me having to write any of the code that actually does that.  While a serialization helper is not particularly unique, storing the whole hierarchy of all objects is extremely useful.

In the game editor, you’ll notice that every object has a context menu.  DirectCraft is also responsible for generating that menu; after defining a class in this way,  DirectCraft’s dcResource<> template class references a static global variable unique to this class, so WAVSound is forced to resolve an action list specific to its own type.  This is how the WAVSound can get a different action list from, say, the Map class–or any other class.

Finally, DirectCraft is also able to save references (pointers, basically) between classes.  Resources are all related hierarchically from one root resource, so there is a dcReference class that allows one class to contain a pointer to another class.  The pointer is automatically serialized just like any other DirectCraft object.

To check out how DC is used in practice, look at the editor’s CompleteEditor class (completeeditor.cpp & .h) and any file under the “/shared/…/tools/” folder.

Tags: , , ,

Media Resources

It’s hard to find good media online.  There is a lot of junk out there, but not much quality unless you’re rolling in dough–and even then, it’s hard to know if what you’re buying is worth it.  Here are a few things we used.

3dbud – Real-time 3D models (http://www.3dbud.com)

Good value on the models here–each one was good quality, and most simple models are $15-$20.  They also provided very fast support when one of my purchases (the dragon mesh) wasn’t encoded into the DirectX format quite right.  Sometimes they do 50% off sales.

3DRT – Buy 3D models (http://www.3drt.com)

These guys were great!  We bought the skeleton swarm pack from them.  We were planning on buying more but that one gave us the most value for how much we spent ($119).  They have had a 20-30% off sale each of the last two winters, so if you’re thinking of buying from them and want to save, that’s the time to do it.  They also have a free monster model available for download.

Basil Studio – GameArt (http://www.basilstudio.com)

This seems like one guy making models and selling them (the shop is hard to find since the website was changed, so here it is).  The models are decently usable, priced appropriately, and he provided some models like the lich model for free.

The artist “Kvakling” on TurboSquid

This user produced some pretty good armor and weapon models.  Specifically, we bought these: 1 2.  They look pretty spiffy, but Erich ended up re-making all of the armors because they were too hard to attach to the character models.  Also, unfortunately he more than doubled the price on these items since we bought them.

renier.de, now defunct (but it still shows up in our “content” path)

We got some tree and house models from this awesome German website by a guy who liked making models and giving them away for free.  The website renier.de is now nothing like it was, so it’s not even worth linking to anymore.

Dark GDK, a very old free release (unavailable; see thegamecreators.com for other good stuff from the same people)

The Game Creators gave away some really old low-poly content for free around Christmas time in the mid-2000′s.  The hound, bog monster and golem models are based on stuff in that pack.

The Problem of Infinite Cash in an MMORPG

The process:

  1. Kill monsters for loot
  2. Liquidate loot for money
  3. Repeat

This is the problem of inflation in an MMORPG.

  1. Currency is inflated, giving those with a lot of time to put into the game (or write bots) a huge advantage
  2. It’s hard to set a fixed price for merchants of items, since the currency’s value is constantly changing
  3. To counteract an item being overpowered because more money keeps piling into the system to purchase the item, a dev has to limit its availability (driving up the p2p price), nerf the item’s attributes or increase the purchase price to keep up with inflation.  These solutions result in items that are either so hard to obtain that they might as well not exist for the average player, or they’re mostly useless.
  4. It’s hard to set up a trading system where currency is more than just filler

The result of killing a monster is some form of material reward.  If it is easy to liquidate this reward and stockpile it, the above scenario is unavoidable.  It can be controlled somewhat with non-drop/non-trade items or having monsters only drop a limited amount of loot, but neither is a stable solution.  Furthermore, it was our opinion that having ND/NT items would break the fun of a game somewhat.  What sense does it make that you can’t put down your sword?  Is it glued to your hand or something?

Besides, as long as there is demand for money (which is the whole point of its existence) there is always some finite fraction of players who are willing to commit huge amounts of time to gaming the system by which it is acquired.  If you make loot low, they’ll just kill a massive number of monsters.  If you make items NDNT, they’ll trade in the non-NDNT ones.  If you don’t have any non-NDNT items, players can never trade with each other–which is a big part of an MMORPG.  If you tax the money they stockpile, they’ll spread it over as many accounts as necessary so as not to be penalized.  If you make durability a big enough factor to matter, they’ll be often annoyed when they get penalized; if it’s not that significant, it isn’t strong enough to soak up extra cash.  All of these extra rules make the game more confusing and, in my opinion, seem restrictive and less fun to play.

Wouldn’t it be nice if there were a way to solve this problem in an elegant, simple way?

Our solution is the Geonite/Bazaar system.  The key to solving the problem of infinite money was not to create artificial limitations, but instead to remove artificial extensions to the in-game economy–namely, the ease of liquidating items.

First, the main issue with having monsters drop lots of items was that these items all have to go somewhere.  Players generally horde the good stuff, but what constitutes “good stuff” depends on the player’s level.  Given that high level and low level characters coexist–and what is junk to one is a prized possession to the other–simply not offering money for goods of a certain type (or reducing the amount of money based on the seller’s avatar level) is not a solution.

For the sale of items, I implemented the Bazaar.  By this mechanism, players can trade items for gold with other players via listings, somewhat like the Amazon Marketplace.  This way, the currency-value is relatively stable (and accurate) since the price is based on the real supply and demand for the item.  The Bazaar skims some money off the top via a listing fee, so the system does actually “leak” money.  This process is also much slower than selling a bunch of junk to a NPC merchant, so players are implicitly encouraged only to list stuff that someone might actually want.

So what about the junk items that need to be eliminated?  There is something for this as well.  The main problem with easily liquidating items is not that the items are being converted into a uniform measurement of value, but that the value can be transferred.  When a player “sacrifices” items for Geonite, that player is the only one that is able to use the Geonite to perform actions.  In an early version of Evidyon, Geonite was a player attribute that allowed them to use special abilities or trigger world events.  In the current version, Geonite is used to trigger the special abilities of Geosids (the big crystals in the game).  This allows non-consumable items to be “consumed” by the system without resulting in inflation.

  1. Money becomes overabundant and worthless

A guide to Evidyon coding terminology

I came up with terms to describe distinctions between various components in Evidyon (usually layers of content) so that they could be succinctly well-defined.  Below is a reference to these terms.  They’re found throughout the code-base, and primarily in the “shared” folder.

Image – A raw data file that is a source of visual information.  Examples include .png, .bmp, .dds, and .jpg images, but this also referrs to auto-generated images such as those created by Perlin noise, a procedural algorithm or by mixing other images.

Texture - Defines a way of rendering an Image.  Multiple textures can use the same source image in different ways.  Types include static, sliding, circling and animated textures.  The water in Evidyon is a circling texture with alpha-blending attribute enabled.  The tree-leaves and grass have alpha-testing and alpha-blending enabled.  Particles render an Image using  a different blending mode so that they “blob” together into bright spots.

Mesh – A static collection of some number of groups of triangles for which each vertex is a GeometryVertex.  Evidyon uses .X files to represent Mesh objects.  Each group of triangles in a mesh is called a “subset”.  Triangles can only belong to one subset.  A mesh can be re-scaled and modified when it is loaded.

Scenery – Similarly to how a Texture defines the rendering of an Image, Scenery defines the rendering of a Mesh.  Its main purpose is to link each mesh subset with a Texture that is used to render that subset.

Animated Mesh – An animated mesh is a collection of geometry in the Unseen Skinned Mesh format [[more on this format some other time]].  It is a very straightforward way of representing geometry that has vertices with influence weights for a skeletal hierarchy of animated bones.  Like a Mesh, it has no texture information but it does have texture groups.  It is interesting to point out that animated meshes also lack the animations themselves.   This is intentional because it is incredibly useful–and something that isn’t available in any animated format I could easily use.  You’ll only find one set of animations for all the avatars, for example, because they all share exactly the same animations since the skeletons are the same.

Skeletal Animation – Defines keyframe animations for one or more sets of AnimatedMesh objects.  Each bone’s offset is in scale/rotate (quaternion)/translate form, and the full set of offsets on every bone is defined for every frame of the animation.

Skinned Mesh – Groups one or more Forms.  Each Form pairs an Animated Mesh with root scaling/transformation data and a set of skin Texture objects.  Also configures a set of Skeletal Animations that can be used for those forms.  Finally, sets the transform/source bone for pre-defined Attachment Points.

Attachment Point - Locations on a Skinned Mesh where objects with standard purposes (i.e. shield, helmet, boots, gloves, sword, off-hand weapon) can be attached.

Actor Profile – Brings a Skinned Mesh’s Form to life by pairing its animations with specific meanings in the game (e.g. play anim3 on death, play anim1 to attack) and matching sound-queues (got hit, die, attack) to sounds.  This also stores the set of animations used in each Combat Profile.

Combat Profile – Based on an Actor’s equipped items, a Combat Profile defining certain combat-specific animations is selected from the Actor Profile during game play.  For instance, an avatar wielding a 2H sword would use the “2H Weapon” Combat Profile.  Animation types defined in the profile include attack swings and combat stance.  This also defines attachment points for items when that profile is applied.  This is what puts the bow in the left hand for the bow animations, but the sword in the right-hand, and the pole-arm weapon in both.

Lifeform AI – Declares an implementation of an NPC.  A Lifeform AI references an Actor Profile to describe how it is to be displayed, but it also gives its visible name and game-mechanic attributes such as attack rates/damages, treasure, spawn effect, speed and visible equipment.

VisualFX – Definition of a simple particle effect, such as a fountain, explosion, or swirling ring.  Also generalizes to simple non-particle visual effects like dropping decals on the ground (e.g. footsteps when a character is walking) or rendering a Scenery instance.

SpecialFX – Combines one or more VisualFX with sound effects and positioning/animation data in a unique way to create the effect associated with some action.  This is used to create the look and sound for a spell.  SpecialFX can also have emitters that generate other special effects.  This type is also are used to represent the mouse-over-player effects and less obviously “special” effects like arrows and blood.

Event - An Event is an in-game instance of some SpecialFX description.  When the server does something, it generates some kind of  “create Event” notification, possibly with a unique ID, that is interpreted by each client.  Every Event has some lifetime, after which the client will automatically delete the event without notification from the server.  Some Event types, such as projectiles, can be explicitly addressed by their unique ID since their exact lifetime depends on whether or not they collide with something in the world.  Other events (most spell effects, for example) are never explicitly “terminated” by the server.

Zone – An area of the world characterized by a name and some attributes

Map – What contains all the data about the game world

Map Mask – Bitmap/PNG (lossless) image for which each color can be used to define something about the square of land at that map location.  Many masks are used to characterize a map.  For example, one mask can define where fluids (deep water, shallow water, acid pools, lava) are placed, another can define ground textures and yet another can define where to place scenery objects.

Map Layer – Used to construct the contents of a Map.  Usually, this is by associating a color in a Map Mask with some functionality, like placing a ground texture or wall, or changing the height of the land.  One frequently-used kind of Map Layer that doesn’t do this is the terrain-blending layer, which replaces ground texture tiles along the edges between two fill areas with a nice border.  This causes grass to blend nicely into dirt, for example.

Region – A 16×16 section of the map.  Only regions with players in them or regions next to regions with players in them are active at any given time.

Inhabitant – A Lifeform AI that is always present in the world. If they die or are deactivated (because nobody is nearby) the inhabitant is recreated when a player approaches.  Examples include merchants and guards.

Spawn – Lifeform AI instances that are created randomly within active Regions.

Navigability – Defines the movement attributes of a tile on the map.  Each type of navigability has its own characteristics.

Trigger – Something that causes an event.  Triggers are used to teleport players between maps when entering/exiting a dungeon; however, the framework exists for them to set off traps or trigger secrets.

Item – An object that can be owned by an avatar, traded, dropped on the ground (it has a visual representation) and used for some purpose.  Swords, shields, charms, potions and armor are all Items.

Treasure – Specification for how monsters drop randomly-generated items

Magic – Implementation of a kind of distinctive magical effect.  Direct-damage, area-of-effect (AOE), damage over time, portal, armor/speed enchantment, etc.  Also associates a Special FX with the casting of the Magic.

Spell – Wraps some number of Magics into the progression of a single Spell that can be used by the player.  Each stage, selected by the caster’s avatar level, the Magic changes (but usually remains the same type) as does that Magic’s associated Special FX.  Finally, this also gives an MP cost to the spell.  In this way, as a player levels up their spells start looking more and more powerful even though they don’t change their names.

Tags: , , , , , , , , , , , , , , , , , , , , , , , , , ,