12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106 |
- <P><PRE>
- **
- ** GCSx design outline
- ** Revision: August 16, 2006 Alexis Janson:
- ** Added @ to message sending syntax
- ** Added ability to access other object's variables
- ** Added 'as' operator to replace to*() functions
- ** Standalone function signifier changed from . to ::
- ** Changed scripts to require a name (no unnamed scripts)
- **
- </PRE></P>
- <H1>0) Introduction</H1>
- <P>
- The goal of this document is to describe all features and concepts of GCSx in plain english. This document will not contain data structures, exact language structure definitions, or other technical documentation. Please note that this means some features (especially language constructs) may be described in an incomplete or technically inaccurate manner.
- </P>
- <H1>1) Basic Game Structure</H1>
- <H2>1A) World</H2>
- <P>
- A world is usually the equivalent of a single game, although technically, a game could be created across multiple worlds or a world could contain multiple games. A world is a single file containing one or more scenes, that is loaded into the GCS to play. (or edit)
- </P>
- <H2>1B) Scene</H2>
- <P>
- Each world is divided into individual scenes. A scene typically represents a single level, board, map, stage, etc. Each scene has a unique name. Scenes can also be referred to using their internal numeric id.
- </P>
- <H2>1C) Layer</H2>
- <P>
- Each scene is divided into one or more layers. Layers are vertically-stacked (in the Z axis) and can be used to allow multiple "floors" or "levels" of gameplay, to visually stack things in game, or for visual effects such as parallax backgrounds. Each layer has a name unique to that scene. Layers can also be referred to using their internal numeric id or position within a scene.
- </P>
- <H2>1D) Sprites</H2>
- <P>
- Sprites are graphics that appear as free-floating entities. Sprites can appear above any layer, and are loosely associated with that layer. Sprites include graphical and positional data, as well as collision maps, animation, and movement information. Sprites are referred to using their unique numeric id, which is system-assigned. Ids are unique across an entire world. Sprites are commonly attached to objects (see 3A) and can in many ways be accessed as part of that object.
- </P><P>
- Sprites can also be "global" sprites that display on all scenes.
- </P>
- <H1>2) Layer Structure</H1>
- <P>
- Each layer has a size, and can be scrolled independently.
- </P>
- <H2>2A) Background</H2>
- <P>
- This is the (typically non-mobile) visual aspect of the layer. It can consist of a single image or of multiple tiled images. (tiled backgrounds can be of any resolution/size) Layers can also be made of a solid color, be invisible, or be constructed using a font and any amount of free-form text. (more details on layer types to follow) Very little game interaction involves the background, beyond representing basic walls and floors.
- </P>
- <H2>2B) Collision Mapping</H2>
- <P>
- This is data that specifies what parts of the background are solid and which parts are not, to represent walls, etc. Each collision map can be defined as one of 32 user-defined types. Some standard types include wall, player, enemy, bullet, etc. The game engine can be trained to look for or ignore different combinations of collisions, for optimized collision checking. Code can also detect what type of collision occured, allowing you to create special zones, types of walls, etc.
- </P><P>
- Collision mapping for a tiled layer is created on a tile-by-tile basis. Each tile can be either entirely solid, entirely empty, or point to a collision map. This collision map can only be of a single collision type.
- </P><P>
- Collision mapping for an image layer is created as one or more collision maps, placed and sized as desired.
- </P>
- <H1>3) Code</H1>
- <H2>3A) Objects</H2>
- <P>
- Objects are the meat of any game, as they contain the code that causes anything interesting to happen. Each object has a name, but it does not need to be unique. (object-to-object communication can have a one-to-many relationship) Objects can also be referred to using their internal numeric ids, which are unique across an entire world. (this allows object-to-object communication between scenes)
- </P><P>
- Objects are not required to have an on-screen representation. Objects with no representation obviously cannot interact with the map or sprites directly, but can still execute any other code.
- </P>
- <H2>3B) Sprite-Linked Objects</H2>
- <P>
- Objects don't technically have to be shown on-screen, but they are typically linked to sprites. Objects that are represented as sprites have the full range of movement that normal sprites do, and automatically handle collisions and other events related to the sprite. Sprites are also used to create objects that appear to be part of the tile-based map. Special functions and modes exist to limit these map-bound objects to the map grid, find objects within a specific tile, etc. but these objects are still just sprite-linked objects with special properties.
- </P><P>
- Most objects in a typical game will be linked to sprites and vice-versa.
- </P>
- <H2>3C) Global Objects</H2>
- <P>
- Objects are normally specific to one scene, but can also be global. Objects that are global (not scene-specific) are present on all scenes of the world, running at all times. Note that normal objects can also move between scenes. Global objects can be linked to global sprites.
- </P>
- <H2>3D) Scripts</H2>
- <P>
- Each world contains a set of scripts. Each script represents instructions to control an object. Each script must be named, and each object links to one (and only one) script. However, a single script can be linked to by more than one object, or no objects at all. Objects can dynamically load a new script at any time. Scripts are not scene-specific in any way. Scripts must have unique names.
- </P><P>
- Multiple objects act independantly, even if sharing the same script. For example, local variables and control flow operate independant to each object.
- </P><P>
- Libraries of code with names can be created separate from worlds, and the GCS includes a number of standard libraries to create basic, standard, or sample objects. To prevent name conflicts, libraries may be accessed using a prefix before each name.
- </P><P>
- Code is stored in two formats- source and compiled. The source code is an exact copy of the original text entered by the programmer. The compiled format is actually a bytecode, made up of basic individual instructions. The compiled format loses all comments, variable names, label and function names, and other things that make it readable, so decompilation will result in difficult to read code. (this is a byproduct, and not intended as a security feature)
- </P>
- <H2>3E) Function Library</H2>
- <P>
- Functions can be written as part of a script, but can also be created as standalone entities. These functions are stored in a function library. These functions can be used by any object, and are for all intents and purposes, the same as a built-in function or a function within the object's script.
- </P><P>
- Function libraries can be created separate from worlds, and the GCS includes a number of standard function libraries to handle many specialized effects and game types. To prevent name conflicts, libraries can be accessed using a prefix before each name.
- </P>
- <H1>4) (unused section)</H1>
- <H1>5) Resources</H1>
- <H2>5A) Image/Tile Sets</H2>
- <P>
- An image set consists of one or more graphics, all the same size. Typical sizes include 16x16, 32x32, and 64x64 for image sets intended for tiled maps, or larger and varied sizes for sprite usage. Note that sizes do not need to be square. Image sets are full-color, and include an alpha channel, for partial transparency. Image sets are used to create tile-based layers and as sprite images. (note that multiple images can be combined to form a single sprite) Image sets have a unique name as an identifier, as well as a unique internal numeric id.
- </P><P>
- When images are used as part of a layer or sprite, they are given a color as well. This color is applied to the image via a multiply effect. (A mode where the color is instead used to replace a specific color or range, or other color-application methods, may be added.)
- </P><P>
- Special image sets for fonts can be created (otherwise referred to just as "fonts") and are exactly like normal image sets, except they store a horizontal cut-off position for each image, and the images are stored in a predefined alphabetical order. The horizontal position is used when writing in the font, to determine where the next character begins. This allows non-fixed-width fonts. Kerning and other specialized font-related functionality is not supported.
- </P>
- <H2>5B) Collision Maps</H2>
- <P>
- Collision maps are very similar to a two-color image in definition. When tiles are placed in a layer, each tile is also assigned a collision map to determine where collisions will be detected. (Two default maps always exist- "entirely solid" and "empty" maps) Note that the same tile can be assigned different collision maps each time it is used- the two are independant.
- </P><P>
- Collision maps can be quickly created using an image set. Images get maps corresponding to the alpha channel of the image. Alternatively, maps can be created from a color range. Default editor functionality can create collision maps based on the images and automatically assigns each image's map wherever you place that tile, so that beginners do not have to worry about collision maps.
- </P><P>
- Collision maps can be defined as one of 32 user-defined types. Some standard types include wall, player, enemy, bullet, etc. The game engine can be trained to look for or ignore different combinations of collisions, for optimized collision checking. Code can also detect what type of collision occured, allowing you to create special zones, types of walls, etc.
- </P><P>
- Sprites are also assigned collision maps, which can be pieced together from the image's collision maps, be created on-the-fly based on the above creation rules, or created custom. The default functionality is for all sprites to simply create their own collision maps. The entire collision map for a sprite must be of a single collision type. If you must have multiple types of collisions for a single entity, you can link multiple sprites together to move in tandem. A sprite also selects which type of collision(s) it wishes to detect. For example, one sprite may be set to detect collisions with walls and enemies, but not with items or bullets.
- </P><P>
- Each image set contains collision maps, each assigned a unique internal numeric id (keep in mind that collision maps for tiles are still selected separate from images for tiles) and each sprite or sprite image has its own collision map as well. Sprite collision maps do not have a special id, being referenced by the sprite instead.
- </P>
- <H2>5C) Sprite Images and Animations</H2>
- <P>
- Sprite images are always built from one or more images from an image set. These images can be from different libraries. Each world can also contain one or more sprite libraries, consisting of predefined sprite images, prebuilt from one or more image sets. Note that the images do not have to follow a grid pattern. The collision data is also prebuilt. This allows you to put together sprites from many images, and simply call them up using a single identifier. Sprite libraries each have a unique name, as well as a unique internal numeric id. Sprite images within each library also have names and internal numeric ids that are unique within that library.
- </P><P>
- Sprite libraries actually contain animation scripts- a single image in a sprite library is simply a one-frame animation.
- An animation script details which frames to play in sequence, and can also include movement commands and delays between frames, as well as looping instructions.
- </P><P>
- Note that the actual, current image for a sprite is stored as its own bitmap, and can be directly drawn upon or modified as a single unit, or created using a font and text. (note that this tends to be slower than using predefined images.) A library of predefined sprite animations, however, pulls from image sets only. You can still create single-piece sprites quite easily, by using an image set that's the same size as your sprites will be. You can directly assign these images to sprites, or create an animation group using them.
- </P><P>
- Sprites handle transparency in the same way as image sets, using alpha channels. Sprites are also given a color, which is applied in a similar manner as to tile images.
- </P>
- <H2>5D) Variables</H2>
- <P>
- Variables are used to store individual values of the programmer's choice. A variable can contain an integer number, a floating-point number, or a string. Variables can also be arrays (lists) of values or associative arrays (hashes) of values. An associative array takes a string as an index, and looks up a value. Arrays and hashes are one-dimensional only. Arrays and hashes can store either integers, floating-points, or strings. (not a combination, and not further arrays or hashes) Note that string concatenation with hash indices can simulate any combination of nested arrays and hashes, if needed.
- </P><P>
- Variables can be defined as "variants" that can contain any type of value, but the usage of these variables is more limited in scope. (they cannot be used in normal operations without specifying a datatype, or they must be assigned to normal variables before usage)
- </P><P>
- Variables can be local, specific to a single object; or global, accessible to all objects in all scenes. Local variables can be accessed by other objects. Variables can also be scoped- that is, local to a single function or code block, only accessible from within that code. These variables (which include function parameters) can NOT be accessed by outside objects.
- </P>
- <H2>5E) Music and Sound Effects</H2>
- <P>
- (...todo...)
- Predefined music library, each item contains filename, tempo, volume, starting position, and can store music file with game; Sound library contains filename, volume, start and end positions, loop setting, center note, and possibly effects like echo/reverb;
- </P>
- <H1>6) Syncronization and Timing</H1>
- <H2>6A) Object Concurrency</H2>
- <P>
- All object code runs "concurrently", via time-slices. A single frame of the game is often referred to as a "cycle". For example, a certain amount of code of one object will execute, then another object, until all objects have had a turn, then a short delay occurs and this process begins anew. An object's time-slice normally ends when it yields control via delaying, idling, polling for events, or otherwise doing nothing. This is under the control of the programmer, allowing you to easily keep all objects synchronized by use of idling or polling commands. Object time-slices can also be temporarily (voluntarily) pre-empted by sending messages, but the object will normally get to continue its code during the same cycle. Objects sent messages will get to run outside of their normal time-slice order, until they finish the message or yield control; these objects will also get their normal time-slice as well, whether it occurs before or after the message was handled.
- </P><P>
- More specifics on how messages and concurrency interact-
- </P><P><UL>
- <LI>An object that sends a message without expecting a reply will pause execution while the recipient processes the message.
- <UL><LI>If the recipient finishes the message within the current time-slice, the original sender resumes* immediately.
- <LI>If the recipient does not finish processing the message, the original sender resumes* (unless sender and recipient were the same) and the recipient will continue to process the message during its own time-slices. The recipient will lose any pending time-slice for the current cycle.
- <LI>If there are multiple recipients, each one gets a chance, in sequence, at the above two options, before the original sender resumes. If one of those multiple recipients is the sender itself, it will always be last in sequence.
- </UL>
- <LI>An object that sends a message expecting a reply will pause execution while the recipient processes the message.
- <UL><LI>If the recipient replies within the current time-slice, the original sender resumes* immediately (unless sender and recipient were the same) with the reply. The recipient will finish processing any code placed after the reply during its own time-slice. (again, unless sender and recipient were the same)
- <LI>If the recipient does not finish processing the message, the original sender waits, and the recipient will continue to process the message during its own time-slices. The recipient will lose any pending time-slice for the current cycle.
- <LI>When the recipient replies during a later time-slice, the recipient will continue its time-slice until complete. The original sender will resume during its normal time-slice for the current cycle.
- <LI>Objects that are awaiting replies are moved to the end of the time-slice ordering until they receive a reply. This means that an object that recieves a reply will always get a chance to act upon that reply in the same time-slice, except for some situations where a reply was the result of another reply.
- <LI>A message-with-reply cannot involve multiple recipients.
- </UL>
- <LI>A recipient that has queueing enabled but is not currently executing code will act exactly as described in the previous two points.
- <LI>A recipient that has queueing enabled and is within executing code will act as described in the previous two points starting at the "does not finish processing" branch.
- <LI>If the recipient is the same as the sender, the only exceptions are listed in the original two points. All other situations have the same results as with different sender and recipient. Any optimizations for this situation will be done in a way that ensures this behavior.
- <LI>*All immediate resumes marked with asterisks assume that, during the course of processing the message, the original sender is not sent its own message to process. An immediate resume is aborted (and the sender ends its time-slice) if the code pointer is not in the exact location it was when the message was sent. If the sender did process a message, but completed processing and has returned to its previous code location, then the resume will take effect normally.
- <LI>"Not finish processing" can be due to an intentional delay, idle, or sync request, or preemption due to excessive processing. Use of 'end' or 'main' count as finishing, in addition to 'return', 'reply', or simply ending the associated function.
- </UL></P><P>
- If too much code is run during one time-slice, it may be preempted. This normally shouldn't occur, but could happen with large amounts of calculations or other processing. Preemption is based on number of opcodes processed- not time elapsed.
- </P><P>
- Objects can also contain sync() functions, which require that a certain number of objects reach the same sync() function before continuing. This lets you ensure total syncronization for code that may have a variable run time. Other built-in functions and language features can assist in syncronization-related tasks as well.
- </P><P>
- Delays and timing may be done in terms of game cycles, or in milliseconds. Game cycles normally run 60 per second, unless the computer cannot keep up. Timing is absolute, and a 1000 millisecond delay will always cause code to halt and resume in as close to 1 second as possible based on outside influences. Even these delays, however, have a granularity of 1/60th of a second- in otherwords, they will always round to the nearest game cycle. This prevents unnecessarily complicated timing code and situations. The ability to change the length of a game cycle exists using fps()- this will also affect timing granularity.
- </P><P>
- During each cycle, by default, all objects are run in the order of their internal numeric ids (i.e. the order of creation). This can be modified using user-assigned priorities. Objects awaiting replies are always moved to the end of the cycle ordering. More details and related language constructs and editor functionality are to be determined.
- </P>
- <H2>6B) Concurrent Scenes</H2>
- <P>
- Under normal circumstances, only the currently active scene is processed- any scene-specific objects on other scenes are not run. However, any number of non-active scenes can be run in the background. Nothing will be displayed, but all objects and sprites will continue to run. This feature must be used carefully, due to the extra processing power and high error potential. Global objects will still only be run once, and only be able to interact with the current scene under normal circumstances. (language constructs exist to specifically interact with other scenes)
- </P>
- <H2>6C) Frame Sequence</H2>
- <P>
- A single game frame is processed in the following order-
- </P><P>
- a) Pending input from user is processed.<BR>
- b) Automatic and queued movements and animations occur.<BR>
- c) Any collisions from b) are triggered.<BR>
- d) All objects run, in sequence. Most object movements are queued until b) next frame. An object can choose to move immediately- if done, any resulting collisions trigger and process immediately.<BR>
- e) Screen is updated. If fps() is faster than the video system, updates may be skipped. If fps() is slower, updates may be interpolated.<BR>
- f) Delay occurs as needed, to create desired fps().
- </P><P>
- Music and sound processing is handled syncronously within its own thread.
- </P>
- <H1>7) Default Language Structure</H1>
- <P>
- The following describes the overall structure and format of the default script language. As the language is compiled into an simple, internal bytecode, other languages could conceivably be written and supported.
- </P><P>
- This language structure is still under development and may change.
- </P>
- <H2>7A) Statements</H2>
- <P>
- Each line of code is a statement. Statements do not need any termination other than end-of-line. An optional semi-colon ; may terminate a statement. If you wish to have multiple statements on a single line, you can use semi-colons to separate them. A statement may be a built-in language construct (using a keyword) or a call to a library or user-defined function, or some mixture.
- </P>
- <H2>7B) Keywords</H2>
- <P>
- The following keywords have special meanings or are reserved.
- </P><P><PRE>and
- array
- break
- by
- case
- const
- continue
- default
- do
- dynamic
- else
- end
- etc
- false
- float
- for
- foreach
- from
- global
- goto
- hash
- if
- import
- in
- int
- is
- local
- query
- main
- namespace
- not
- null
- or
- private
- public
- real
- repeat
- reply
- restrict
- return
- send
- state
- static
- str
- switch
- this
- to
- true
- until
- using
- var
- void
- while
- with
- xor</PRE></P>
- <P>
- Keywords are NOT case-sensitive, and are used to perform various built-in language functionality or reserved for future functionality.
- </P>
- <H2>7C) Code Blocks</H2>
- <P>
- Code blocks consist of zero or more statements, enclosed between curly braces { and }. Anywhere a single statement may be used, a block of code may be used instead. { and } have an implied end-of-line before and after them, so you may use { and } on the same actual line with a statement, or even to separate multiple statements on one line, similar to semi-colons.
- </P>
- <H2>7D) Data Types</H2>
- <P>
- All values in a script must be an integer number, a floating-point number (decimal), a string, or an array or hash containing one of those types. All expressions evaluate to one of these types, all functions return one of these types, and all variables contain one of these types. Integers and floating-points are stored and treated differently, although they are often interchangable. Any text that refers to an "undefined value" means that there is still a distinct value, but it is unpredictable and could be any value of the proper type.
- </P><P>
- Integers are stored as signed values using at least 32 bits, although this is subject to increase. Integer overflow results in an undefined value.
- </P><P>
- Floating-point numbers are stored as signed values with a currently undefined precision. Decimal overflow results in an undefined value. Decimal underflow is rounded off.
- </P><P>
- Strings are stored as a series of 8-bit extended ASCII characters. Unicode is not supported. Any characters can be stored, except for NUL. (ASCII 0) String length may be limited, but at least 32767 characters of storage will be allowed. Strings that exceed the maximum length, if one is used, are truncated.
- </P><P>
- Boolean values (true and false) are represented as integers. An integer value of 0 represents false; an integer value of 1 represents true. Note, however, that any value will be considered true except for the numbers 0 or 0.0. (this includes any string, array, or hash, even if empty)
- </P><P>
- Integers convert to floating-point values and vice-versa automatically. Decimals will be truncated when converting to integers. If a floating-point value is too large to convert to an integer, an undefined value will result. Both numeric values convert to strings automatically. No other automatic conversions are performed.
- </P>
- <H2>7E) Literals</H2>
- <P>
- Samples:
- </P><P><PRE>3.14159
- +52
- -17.42
- 0xFFE0
- "test"
- "Line 1\n\rLine 2"
- [ 1, 2, 3 ]
- [ "A", "B", "C", "D" ]</PRE></P>
- <P>
- A literal is a fixed value. Numeric literals are written using the digits 0123456789 with optional decimal point . and positive + or negative - signs. You may also write numeric literals in base 16 by starting with the characers 0x followed by hexadecimal digits 0123456789ABCDEF. (case insensitive) No decimal or sign is allowed on base 16 literals.
- </P><P>
- String literals are written using double quotes " on each side. A string may contain any number of characters. You may not directly include carriage returns, newlines, double quotes, or backslashes. To include these characters, use \r, \n, \", and \\, respectively. You may not include NUL (ASCII 0) or characters with an ASCII value above 255.
- </P><P>
- Array literals are written using square brackets [ and ]. Separate multiple array elements with commas.
- </P><P>
- Associative arrays (hashes) cannot be written as literals.
- </P>
- <H2>7F) Identifiers</H2>
- <P>
- Samples:
- </P><P><PRE>Zebra
- R2000
- OBJECT_ONE</PRE></P>
- <P>
- Identifiers are used to name and represent variables, constants, functions, script names, libraries, labels, or states. Identifiers must begin with a letter (A through Z) or underscore _, followed by any sequence of letters, underscores, or digits (0-9). Identifiers can be between 1 and 64 characters in length. (this limit may change or be removed based on internal structure design yet to come) Identifiers are NOT case-sensitive. Identifiers may not have the same name as a keyword.
- </P>
- <H2>7G) Variables, Constants</H2>
- <P>
- Sample declarations:
- </P><P><PRE>float Position
- local str Name
- str array Codes = [ "A", "B", "C" ]
- str hash Colors
- global int Gems = 2 * 2
- global const str Name = "Bob"
- var Something</PRE></P>
- <P>
- Sample usage:
- </P><P><PRE>VarName
- $VarName</PRE></P>
- <P>
- Variables and constants can be local or global. A local variable can be accessed by outside objects, but local constants can only be accessed by the object where they are defined. A global variable or constant is accessible by any object. You cannot have more than one variable or constant with the same name at the same level. If you have the same name variable or constant as both local and global, the local one is used when applicable.
- </P><P>
- Variables and constants must be declared before their use. This is done using declaration statements. A declaration consists of a scope, followed by a type, followed by an identifier. Variable scope must be either 'local' or 'global'. If scope is not specified, local scope is used. Variable type must be either 'var', 'float', 'int', or 'str'. Variable type (except for 'var' variables) may also include either 'hash', 'array', or 'const' as a modifier. (The modifer may be placed either before or after the type.) The identifier is used as the variable or constant name.
- </P><P>
- The declaration can then be followed by an equal sign and a literal value or expression, to assign a starting value. Constants must have this assignment and it must be a literal value. (not an expression) Note that constants cannot be arrays or hashes, and cannot be variants.
- </P><P>
- All variables and constants must be declared before using. Any variable without a starting value will be set to 0, 0.0, "", or an empty hash or array, as appropriate. Global variable and constant declarations may appear in any script, but must appear in every script that uses them, before they are used. (the type must remain the same for all uses, as well as the value for constants) The only exceptions to these rules are covered under function syntax.
- </P><P>
- To actually use a variable or constant, simply use its identifier. (name) Attempting to refer to a non-existant variable will result in a compilation error. Optionally, when referring to a variable, you may prefix the name with a dollar sign $. You may also use the dollar sign when declaring variables, although it is entirely redundant. The dollar sign allows you to clarify when you intend an identifier to represent a variable, instead of something else.
- </P><P>
- Variables of 'var' type are "variants", and can contain any type of value, defaulting to an integer 0 as a starting value. Variables of type 'var' can only be assigned values, assigned to other variables, or passed as parameters to functions. You may not use a 'var' variable in an expression unless you cast it to another datatype first. Assigning a 'var' variable to a normal variable follows the conversion rules used for calling functions using . periods. Attempting to use a 'var' variable within an expression, without casting to a datatype first, will cause a compile-time error.
- </P>
- <H2>7H) Expressions</H2>
- <P>
- Samples:
- </P><P><PRE>A + B
- (X * 8 + Y) * 40</PRE></P>
- <P>
- An expression is simply anything that, during gameplay, will evaluate to a value the script can use. An expression can be as simple as a literal or a variable identifier, or a complex series of operations.
- </P><P>
- Any expression (other than a simple literal or identifier) is constructed using operators. Most operators work on two expressions, to create one value. For example, 1 + 2 is an expression that takes two other expressions (1 and 2) and applys the + operator to get a value. Expressions can be chained or nested in any sequence.
- </P><P>
- The following operators work on two expressions: # + - / * % & | << >> < > = == <= >= <> and or = is as .<BR>
- The following operators work on one expression: + - ~ not
- </P><P>
- Expressions evaluate using operator precedence. Operator precedence can be overridden using parenthesis ( and ). All operators at the first precedence level evaluate left to right, then the next level, and so on.
- </P><P>
- Precedence 1: Parenthesis ::
- Precedence 2: . @ as
- Precedence 3: Functions Subscripts<BR>
- Precedence 4: + - ~ not (all unary)<BR>
- Precedence 5: * / %<BR>
- Precedence 6: + -<BR>
- Precedence 7: << >><BR>
- Precedence 8: & | ^<BR>
- Precedence 9: #<BR>
- Precedence 10: < > <= >=<BR>
- Precedence 11: == <> != is<BR>
- Precedence 12: and or
- Precedence 13: =
- </P>
- <H3>7H1) Operators + - (Unary)</H2>
- <P>
- When - applies to a single expression, it returns the negative value of that expression. + returns the value unchanged. + and - can only be used on numeric values. Use on other values will result in a compilation error.
- </P>
- <H3>7H2) Operator ~ (Unary)</H3>
- <P>
- ~ returns the binary negation of an expression. ~ can only be used on integer values. Floating-point values will be converted to integers first. Use on other values will result in a compilation error. Note that the number of bits available is not currently defined, although it is guaranteed to be at least 32.
- </P>
- <H3>7H3) Operator not (Unary)</H3>
- <P>
- 'not' returns the logical negation of an expression. 'not' can be used on any type of value, but will always return either true or false.
- </P>
- <H3>7H4) Operators * / + -</H3>
- <P>
- These four operators perform basic mathematical functions between two expressions- multiplication, division, addition, and subtraction. These may only be used on numeric values. If either expression is a non-numeric value, this will cause a compilation error. Dividing by zero will result in an undefined value.
- </P><P>
- If either expression is a floating-point value, the result will be a floating-point value. If both expressions are integer values, the result will be an integer value. If you wish to get a floating-point result from dividing two integers, you must first convert one to floating-point.
- </P>
- <H3>7H5) Operator %</H3>
- <P>
- % returns the modulo of two expressions, which is the remainder after an integer division. Floating-point expressions will be converted to integers before calculating. If either expression is a non-numeric value, this will cause a compilation error. Also, modulo by zero will reutrn an undefined value.
- </P>
- <H3>7H6) Operators << >></H3>
- <P>
- These two operators perform bitwise logical shifts on an expression. The first expression is shifted a number of bits left or right equal to the second expression. Floating-point expressions are converted to integers before calculating. If either expression is a non-numeric value, you will get a compilation error. Shifting a negative number of bits causes a shift in the opposite direction.
- </P><P>
- Note that shifting left is equivalent to multiplying by 2, except that bits shifted past the end of the integer are lost and the sign will shift based on the final bit. Shifting right is equivalent to dividing by 2, rounded down, except that negative values will become positive. (Bits shifted past the end are lost)
- <P>
- <H3>7H7) Operators & | ^</H3>
- <P>
- & performs a bitwise 'and' operation, | performs a bitwise 'or' operation, and ^ performs a bitwise 'xor' operation. Floating-point expressions will be converted to integers before calculating. If either expression is a non-numeric value, you will get a compilation error.
- </P>
- <H3>7H8) Operator #</H3>
- <P>
- # concatenates two strings. Numeric values are converted to strings before concatenating. If either expression is a hash or array, you will get a compilation error.
- </P>
- <H3>7H9) Operators < > <= >= == <> !=</H3>
- <P>
- These six operators perform logical comparisons. < returns true if the first expression is less than the second. > returns true if the first expression is greater than the second. == returns true if the first expression is exactly equal to the second. <= returns true if the first expression is either less than or equal to the second. >= returns true if the first expression is either greater than or equal to the second. <> and != return true if the first expression is not equal to the second.
- </P><P>
- Note that <> and != are synonyms.
- </P><P>
- When used on two numeric values, a numeric comparison is performed. When used on two string values, an alphabetical comparison is performed. This comparison is NOT case-sensitive, but it is an ASCII-based sort, not a true sort. If used on a string value and a numeric value, the numeric value is first converted to a string.
- </P><P>
- If either expression is a hash or array, a compilation error will result.
- </P>
- <H3>7H10) Operator is</H3>
- <P>
- 'is' determines if two values are of the same type, defined as one of the types 'int', 'float', or 'str' plus optionally 'hash' or 'array'. (for nine possible permutations) Local/global/const are not taken into account. If both values have the same type, 'is' returns true, or false otherwise. For 'var' variables, the actual type contained within is used.
- </P><P>
- The second operand to 'is' may be 'int', 'float', 'str', 'hash', 'array', or a legal combination thereof.
- </P>
- <H3>7H11) Operators and or</H3>
- <P>
- 'and' performs a logical 'and' operation, which returns true if both expressions are true, or false otherwise. 'or' performs a logical 'or' operation, which returns false if both expressions are false, or true otherwise.
- </P>
- <H3>7H12) Operator =</H3>
- <P>
- = performs a normal variable assignment. The left operand must be a variable or subscript into a variable. (something you can assign to) The 'result' of the operator is the same variable, so you can chain assignments or assign-and-test in one expression.
- </P>
- <H3>7H13) Operators . @</H3>
- <P>
- . is used for sending messages to objects and accessing other objects' variables. (covered in other sections) @ is also used as an advanced part of sending messages.
- </P>
- <H3>7H14) Operator as</H3>
- <P>
- 'as' converts a value to another type. The first operand to 'as' can be any value, and the second must be a datatype such as 'int' or 'str hash'. The value is converted to or treated as the listed type. This will never result in an error- a conversion that cannot be performed will result in a 0, 0.0, "", or blank hash or array value.
- </P>
- <H2>7I) Subscripts</H2>
- <P>
- Samples:
- </P><P><PRE>Party[0]
- Colors["Red"]
- [ "Mon", "Tue", "Wed" ][DayOfWeek]
- "ABC"[1]</PRE></P>
- <P>
- When you refer to an array or hash variable by name, normally it refers to the entire array or hash. This is typically useless, as most operations cannot work on an entire array or hash.
- </P><P>
- To retrieve a single value from an array, follow the array name with a numeric index value enclosed in square brackets [ and ]. Arrays use zero-based numbering. (i.e., the first element has an index value of zero) Floating-point values will be converted to integers before being used as an index. Non-numeric index values will result in a compilation error. Negative index values or attempting to retrieve a value that has not been yet set will return 0, 0.0, or "", depending on the array type.
- </P><P>
- To retrieve a single value from a hash, follow the hash name with the lookup value enclosed in square brackets. All lookup values are converted to strings before being used. Non-numeric, non-string index values will result in a compilation error. Attempting to retrieve a value that has not yet been set will return 0, 0.0, or "", depending on the hash type.
- </P><P>
- To retrieve a single character from a string, follow the string with the character position enclosed in square brackets. Characters are returned as integer ASCII values from 1 to 255. Strings use zero-based numbering. (i.e., the first character has a position of zero) Non-numeric index values will result in a compilation error. A negative index value or attempting to retrieve a character past the end of the string will return 0.
- </P><P>
- Subscripts can be used on expressions that return arrays or strings, or array or string literals, as well. Attempting to subscript a non-array, non-hash, non-string value will result in a compilation error.
- </P>
- <H2>7J) Assignment</H2>
- <P>
- Samples:
- </P><P><PRE>
- Color = "Red"
- $MyArray = [ 1, 2, 3 ]
- MyArray[0] = 5
- MyString[0] = "A"[0]
- Items["Hand"] = "Sword"
- a = b = c = 5</PRE></P>
- <P>
- Assignment statements are used to store values into variables. Assigments have the name of a variable, followed by an equal sign =, then the value or expression to store. You can also place a valid subscript on the left side of the equal sign. It is also legal to use the dollar sign syntax to refer to a variable here. Attempting to place anything else on the left side is invalid syntax and will result in a compilation error. (there are no limits to what can be placed on the right side)
- </P><P>
- Assigning an entire array will overwrite the existing array, even if the previous array had more elements.
- </P><P>
- When assigning to a string subscript, you must assign an integer ASCII value from 1 to 255. Non-numeric values will result in a compilation error. Values outside this range will result in an undefined character being stored.
- </P><P>
- Note that you cannot name a hash variable on the left without also giving a subscript, as you cannot assign to an entire hash at once.
- </P><P>
- If you attempt to assign a floating-point value to an integer variable, it will be truncated. Attempting to assign an integer value to a floating-point variable will convert appropriately. Attempting to assign a numeric value to a string variable will convert appropriately. Any other incorrect type assignments will result in a compilation error.
- </P><P>
- Mid-statement assignment and multiple assignment are supported- technically, assignment is simply another operator.
- </P>
- <H2>7K) Functions, Function Libraries</H2>
- <P>
- Sample Function Definitions:
- </P><P><PRE>int MyFunction {
- return
- }
- int Sum(int A, int B, int C) {
- int Result = A + B + C
- return Result
- }
- var ITakeAnyTypeOfParameter(var Value) {
- }</PRE></P>
- <P>
- Sample Function Calls:
- </P><P><PRE>DoSomething
- DoSomethingElse()
- GotoXY 1, 2
- ::PrintMsg("Hello", Red)
- Var = Abs(Var)
- Result = Library::Function(1, 2, 3)
- ::"MyFunction"</PRE></P>
- <P>
- Functions allow you to assign a name to one or more lines of code, and call that code at any time. Functions can optionally accept parameters passed to them, and always return exactly one value. (which you can choose to ignore) Functions can be a part of an object's code, or part of a global library of functions that all objects can access. (this is similar to the distinction between local and global variables)
- </P><P>
- A function definition consists of a return type (required), then an identifier for the function name, optionally followed by one or more parameter declarations.
- </P><P>
- The return type must be 'var', 'int', 'str', or 'float', optionally combined with 'array' or 'hash'. (you cannot specify 'var' with 'hash' or 'array') A return type is required.
- </P><P>
- Parameter declarations are exactly the same as variable declarations, except that 'global' or 'local' are not used, and you cannot assign starting values. A declaration consists of 'var', 'int', 'str', or 'float', optionally combined with 'array' or 'hash', followed by an identifier. (you cannot specify 'var' with 'hash' or 'array') Parameter identifiers can optionally be prefixed with dollar signs. Parameter declarations are separated with commas, and the entire set of declarations may optionally be enclosed in parenthesis ( ).
- </P><P>
- When passing arrays or hashes, or returning them, keep in mind that the entire array or hash is not copied- instead, a "reference" to the array or hash is passed. This means any changes made will affect the original array or hash. This is irrelevant for returning a function-local variable, but important otherwise. You can always assign an array to another array to make a copy to work with, if necessary.
- </P><P>
- This function definition is then followed by a code block containing the body of the function. An object function definition must be created outside of any other blocks of code. The function definition itself does not do anything, it must be called elsewhere for it to have any effect.
- </P><P>
- Within a function, use the keyword 'return' to exit the function and return a value. Reaching the end of the function's code block will automatically return. You can optionally follow 'return' with an expression, and the resulting value will be returned; otherwise, either 0, 0.0, "", or an empty array or hash will be returned, depending on the return type. Attempting to return a value not compatible with the return type will result in a compilation error.
- </P><P>
- To call a function, simply use the name of the function as part of an expression or as a statement by itself. You may optionally follow the function name with one or more values or expressions as parameters. Separate multiple parameters with commas, and you may optionally enclose them in parenthesis. Passing the wrong parameters, or parameters of the wrong type, will result in a compilation error. When a function is used as a statement, the return value is discarded. When a function is part of an expression, its return value is used within the expression.
- </P><P>
- When calling a function, you may optionally precede its name with a double-colon. When using this syntax, the function name may be specified as a string literal or an expression that results in a string literal. (expressions should be enclosed in parenthesis, due to operator precedence) This allows you to dynamically choose a function to call. Attempting to call functions dynamically has its own problems, as this is the only place the compiler cannot check for proper data types. The compiler will instead attempt to convert parameters at runtime. Any parameters you don't specify and any parameters that can't be converted will be passed as empty values. (0, 0.0, "", or an empty hash or array.) Conversion is limited to converting between numeric types and converting numeric types to strings. The same conversion is done on the return type. If you attempt to dynamically call a function that does not exist, nothing happens, and an empty value of the type you were expecting is returned.
- </P><P>
- Within a function code block, the parameter names listed in the function definition will automatically become function-local variables of the same name, containing the parameters passed. These variables are only visible within the function itself, and are kept separate and hidden from any other copy of the function running, or any other code elsewhere. You can also define more variables (optionally using the 'local' keyword) in the code block, which will be treated the same way- only visible within the function itself. These function-local variables can even share names with normal local or global variables, and will be accessed instead. These declarations can do anything a normal variable declaration can do except define a global. (even constants are valid)
- </P><P>
- You may define functions in function libraries instead of within object code. These functions are automatically usable by any object at any time, and can define function-local variables in the same way regular functions can. Multiple function libraries can be loaded at one time. If multiple libraries have functions with the same name, you can specify which library to access by prefixing the function name with the library name and a double colon ::. Function libraries cannot declare code outside of functions or declare local or global variables or constants. Function libraries can call functions in other libraries, however.
- </P><P>
- As functions can be called without parameters or parenthesis, they can be indistinguishable from variables when used by name alone. You can use dollar signs $ or double-colons :: to distinguish in these cases, as needed. Note that $blah always means "a variable named blah" and ::blah always means "a function named blah". If you wanted to call the function whose name is stored in a variable, you would use ::(blah) or ::($blah). (the latter being necessary if there was also a function named "blah")
- </P>
- <H2>7L) If, Else</H2>
- <P>
- Samples:
- </P><P><PRE>If A > B return
- If (Gems = 0) {
- Say("Out of gems!")
- }
- Else {
- Gems = Gems - 1
- }</PRE></P>
- <P>
- 'if' allows you to execute a statement or block of code based on whether a condition is true or false. 'if' is followed by an expression, optionally enclosed in parenthesis, then a statement or code block. If the expression is true, the statement or code block is executed; otherwise, it is skipped.
- </P><P>
- This may optionally be followed by 'else' and another statement or code block, which is executed if the expression is false, otherwise it is skipped. As 'if' is itself a legal statement, you can chain multiple conditions by using 'else if'.
- </P>
- <H2>7M) True, False, Query</H2>
- <P>
- 'true', 'false', and 'query' are pre-defined constants represented by keywords. 'true' is always a true value, and 'false' is always a false value. 'query' is also a true value, but is guaranteed to be different than 'true', and guaranteed to be less than 0. All values are integers. 'true' is currently defined to be 1, 'false' to be 0, and 'query' to be -1, but this behavior should not be relied on. 'query' is used in many built-in functions as a way to check the state of something without setting it.
- </P>
- <H2>7N) For</H2>
- <P>
- Sample:
- </P><P><PRE>For (Color = 0 To 15) {
- ChangeFg(Color)
- }</PRE></P>
- <P>
- 'for' allows you to quickly cycle through a range of integers or perform a block of code a predetermined number of time. 'for' is followed by a variable name, an equal sign =, and then two expressions separated by 'to'. This is then followed by a statement or block of code. You may optionally place parenthesis around the variable and expressions as a group.
- </P><P>
- The variable is initially set to the first expression, then the block of code is executed. Then, the variable is compared to the current value of the second expression. If equal, the loop ends. Otherwise, the variable increases by one, and the loop repeats. The second expression is evaluated each time through the loop.
- </P><P>
- If a non-numeric expression is used, a compile-time error will result. There is currently no way to loop backwards or count by anything other than one. If you require more detailed control, use a 'while' or 'until' loop.
- </P>
- <H2>7O) Foreach</H2>
- <P>
- Sample:
- </P><P><PRE>Foreach (Word in WordArray) {
- Say(Word)
- }</PRE></P>
- <P>
- 'foreach' allows you to cycle through all data in an array or hash. 'foreach' is followed by a variable name, 'in', and then an expression that evaluates to an array or hash. (usually a variable name) This is then followed by a statement or block of code. You may optionally place parenthesis around the variable and expression as a group.
- </P><P>
- The variable is initially set to the first item in the array or the first index in the hash. The block of code is then executed. Then, if there are any more items or indices left, the loop repeats. The expression for the array or hash is only evaluated once, before the loop begins, so if the contents change, it won't affect the loop.
- </P><P>
- If the expression evaluates to something other than an array or hash, or the variable specified is not of the same type as the array or hash, you will get a compile-time error. Also note that hash indices may be listed in any order, not necessarily the order they were created or a sorted order.
- </P>
- <H2>7P) Do, While, Until</H2>
- <P>
- Samples:
- </P><P><PRE>While (A < 10) {
- A = A + 1
- }
- Do {
- Say "Hello"
- } Until Gems = 100</PRE></P>
- <P>
- These keywords allow you to construct loops that loop based on your own conditions. These loops can be constructed in one of two ways. Prefix loops are constructed with the 'while' or 'until' keyword, then a condition optionally in parenthesis, then a statement or block of code. Postfix loops are constructed with the 'do' keyword, then a statement or block of code, then the 'while' or 'until' keyword, then a condition optionally in parenthesis.
- </P><P>
- A prefix loop checks the condition, then executes the code, then repeats. A postfix loop executes the code, then checks the condition, then repeats. A 'while' loop repeats as long as the condition is true. An 'until' loop repeats as long as the condition is false.
- </P>
- <H2>7Q) Labels and Goto</H2>
- <P>
- Sample:
- </P><P><PRE>label:
- Say "This will repeat an infinite number of times"
- goto label</PRE></P>
- <P>
- Labels are simply a statement consisting of an identifier, followed by a colon :. A label has no actual effect on a script, by itself. Use the 'goto' keyword, followed by a label, to send script execution to a label. The label must be before the 'goto' statement.
- </P><P>
- You cannot normally jump to a label that is inside a function except from within that function, or a label that is outside of all functions from within a function.
- </P><P>
- You cannot dynamically determine label names using expressions. You can have more than one label with the same name, but only the first one that fits a given circumstance will be used.
- </P>
- <H2>7R) Break, Continue, Repeat</H2>
- <P>
- Samples:
- </P><P><PRE>For (Pos = 1 To 100) {
- If (Pos > SomeVar) Break
- }
- outer:
- While (X < 100) {
- While (Y < 100) {
- If (Y > X) Break outer;
- Y = Y + 1
- }
- X = X + 1
- }</PRE></P>
- <P>
- These keywords are used as statements by themselves or with a label, and only work within a loop- a 'do', 'while', 'until', 'for', or 'foreach' code block. 'break' ends the currently-running loop, resuming execution right after the loop. 'continue' jumps to the end of the loop's code, but then proceeds normally to loop normally, checking any condition, etc. 'repeat' restarts the current loop, without checking any condition or changing any values.
- </P><P>
- If used with a label, the loop affected is the loop that starts right after the given label. A label that does not refer to a currently-running loop or use of these commands outside of a loop causes a compilation error.
- </P>
- <H2>7S) Main</H2>
- <P>
- The 'main' keyword is used by itself. It causes the current function to become the 'main' loop of the object. This is done by clearing the stack. Any further use of 'return' in (or reaching the end of) the current function will be like 'end'- any code that called the current function will not execute under any circumstance. If another object is waiting on a message reply, it will immediately receive an empty return value.
- </P><P>
- This keyword is typically used to create functions that receive a message and then handle all processing from that point forward. This keyword can also be used before a function definition, to denote that the function always becomes the 'main' loop, whenever it is called. This keyword can go before or after the return type.
- </P><P>
- Although technically this can be simulated by just never returning from a function, this could cause an object's stack to grow needlessly, eventually causing internal overflow errors.
- </P>
- <H2>7T) End</H2>
- <P>
- The 'end' keyword is used by itself. It causes the current object to stop execution of all code. Only an outside event or message will cause the object to resume running. This behavior occurs even if the object is in the middle of a function.
- </P>
- <H2>7U) Sending and Receiving Messages</H2>
- <P>
- Samples:
- </P><P><PRE>ObjectGuy.GoNorth
- $VarWithObjectNameOrID.GoNorth
- "Robot*".::DoStuff A, B, C
- "*".Dance(5)
- Result = "Player".WhereAreYou()
- Object.$Var</PRE></P>
- <P>
- Sending messages between objects is the primary way in which they communicate. Under normal conditions, when you send a message to an object, that object immediately stops what its currently doing and starts a function. When that function returns, the object will then resume whatever it was currently doing. Meanwhile, the sending object continues to the next statement. Note that due to the method of concurrency used, the sending object will temporarily "stop" while the recipient object handles a message. Once the recipient object finishes handling the message or intentionally delays, the sending object will resume its script again. This means that if you send multiple messages in a row to an object with no delays, they will most likely be executed in order immediately. However, if one or more of those messages causes a delay or idle, those messages will finish during the object's future, normal time slices.
- </P><P>
- Note that outside game events may send messages; actions of objects can also indirectly trigger messages; these are also processed immediately, pre-empting the current object until complete or an idle or delay.
- </P><P>
- The most basic syntax for sending a message is the name of the object as an identifier, followed by a period ., followed by the name of the function. This can then be followed by parameters, optionally enclosed in parenthesis, if needed. The function can be a function specific to the object, or a global, library, or built-in function. Sending parameters in this way is subject to standard parameter-conversion rules.
- </P><P>
- Just like with regular function calls, you may optionally use a double-colon to clarify it is a function call, optionally with a library name to clarify where the function is located.
- </P><P>
- Note that objects do not have to have unique names, so this can potentially send a message to multiple objects. In this case, all objects with the given name receive the message. In order to allow direct one-to-one communication, you may instead specify the internal numeric id of an object. As there is no way to know a numeric id when writing a program, you cannot specify an id as a numeric literal- you must use a variable or function to retrieve it. Note that if a name or id doesn't match any objects, nothing will happen. This is not considered an error. Also note that using a name only searches the current scene, however, using an id can access objects outside of the current scene.
- </P><P>
- You may also specify the object name using a string literal, variable name, or other expression. (variables and expressions should be enclosed in parenthesis, due to operator precedence) This allows you to dynamically determine which object to send the message to. Note that if you use a variable by itself, you must enclose it in parenthesis or the compiler will think you want to access a variable of the other object. (covered below)
- </P><P>
- The name specified, if done via a string or expression, may contin asterisks * as wildcards. The wildcard represents any zero or more characters, and all matching objects are sent the message. "*" will send a message to all objects- even the object doing the sending. (it is legal for an object to send itself a message)
- </P><P>
- The function specified can use indirect function syntax as well, using a string literal or expression. This will dynamically determine the name of the function to call.
- </P><P>
- Normally, when sending a message, the sender resumes code during the same cycle, and does not get a result back. However, if you send a message as part of an expression or statement, the sender will halt and do nothing more until the messaged object finishes running its function- even if there are delays. The return value of the function will then be passed back to the sender (this is called a 'reply') and the sender will resume running. Do not use this form unless you really want to halt your code and wait for a response. Any use of message sending within an expression, statement, assignment, etc. will cause this, so be careful.
- </P><P>
- Some caveats apply to this variation- If no matching object or function is found, an empty value is immediately returned. If multiple matching objects are found, only one processes the message. (The object is not chosen at random, but there is no guaranteed way to predetermine which one will receive the message) If the matching object quits running without replying, an empty value is returned. If a value of the wrong type is returned, an empty value will result unless conversion (between numeric types or from numeric to string) can be done. The sending object will not resume its current code path until a reply is received, but it can still receive and process messages as normal, and when done, returns to waiting for a reply. (or immediately resumes with one that was returned while it was busy with other code) In terms of concurrency, the messaged object will begin its code immediately, without waiting for its normal position in sequence, and if it finishes its code in a single cycle, the sending object will also resume immediately and the messaged object will get to run as normal in sequence, if it has not already. (this situation is exactly the same as the handling of a normal message that has no delays.)
- </P><P>
- There is no way to send a message that causes a 'goto' to a label instead of a function call.
- </P><P>
- In addition to calling functions, you may access variables of the other object. You may optionally use $ to clarify that you intend to access a variable, and not a function. Sometimes the compiler will not be able to determine whether you meant to access a variable or call a function with no parameters- this will cause an error and you must add $, ::, or () to clarify your intention. You cannot access variables using dynamic names from strings or expressions- if you use an expression with the . operator, it is automatically assumed that you mean to send a message. (call a function)
- </P><P>
- When you access a variable of another object, if multiple objects match, and you are setting the variable, you will set the variable for all matching objects. However, like calling a function with a return value, if you are reading the variable, you will read the variable from a single object chosen arbitrarily. Also, if no matching objects are found, an assignment will have no effect, and reading a variable will return a 0, 0.0, "", or blank hash or array.
- </P>
- <H2>7V) Reply</H2>
- <P>
- The 'reply' keyword is similar to 'return', except that it simply sends a reply to the object that messaged it, without actually halting execution of the current function. When 'reply' is used, if the current function was the result of a message, the object who sent the message resumes. If that object was waiting on a return value, the value after 'reply' is used. The current object and function then continues to run during that object's normal time-slice.
- </P><P>
- If you use 'reply' in a function that was not called via a message, nothing happens. If a function called via a message proceeds to another function, that function cannot use 'reply' to any effect, either- 'reply' must be used in the original function that resulted from a message.
- </P>
- <H2>7W) Comments</H2>
- <P>
- Samples:
- </P><P><PRE>// Comment goes here
- /*
- Multi-line
- comment
- */</PRE></P>
- <P>
- Comments allow you to insert text into a script that is ignored, or temporarily disable one or more lines of code. Start a comment with a double slash //, followed by any amount of text. The comment will end at the end of the line. This comment can share a line with other code.
- </P><P>
- Alternately, you can begin a coment with slash asterisk /*. This comment will continue until a closing asterisk slash */ combination. This comment can be any number of lines, or just part of a line if desired. This comment can also share a line with other code- even appearing in the middle of a single statement. You cannot nest multiple comments using /* and */. In other words, even if multiple /* symbols appear, only a single */ symbol is used to end the comment.
- <P>
- <H2>7X) State</H2>
- <P>
- Samples:
- </P><P><PRE>state jump {
- int shot {
- doSomething()
- }
- }
- state jump, fall
- self().shot()
- .shot()
- state default</PRE></P>
- <P>
- States allow you to define sets of messages that are only available from a given state. You can then set an object's state, and only messages within that state are accessible. This allows state machines that properly handle incoming messages.
- </P><P>
- To define a state, use the 'state' keyword followed by an identifier that represents the state name, then follow with a code block. This code block can only contain function definitions. These functions can share names with functions within other states or outside of any state.
- </P><P>
- To set the state of an object, use the 'state' keyword followed by one or more identifiers representing the current states, separated by commas. Once a state has been set, any messages sent to that object will only trigger functions found within the given states. Functions outside of any state can also be triggered via messages (this is always the case) but would be overriden by matching functions within a state.
- </P><P>
- Use 'state' followed by the 'default' keyword to reset the state. (this is the default state before any 'state' keywords are processed) In the default state, only functions outside of any state blocks are triggered via messages.
- </P><P>
- You can also list states using strings, but remember that you must prefix variables with $ and functions with :: if necessary to distinguish them from a similarly-named state.
- </P><P>
- States are searched for matching functions in the order listed after the 'state' keyword, in case of multiple matching functions. Only the first matching function is used. The default state is always searched last.
- </P><P>
- Changing the current state does not affect any currently-running code- you can safely change the state to 'jump' from within a 'fall' state function, for example.
- </P><P>
- Note that changing states does not affect normal function calls within the object itself. Normal function calls from within a state can only access other functions within that state or functions outside of state blocks. Normal functions calls outside any state block can only access functions outside of state blocks. Funtions called dynamically using :: and a string can only access functions outside of state blocks unless the state is specified, as below. In order to make a function call that is affected by the current state, an object must send itself a message- this is simplest to do using the self().function() or simpler .function() syntax.
- </P><P>
- From within an object, you can also explicitly access functions from another state using :: syntax. For example, to access a function within the 'jump' state, you might write jump::doSomething(). You may also use 'default' in this way. Note that if you have named one of your states the same as a function library, you will no longer be able to use :: syntax to access that library- it will instead attempt to access that state. There is no restriction on accessing functions across different states from within an object, as long as you specify the state using ::. You may not use this method when sending messages to other objects- you may only access their currently defined states.
- </P>
- <H2>7Y) Message Sending Efficiency</H2>
- <P>
- Due to the way objects and scripts work, sending messages between objects can be very inefficient, as the compiler has no way of knowing what functions or variables may be available for the recipient object. The compiler also has no way of knowing what type of value may be returned by a function, required for it's parameters, or stored in a variable. Some ways exist to make this more efficient.
- </P><P><PRE>object@scriptname.func()</PRE></P><P>
- The @ 'operator' allows you to clarify what type of script will be running on a given object. This allows the compiler to know exactly what type of function or variable you are accessing. If you give the wrong script name, then nothing will happen, as if no matching object was found- even if the object does exist and has access to a function of that name.
- </P><P><PRE>@scriptname.func()</PRE></P><P>
- You can also simply access scripts by their scriptname instead of object name- this example accesses all objects running the given script. This also allows the compiler to know exactly what type of function or variable you are accessing. @ can only be followed by a script name in identifier form- you cannot use expressions or variables after a @.
- </P><P><PRE>object.lib::func()</PRE></P><P>
- Even when accessing objects without scriptname designators, if you are accessing library functions, the compiler can optimize the function using this knowledge. The library name ensures the proper function is found- without it, the compiler cannot assume the library function is intended unless no known script has it's own function by that name.
- </P><P><PRE>#names a b c*</PRE></P><P>
- At the top of a script, you may use a # config line to define which names are allowable names for that script. GCSx will then prevent you from naming an object running that script to any other name. By doing this, you help the compiler narrow the possibilities for a given object. If the compiler can assert that a given name is gauranteed to be running a given script, it can determine the function or variable you will be accessing. This works best when you use unique names or prefixes for each potential script.
- </P><P>
- If you do not do this, the compiler will attempt to associate object names with script names automatically, but it can't always determine all possibilities. If at any point you use the changename() function with a variable or other expression, the compiler will be unable to associate any names with any scripts, as there is always a possibility that you could change at least one script to any name. In these cases, you should at least use #names on the scripts that contain changename() functions.
- </P>
- <H1>8) Built-in Functions</H1>
- <P>
- Only functions in the standard, built-in library are included here. In addition, functions concerning sprites, layers, scenes, input, graphics, or sound of any kind are covered in other sections. All functions return empty values of the appropriate type if an error occurs. Compile-time errors will result if invalid parameter types are used.
- </P><P>
- Built-in functions are considered part of the gcsx library, so if you've defined a function with the same name elsewhere, you can still access the built-in function by prefixing it with 'gcsx::'.
- </P>
- <H2>become()</H2>
- <P><PRE>Definition: int become(str script)
- (does not return unless error)</PRE></P>
- <P>
- become() allows an object to change its script. Specify the name of the script to become, as a string. If a script by that name exists, the object will immediately switch to that script. The new script then begins running at the start. All messages in the queue and all local variables are lost. If the script is not found, the old script resumes immediately after the become() function, so you can follow this with code that assumes an error occurred. (0 will be returned.)
- </P>
- <H2>block()</H2>
- <P><PRE>Definition: int block(true | false | query)</PRE></P>
- <P>
- block(true) enables message blocking, block(false) disables it. When message blocking is on, an object will ignore and forget any messages sent to it from any source. block(false) will not disable any blockobject() blocks. block() always returns the current block condition. Use block(query) to return the current condition without changing it.
- </P>
- <H2>blockobject()</H2>
- <P><PRE>Definition: int blockobject(str objectname, true | false | query)</PRE></P>
- <P>
- blockobject() handles message blocking for specific objects, by name. You may specify an objectname as a string, which will block messages from objects matching the given name. You may use asterisks * as wildcards. The second parameter should be true to enable a specific block, false to disable it, or query to simply request whether a given name is being blocked. blockobject() always returns the current block condition for the specified objectname. You can use blockobject("", false) to disable all object-specific blocks in place.
- </P><P>
- Note that when changing or querying existing blocks, names must exactly match. For example, even though blocking "robot*" will block an object named "robot123", blockobject("robot123", query) would still return false, and neither blockobject("robot123", false) nor blockobject("rob*", false) would disable the block.
- </P>
- <H2>changename()</H2>
- <P><PRE>Definition: int changename(str newname)</PRE></P>
- <P>
- changename() allows an object to change its name. This does not affect any messages currently queued or being processed. This function always returns 0.
- </P>
- <H2>concurrent()</H2>
- <P><PRE>Definition: int concurrent(true | false | query)</PRE></P>
- <P>
- concurrent(true) will enable concurrency for the current scene, while concurrent(false) will disable it. Once concurrency is enabled for a scene, objects, sprites, and any other game-controlled items will continue to run, even when another scene becomes current. Note that a scene running concurrently will not be displayed on the screen in any way, output any sound, or detect any user input.
- </P><P>
- This feature should be used sparingly, as it can severely increase the amount of processing required to run a game.
- </P><P>
- concurrent() always returns the current concurrency setting. Use concurrent(query) to return the current setting without changing it.
- </P>
- <H2>delay()</H2>
- <P><PRE>Definition: int delay(int ticks)</PRE></P>
- <P>
- delay() will wait 'ticks' milliseconds. Note that this function, by default, has a granularity of 1/60th of a second, or about 17 ticks, so delay(1000) will wait somewhere between 1000 and 1017 ms. delay(0) will do nothing, returning immediately. The granularity of this function is affected by fps(). This function always returns 0.
- </P>
- <H2>die()</H2>
- <P><PRE>Definition: int die()
- (does not return)</PRE></P>
- <P>
- die() causes the object to be destroyed completely. Its code will stop execution and the object will cease to exist as part of the game. If the object is linked to a sprite, the sprite will disappear as well.
- </P>
- <H2>discard()</H2>
- <P><PRE>Definition: int discard()</PRE></P>
- <P>
- discard() is only used when queueing is enabled via queue(). If queueing is enabled, discard() checks to see if there is a message in the queue, and if so, discards it without doing anything. This only affects the next message in the queue. You can use this in conjunction with peek() to filter messages as desired. This function returns true if a message was found to discard, false otherwise.
- </P>
- <H2>fps()</H2>
- <P><PRE>Definition: int fps(int number)
- Returns: integer</PRE></P>
- <P>
- fps() allows you to change how long a single game cycle (or frame) lasts. Normally, game cycles (frames) occur 60 per second. You can set this mode by using fps(60). You may, however, set GCSx to run any number of frames from 1 to 200 per second. The number passed is equal to the number of frames or cycles to run per second.
- </P><P>
- fps() returns the current frames-per-second setting. You can use fps(query) to retrieve the current frames-per-second setting without making a change. Attempting to set fps() above 200 or below 1 will result in no change.
- </P><P>
- Note that changing the fps() setting also affects the granularity of delays and timers.
- </P>
- <H2>globalobject()</H2>
- <P><PRE>Definition: int globalobject(int id, true | false | query)</PRE></P>
- <P>
- globalobject() allows you to see whether a given object is global or scene-specific, as well as change that setting. The 'id' parameter must be the internal id of the object to check or change. Use self() for the current object. The second parameter should be true to make an object global, false to make an object specific to the current scene, or query to simply check the status.
- </P><P>
- globalobject() always returns true or false based on whether the object is currently global. This function does not affect any local or global variables adversely.
- </P>
- <H2>idle()</H2>
- <P><PRE>Definition: int idle(int count)</PRE></P>
- <P>
- idle(count) is used to wait 'count' game cycles. The first game cycle counted is the current game cycle, so idle(1) will end the object for the current cycle and proceed to the next object in schedule, but have no further effect or delay. idle(0) will do nothing, returning immediately. The return value is always 0.
- </P>
- <H2>indices()</H2>
- <P><PRE>Definition: str array indices(hash thehash)
- Returns: array</PRE></P>
- <P>
- indices() will return all existing index values of the given hash, as an array. The order of the indices is indeterminate- they are not sorted in any way. The order will correspond to the array returned by values() or a second call to indices() or a foreach loop, however, as long as the hash does not change. An empty hash will return an empty array. All indices are unique, so there will be no duplicates.
- </P>
- <H2>length()</H2>
- <P><PRE>Definition: int length(str thestring)</PRE></P>
- <P>
- length() returns the length of the given string. length("") will return 0.
- </P>
- <H2>objectfind()</H2>
- <P><PRE>Definition: int array objectfind(str name)</PRE></P>
- <P>
- objectfind(name) will find all objects matching the given string and return their internal ids. The name may contain asterisks * as wildcards. The results are returned in an array, even if only one matching object is found. If no objects are found, an empty array is returned.
- </P>
- <H2>objectname()</H2>
- <P><PRE>Definition: str objectname(int id)</PRE></P>
- <P>
- objectname() returns the name of an object. Use objectname(self()) for an object to retrieve its own name.
- </P>
- <H2>peek()</H2>
- <P><PRE>Definition: str peek()</PRE></P>
- <P>
- peek() is only used when queueing is enabled via queue(). If queueing is enabled, peek() checks to see if there is a message in the queue, and if so, returns a string containing the name of the function being called. If there is no message waiting, "" is returned. This allows you to check if any messages are waiting, without actually performing them yet. If you determine you wish to run the event, call poll().
- </P>
- <H2>poll()</H2>
- <P><PRE>Usage: int poll()</PRE></P>
- <P>
- poll() is only used when queueing is enabled via queue(). If queueing is enabled, poll() checks to see if there is a message in the queue, and if so, performs it. If there is not, nothing happens. Polling allows you to delay responding to messages until appropriate, in between performing your own tasks. A call to poll() only processes one message, even if multiple are waiting. Use a loop with peek() if you want to ensure the queue is empty before resuming your other tasks. poll() returns true if a message was processed, false otherwise.
- </P><P>
- Note that any message processed will properly take into account the current state.
- </P>
- <H2>scriptname()</H2>
- <P><PRE>Usage: str scriptname(int id)</PRE></P>
- <P>
- scriptname() returns the name of the script being run by an object. Use scriptname(self()) for an object to retrieve its own script name.
- </P>
- <H2>queue()</H2>
- <P><PRE>Usage: int queue(true | false | query)</PRE></P>
- <P>
- queue(true) will turn on message queueing, queue(false) will turn it off. When queueing is enabled, all messages sent to the object will be placed in a queue. Anytime the object ends code execution, the next message in the queue is performed immediately. If the queue is turned off with messages in it, any messages left in the queue immediately run as if they had just been received in the same order they were. (this will result in them being processed in reverse order)
- </P><P>
- In other terms, normally messages are processed in a LIFO order. queue() changes this to FIFO order.
- </P><P>
- Combining replies and queues can result in hung objects if care is not used. The simplest example would be an object that enables queueing, than sends itself a message requiring a reply. It will hang indefinately, waiting for a reply from a message that it will never execute because it is queued, waiting for code to end execution.
- </P><P>
- queue() always returns the current queue setting. Use queue(query) to return the current setting without changing it.
- </P>
- <H2>quit()</H2>
- <P><PRE>Usage: int quit()
- (does not return)</PRE></P>
- <P>
- quit() causes the world to exit entirely. There is no confirmation or possibility of error. If the game is being played from the normal GCS, this will return to the menu for selecting games, etc. If the game is being played using a standalone GCS runtime, the program will exit completely.
- </P>
- <H2>self()</H2>
- <P><PRE>Usage: int self()</PRE></P>
- <P>
- self() returns the internal id for the current object.
- </P>
- <H2>sizeof()</H2>
- <P><PRE>Definition: int sizeof(var)
- Actual Usage: sizeof(hash)
- Actual Usage: sizeof(array)</PRE></P>
- <P>
- sizeof() will return the number of entries in a hash or array. An empty hash or array will return 0. A non-hash, non-array value will also return 0.
- </P>
- <H2>snapshotcopy()</H2>
- <P><PRE>Definition: int snapshotcopy(str source, str dest)</PRE></P>
- <P>
- snapshotcopy() is used to copy a saved game snapshot from snapshotsave() from one slot to another. 'source' is the unique identifier for the game to copy from, 'dest' is the identifier to copy to. snapshotcopy() returns true if successful, false if an error. Errors include the source game not existing, or some sort of file error. It is not an error to overwrite an existing game.
- </P>
- <H2>snapshotdelete()</H2>
- <P><PRE>Definition: int snapshotdelete(str name)</PRE></P>
- <P>
- snapshotdelete() is used to delete a saved game snapshot from snapshotsave(). 'name' is a unique identifier for the saved game. snapshotdelete() returns true if successful, false if an error. It is considered an error if there is no saved game with the given id.
- </P>
- <H2>snapshotinfo()</H2>
- <P><PRE>Definition: var snapshotinfo(str name, str varname)</PRE></P>
- <P>
- snapshotinfo() is used to view data from a saved game snapshot, saved using snapshotsave(). 'name' is a unique id that identifies the saved game. 'varname' is a string containing the name of a global variable to view. snapshotinfo() will return whatever value is in the global variable, or 0 if there is any error retrieving that data. The return value for this function will follow the same conversion rules as converting function return values.
- </P>
- <H2>snapshotlist()</H2>
- <P><PRE>Definition: str array snapshotlist()</PRE></P>
- <P>
- snapshotlist() returns an array containing the string ids of every snapshotsave() for the current world. An empty array is returned if no games have been saved or if there is an error retrieving the information.
- </P>
- <H2>snapshotload()</H2>
- <P><PRE>Definition: int snapshotload(str name, str returnvalue)
- (does not return unless error)</PRE></P>
- <P>
- snapshotload() is used to load an exact snapshot of the game, saved earlier using snapshotsave(). This is most commonly used to allow the user to save and load their game progress. 'name' is a unique string id that identifies the saved game. 'returnvalue' can be any value other than "" or "fail", and is used as the return value from snapshotsave().
- </P><P>
- snapshotload() will only return if there is an error. This could be a loading error of some sort, or because you passed "" or "fail" for 'returnvalue'. In this case, the return value will be 0.
- </P>
- <H2>snapshotmodify()</H2>
- <P><PRE>Definition: int snapshotmodify(str name, str varname, var newvalue)</PRE></P>
- <P>
- snapshotmodify() is used to modify a saved game snapshot, saved using snapshotsave(). This is done by changing the value of a global variable. (this is the only type of modification that can be done) 'name' is a unique string id that identifies the saved game. 'varname' is a string containing the name of a global variable to modify. The value to assign is passed as 'newvalue', and should be of the same type as the global variable.
- </P><P>
- snapshotload() will return true normally, or false if there is an error. This could be a file error of some sort, or indicate no saved game by the given name exists. An error could also occur if you tried to assign an invalid data type, such as an array to a non-array variable or a string to an integer varaible, or if the global variable does not exist.
- </P>
- <H2>snapshotsave()</H2>
- <P><PRE>Definition: str snapshotsave(str name)</PRE></P>
- <P>
- snapshotsave() is used to save an exact snapshot of the game. This is most commonly used to allow the user to save and load their game progress. 'name' is a unique string id to refer back to the saved game later. You can save games using actual names, or if you prefer, just use numbers to index them.
- </P><P>
- When you first call snapshotsave() and the game is saved, it returns "". This signifies the game being saved. When the game is later reloaded, snapshotsave() returns a user-defined value other than "". This signifies the game being loaded. Note that the same code- the code immediately after the snapshotsave() function- will execute both right after a game being saved, and right after a game being loaded. You must check the return value if you wish for something different to occur (such as different animations) in these cases.
- </P><P>
- snapshotsave() will return "fail" if a save was attempted, but failed for some unknown reason.
- </P>
- <H2>snapshottime()</H2>
- <P><PRE>Definition: int snapshottime(str name)</PRE></P>
- <P>
- snapshottime() is used to determine when a saved game snapshot was saved, using snapshotsave(). 'name' is a unique string id that identifies the saved game. The value returned is just like a value from time(), and can be similarily converted using timeconvert(). snapshottime() can be used with a name of "" to retrieve the most recent time the currently running game world was saved, regardless of any snapshotload() calls or quitting and restarting the game. This value will be equal to the snapshottime() of an existing saved game, unless that saved game was deleted. snapshottime() returns 0 if the time cannot be retrieved (if the id cannot be found, or there is a file error) or if the world has never been saved.
- </P>
- <H2>spawn()</H2>
- <P><PRE>Definition: int spawn(str script)</PRE></P>
- <P>
- spawn() allows you to create a new object, with the specified script. spawn() returns the internal id of the new object. The object will not start with any graphical representation, tile, sprite, or position. Either the new object or the object calling spawn() should assign a representation immediately, if one is needed. A spawned object starts out as part of the current scene, but can be changed to a global object using globalobject() if needed.
- </P><P>
- In terms of concurrency, the new object will not immediately run, but will get to run as part of the current cycle.
- </P><P>
- If a script with the given name does not exist, nothing happens, and 0 is returned.
- </P>
- <H2>substr()</H2>
- <P><PRE>Definition: str substr(str thestring, int start, int length)</PRE></P>
- <P>
- substr() takes a portion of a string and returns it. The portion is taken starting at the start position given, and continues for the given length in characters. If length is 0, the entire rest of the string is returned. The start position is zero-based- a start of 0 means to start at the beginning of the string. Requesting more characters than are present will simply return as much as possible. Requesting a substring past the start of the string will return "".
- </P>
- <H2>sync()</H2>
- <P><PRE>Definition: int sync(str syncname, var)
- Actual Usage: sync(syncname, int)
- Actual Usage: sync(syncname, str)</PRE></P>
- <P>
- sync() will halt code execution and wait until a certain number of other objects are waiting. The second parameter may be an integer, in which case that many objects must be waiting. The second parameter may be a string, in which case all objects with a matching name (on that scene, plus global objects) must be waiting. Only objects with the same 'syncname' are counted. For example, sync("x", 2) will halt until a second object also runs a sync("x", 2), then both will resume. If an object name is used, it may contain asterisks * as wildcards.
- </P><P>
- Using the same syncname, but different counts or object names, is legal, but not recommended.
- </P><P>
- Objects in a sync() can still receive and process messages, and will NOT be counted as part of the sync() during that time. An object must be idle and actually waiting at the sync() command to be part of the count.
- </P><P>
- In terms of concurrency, all sync() conditions are checked at the end of each cycle, and any objects with fulfilled conditions are released simultaneously, and will act on their normal schedule again. This means that even a sync() call that is immediately fulfilled will still be equivalent to an idle(1). This also means that a sync that requires 2 objects can be fulfilled by 3 or even more objects, as long as they all join the sync() during the same cycle.
- </P><P>
- If invalid parameters are passed to sync(), it immediately returns 0. Otherwise, sync() returns the number of objects that were synced when the condition was released.
- </P>
- <H2>ticks()</H2>
- <P><PRE>Definition: int ticks()</PRE></P>
- <P>
- ticks() returns the number of milliseconds from an arbitrary point in the past. Although meaningless individually, you can use two values and tickselapsed() to determine elapsed time. ticks() values automatically account for a game being saved and later reloaded- if you request a ticks() value, save the game, wait a day, reload the game, and request another ticks() value, it will only be a few seconds later.
- </P>
- <H2>tickselapsed()</H2>
- <P><PRE>Definition: int tickselapsed(int start, int end)</PRE></P>
- <P>
- tickselapsed() returns the number of milliseconds that passed between a starting ticks() value and an ending ticks() value. You should use tickselapsed() instead of subtraction, because tickselapsed() accounts for wraparound that can occur in the values. If you pass the ticks values in the wrong order, you will get invalid results.
- </P>
- <H2>time()</H2>
- <P><PRE>Definition: int time()</PRE></P>
- <P>
- time() returns an integer representing the current date and time. This value is the number of seconds from an arbitrary, fixed point in the past. These values can be passed to timeconvert() to determine month, year, hour, etc.
- </P>
- <H2>timeconvert()</H2>
- <P><PRE>Definition: array timeconvert(int time)</PRE></P>
- <P>
- timeconvert() converts a time value returned by time() into a usable format. The array returned contains all information about the time, in the following order- [ second, minute, hour, day, month, year, dayofweek ]. Hour ranges from 0 to 23, day ranges from 1 to 31, month ranges from 1 to 12, and year is four digits, so these values can be displayed directly or used as an array subscript to get English months. 'dayofweek' represents the day of the week from Monday at 1 to Sunday at 7.
- </P>
- <H2>timer()</H2>
- <P><PRE>Definition: int timer(int startticks, int frequency, int count, int objectid, str message)
- Returns: integer</PRE></P>
- <P>
- timer() is used to start a timed event. A timed event automatically sends messages to an object at predetermined intervals. Timers are based off of ticks() values.
- </P><P>
- 'startticks' is the ticks() value from which the timer bases its timing off of. This value must be a current or past ticks() value- not a time in the future. 'frequency' is the number of ticks to wait from the starting time, before triggering. 'count' is the number of times the timer should trigger- each additional time waits an additional 'frequency' ticks before triggering. A 'count' of 0 will create a timer that runs continuously until stopped. 'object' is the id of the object to send the message to. 'message' is the message to send to the object, in string format.
- </P><P>
- timer() returns a unique numeric id that represents the timer. You can later call timercancel(id) to cancel a running timer.
- </P><P>
- When the timer triggers, the selected message is sent to the selected object. The message includes parameters- the first parameter will be equal to the timer id, and the second will be equal to the ticks() value that triggered the timer. (which will be slightly different from the current ticks() value, due to cycle granularity) Keep in mind that, because cycles normally occur only 60 times a second, any attempt to run a timer with a frequency of less than 17 can trigger multiple times a cycle. Unless this is properly accounted for, object message queues can quickly overflow and bog the game system down, possibly even crashing. The granularity of timers is affected by fps().
- </P><P>
- The reasoning behind using a 'startticks' value and then a 'frequency', even for one-time timers, is due to the potential wraparound of ticks() values. Object code should never perform math on ticks() values. This also allows you to based multiple timers off of a single point in time, as ticks() values can differ between two statements, even when one statement runs immediately after another.
- </P>
- <H2>timercancel()</H2>
- <P><PRE>Definition: int timercancel(int id)</PRE></P>
- <P>
- timercancel() cancels a currently running timer. It returns true if a timer was found to cancel, false otherwise.
- </P>
- <H2>values()</H2>
- <P><PRE>Usage: array values(hash thehash)</PRE></P>
- <P>
- values() will return all existing values of the given hash, as an array. The order of the values is indeterminate- they are not sorted in any way. The order will correspond to the array returned by indices() or a second call to values() or a foreach loop, however, as long as the hash does not change. An empty hash will return an empty array. Note that values do not have to be unique, so there may be duplicates returned.
- </P>
- <H2>worldload()</H2>
- <P><PRE>Definition: int worldload(str world, str array retain)
- (does not return unless error)</PRE></P>
- <P>
- worldload() allows you to switch to another game world. Pass the filename as a string, for 'world'. Pass a blank string "" to reload the current game world. This filename should not have any path information- only the current directory can be accessed. You may optionally pass an array of strings as well. This array should contain the names of global variables you wish to retain. These names may contain asterisks * as wildcards. Any existing global variables will overwrite any default values for global variables of the same name in the other game world. Note that this cannot create global variables in the other world that do not already exist.
- </P><P>
- Code after this function will only execute if there is an error, such as a missing or invalid file. You should place code after the call to worldload() to handle this possibility. The return value in these cases is 0.
- </P>
- <H2>worldname()</H2>
- <P><PRE>Definition: str worldname()</PRE></P>
- <P>
- worldname() returns the name of the current world.
- </P>
- <H1>9) Sprites</H1>
- <P>(...todo- including functions to support...)</P>
- <H2>9A) Basics</H2>
- <P>(function to retrieve/find)</P>
- <H2>9B) Animation, Movement-Triggered Animation</H2>
- <H2>9C) Velocity, Accelleration</H2>
- <H2>9D) Movement/Animation Queue</H2>
- <H2>9E) Collisions Polling/Triggers</H2>
- <P>(setting of collision type and type-masks to determine what to detect; includes optional automatic clipping to prevent the actual collision, although collision is still triggered; be sure to clarify what happens if a collision occurs and is not solved, and the collision is still present next cycle)</P>
- <H2>9F) Sprite Linking</H2>
- <H1>10) Layers</H1>
- <P>(...todo- including functions to support...)</P>
- <H2>10A) Basics</H2>
- <P>(function to retrieve names/by name; function to reset to original contents; take and retrieve snapshots)</P>
- <H2>10B) Scrolling, Velocity, Accelleration</H2>
- <H2>10C) Camera, View</H2>
- <H2>10D) Focal Point, Parallax, Dead Spots</H2>
- <P>(Dead spots- areas not scrolled to be shown at any time- big 'maybe', algorithm doesn't seem obvious)</P>
- <H2>10E) Scaling</H2>
- <H2>10F) Rotation, Tilting</H2>
- <H2>10G) Transparency, Alpha Blending</H2>
- <H2>10H) Palette Effects, Timed Fading</H2>
- <H2>10I) Exits and Transitions, Offsets, Seams, Visual Wraparound</H2>
- <P>(transitions- how to animate between scenes, if anything like this is supported)</P>
- <P>(seams- continuous linked scenes- another 'maybe')</P>
- <H1>11) Scenes</H1>
- <P>(...todo- including functions to support...)</P>
- <H2>11A) Basics</H2>
- <P>(function to retrieve names/by name; reset to original contents; take and retrieve snapshots)</P>
- <H1>12) User Input</H1>
- <P>(...todo- including functions to support...)</P>
- <H2>12A) Keyboard</H2>
- <H2>12B) Mouse</H2>
- <H2>12C) Joystick/Gamepad</H2>
- <P>(retrieve number of axis, button, etc.)</P>
- <H2>13) Sound</H2>
- <P>(...todo- including functions to support...)</P>
- <P>
- Tempo, Volume, and Position for music; deltas/targets for all; queueing/crossfading songs; event triggers on a special mod effect; global music and sound volume settings; remembering song position for later reentry, including fading
- </P>
- <H1>14) (unused section)/H1>
- <H1>15) Editor Features</H1>
- <P>(...todo...)</P>
- <P>Novice mode</P>
- <P>Standard templates, such as standardized options/load/save/highscore/titlescreen code</P>
- <P>File lists with names which load in a separate thread so as not to delay opening the file list</P>
- <P>Mini-map</P>
- <P>Shuffle, random placement, gradients and fills, (fill/fill to border) terrain generation</P>
- <P>Embed external files into game file, such as music, sound, etc.</P>
- <P>Import image as map- splits into non-duplicate tiles</P>
- <P>Multi-level undo/redo for all features, specific to window (IE undo in a layer window won't undo code changes in another window)</P>
- <P>Auto-save, auto-backup to X levels when saving</P>
- <H1>16) Gameplay Features</H1>
- <P>
- There are no built-in gameplay features other than the following system functions. Many things you would normally find automatic support for, such as saving and loading games, high scores, title screens, and game over, must be supported via object code. However, standard templates will exist to provide users with these features with little to no coding on their behalf.
- </P><P>
- The current plan includes creating a GCSx runtime module that will exist specifically to play games, with no other functionality. The runtime module will be linked to and distributed with a single GCSx game world. This runtime module disables features as noted.
- </P>
- <H2>Menu</H2>
- <P>
- The user can press F12 to view the menubar, which contains all other functions here for easy access. This is the only way to access these functions, making F12 the only key a world cannot use. These functions are also available on startup, until a world is loaded, and become available again if a world quits; the user is also given a warning (which they may disable) about how to use F12 to access the menu, when first starting a world. This menu is not available in the runtime module.
- </P>
- <H2>Help</H2>
- <P>
- This brings up GCSx-related help. This feature is not available in the runtime module.
- </P>
- <H2>Load World</H2>
- <P>
- This brings up a list of available worlds. If a world is selected, it is loaded and gameplay begins from the start. The user is first given a warning about how to use F12. This feature is not available in the runtime module.
- </P>
- <H2>System Settings</H2>
- <P>
- This opens a dialog where the user may select a video mode, windowed or full-screen, and set music and sound volumes. Other system settings may be available as well. This feature is not available via menu in the runtime module, although a world may request this dialog manually, or change these settings via functions.
- </P>
- <H2>Enter Editor</H2>
- <P>
- This starts the GCSx editor. This feature is not available in the runtime module.
- </P>
- <H2>Quit</H2>
- <P>
- This exits GCSx completely. The user is asked for confirmation if currently playing a game. This feature is not available via menu in the runtime module- a world must use the quit() function to exit. If the user attempts to close the runtime module in an OS-related manner (such as trying to close the application window) the game exits immediately. This behavior may be changed to allow a game to prompt the user to save.
- </P>
- <H2>Debug Console</H2>
- <P>
- This opens a debugging console. The console displays pertinent information, and allows you to run GCSx commands, view variables, and perform other debugging tasks. This feature is not available in the runtime module.
- </P>
- <H1>17) Additional Functions</H1>
- <P>(...todo...)</P>
- <P>(may or may not be separate libraries or built-in)</P>
- <P>string: instr tolower toupper replace remove regexp</P>
- <P>math: abs cos acos log sin asin sqrt tan atan random round</P>
- <P>array: find remove insert sort append push pop shift unshift subarray split merge</P>
- <P>video: list available modes, select mode, change between windowed and fullscreen</P>
- <P>file: fileopen(filename), fileread(id), filewrite(id, value), fileclose(id), filetime(filename); writes and reads a GCSx-specific format; can also read plain-text files one line at a time if first line is GCSDATA; can only read files in same directory; cannot overwrite files that already exist and aren't a GCSx-format file</P>
|