Scripting: Difference between revisions

From EDukeWiki
Jump to navigation Jump to search
 
(163 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{EDuke32 Intro}}
<div class="floatright">__TOC__</div>
==About This Guide==
==About This Guide==
<div class="floatright">__TOC__</div>
===Intro===
===Intro===
This guide will get you started with the basic aspects of EDuke32's commands -- those which set it apart from vanilla DN3D.  This guide makes no assumptions regarding the programming experience of the reader, other than that the reader comprehends and understands the basic CON system as was seen in Duke Nukem 3D 1.3d-1.5.
This guide will get you started with the basic aspects of EDuke32's commands -- those which set it apart from vanilla DN3D.  This guide assumes both that the reader comprehends and understands the basic CON system as was seen in Duke Nukem 3D 1.3D/1.5, and that the reader is familiar with a few basic programming concepts.


===Con Basics===
===CON Basics===
As stated, this guide assume that you are familiar with the original CON code from Duke3D.  If you are not already familiar with the default commands, the authors of this guide recommend the following guide to the basics, imported into the EDukeWiki for your convenience:
*full command list
**[[Full command list|alphabetized]]
**[[:Category:All commands|categorized]]
If you are not already familiar with the default commands, the authors of this guide recommend the following guide, imported into the EDukeWiki for your convenience:
* [[Confaq42|Con FAQ 4.2 by Jonah Bishop (note: some sections have been shuffled around a bit by TX)]]  
* [[Confaq42|Con FAQ 4.2 by Jonah Bishop (note: some sections have been shuffled around a bit by TX)]]  
** [http://rtcm.thecomitatus.com/knowledge-base/downloads-rtcm/duke3d-faqs-con/confaq42.zip Download stand-alone zipped version (original version from 1999 in .txt format)]


Another good source of basic information is the following FAQ:
Other good sources of basic information are the following FAQs:
* [http://rtcm.thecomitatus.com/knowledge-base/downloads-rtcm/duke3d-faqs-con/H_CONFAQ.zip Con Editing Information v1.3 by James Ferry]
* [http://dukertcm.com/knowledge-base/downloads-rtcm/duke3d-faqs-con/H_CONFAQ.zip Con Editing Information v1.3 by James Ferry]
* [http://dukertcm.com/knowledge-base/downloads-rtcm/duke3d-faqs-con/conedit.zip Con Editing FAQ v2.0 by Joris B. Weimar]
 
==CON Mechanics==
 
===Gamevars===
 
====Overview====
 
[[Gamevar]]s are the heart and soul of modern EDuke32 CON scripting.  Gamevars are signed 32-bit fixed-point integer variables, a ubiquitous staple of programming languages. Gamevars are used to store, manipulate, and compare various numerical information.  Prior to gamevars' introduction in [[WWII GI]], the only usable alternatives were manipulations of inventory item counters and move commands.
 
====Scope====
There are three scopes of gamevars.
* '''Global variable''': If a variable is declared as global, there is only one copy of it which may be accessed by any actor in the game.
 
For example:
 
  gamevar shots_fired 0 0 // gamevar declarations occur outside of states, events, and actor code
 
This line tells the game to create a variable named "shots_fired" which is initialized to 0.  But wait -- what's the second 0 for?  If you're asking yourself this question, go back and read the actual page on [[gamevar]]s.  Now that you know that gamevars have flags which must be declared along with the var's name and value, let's continue.
 
So, we've created a gamevar.  Now what?  For this example, we'll find the places in the GAME.CON code where the command "shoot SHOTSPARK1" occurs, and in each instance add the line "addvar shots_fired 1", so that in each case it looks like this:
 
  shoot SHOTSPARK1
  addvar shots_fired 1 // increments the counter
 
This will make the shots_fired var increment each time an enemy fires a SHOTSPARK1 projectile.
 
* '''Per-player variable''': If a variable is declared as per-player, there is one copy of it for each player in the game, meaning that the variable is set independently for each player.  If a player performs an action that triggers a per-player variable change within an event or the APLAYER actor code, it will only change for the player that initiated that action.  If an actor changes a per-player variable, it will change for the closest player to the actor what changed it.  You can use the [[setplayervar]] command to set a per-player var for a particular player who is not the closest one, but this is only necessary in multiplayer games (and even then it is seldom necessary).  In single player games, there is little practical difference between a global gamevar and a per-player variable.
 
* '''Per-actor variable''': If a variable is declared as per-actor, then there is separate copy of it for each sprite in the game.  That means that every monster, projectile, player, decorative sprite -- pretty much anything in the game that isn't a wall or sector -- has its own copy of the variable.  This is useful if you want to have actors other than the player store information for more than one [[tic]].  For example, suppose you want each monster to keep track of how many bullets it has fired:


==Gamevars==
  gamevar shots_fired 0 2


===About===
This line tells the game that there is a variable named "shots_fired" which is initialized to 0 ( the 0 ) and is per-actor( the 2 ).
[[Gamevar|Gamevars]] were introduced in [[WW2GI]] and remain the most important aspect of the new commandset.  Gamevars allow you to store, manipulate, and execute code based on defined valuesPrior to gamevars, the only usable alternatives were manipulations of inventory item counters and were, as you can imagine, very limited.
With the gamevar declared, you could then add code to the monsters to increment the var when firing shots (just like in the example above for global gamevars)Since the gamevar is per-actor, the same code would now increment the variable separately for each monster.


===Types===
'''There are also some [[pre-defined gamevars]] and special [[constantly updated gamevars]] which should only be used in certain ways.'''
There are three basic types of gamevar, each type storing a signed 32-bit fixed-point integer.  The three basic types are as follows:
* Global variable: A global variable will be the same for any actor it is used in. Changing a global variable changes it for all actors in the game.
* Per-player variable: A per-player variable may be set independently for each player in the game.  If a player performs an action that triggers a per-player variable change within an event or the APLAYER actor code, it will only change for the player that initiated that action.  If an actor changes a per-player variable, it will change for the closest player to the actor that changed it.
* Per-actor variable: A per-actor variable may be independently assigned to each copy of an actor in the game.


===Use and manipulation===
====Use and manipulation====


Custom variables are defined with the [[gamevar]] command, and the term gamevar is therefore used to describe custom variables in EDuke32.
Custom variables, or "gamevars" are declared with the [[gamevar]] command.


The syntax for defining a gamevar is:
The syntax for defining a gamevar is:
::gamevar <varname> <value> <flags>
:gamevar <varname> <value> <flags>


You may use upper or lowercase letters for <varname>.  EDuke32 is case-sensitive, so a var named "EXAMPLE1" is different from "example1."  Variable names should not begin with a digit.  
You may use upper or lowercase letters for <varname>.  EDuke32 is case-sensitive, so a var named "EXAMPLE1" is different from "example1."  Variable names should not begin with a digit.  


The <value> of the gamevar may be positive or negative.
The <value> of the gamevar may be positive or negative and must be a whole number.


The <flags> may be set to either 0 (global), 1 (per-player), or 2 (per-actor).
The <flags> may be set to either 0 (global), 1 (per-player), or 2 (per-actor).


Manipulation of gamevars is accomplished by using a variety of commands that range in functionality from simple mathematical functions to grabbing internal values for manipulation in the CONs. Here are a few of the more common variable manipulation primitives:
*[[ifvar conditions]]
*[[gamevar operators]]
 
===Members of game structures===
 
*[[structure access]]
*[[full list of structure members]]
EDuke32, like prior EDuke iterations, gives you access to game structures--basic elements of the game such as the player, sprites, walls, and sectors--and their predefined property variables, or members.


*[[addvar]], [[addvarvar]]
The most common of these structures is that of the veritable '''actor'''. Objects in the game world, such as monsters, missiles, bits of debris, and so on, are referred to as "sprites" (because they are typically rendered as 2D pictures).  Sprites that execute code are usually referred to as "actors". Every sprite has its own data structure containing all of the member elements in the list linked to above.  Various original CON commands, such as "cstat", "spritepal", "sizeat", etc. work by changing values in the sprite structure of the actor executing the command.  These structure members can be modified directly in an EDuke32 script by using the [[setactor]] command.  Commands like setactor enable us to modify every member of the structure, even those that could not be accessed with the original scripting system. See [[getactor]] and [[setactor]] to see how these commands are used.
*[[divvar]], [[divvarvar]]
*[[mulvar]], [[mulvarvar]]
*[[randvar]], [[randvarvar]]
*[[subvar]], [[subvarvar]]
*[[setvar]], [[setvarvar]]


And some of the more rarely used ones:
The members of the '''player''' structure are controlled using the [[getplayer]] and [[setplayer]] commands.  The player structure members deal with properties unique to the player which are not shared with other actors.  These include variables associated to the players weapons, inventory items, special animations, jumping counters, etc.  See the complete list of [[members of the player structure]] for details.  [[Make Nightvision light rooms|This example]] shows how to get a member of the player structure into a variable, check its value, then alter a member of the player structure accordingly.


*[[andvar]], [[andvarvar]]
The '''tsprite''' structure is an interesting one. It operates on sprites within the context of just the renderer. This means that you can alter the properties of what is displayed without altering the sprite itself. The possibilities are endless with this functionality. It is also useful for intercepting and redirecting hardcoded behavior.
*[[sin]], [[cos]]
*[[orvar]], [[orvarvar]]
*[[shiftvarl]], [[shiftvarr]]
*[[xorvar]], [[xorvarvar]]


Note that a complete list of primitives is, of course, available [[Full_command_list|here]].  The importance of manipulating gamevars will become clear when we get to talking about getting and setting the members of the various structures in the game.  Also note that as of EDuke32 1.3.0, all commands which accept read-only gamevars will also now accept constants in place of them.
The '''input''' structure holds data about what game-defined control functions are being operated.


==Members of game structures==
Check out the pages linked above for a full index of the various structures available in scripting.


===Overview===
===Events===
===Player===
===Actor===
===Sector===
===Wall===


==Events==
*[[EDuke32 event list|events list]]


===Overview===
EDuke32 provides both an object-oriented and a procedural interface to the game's internal workings.  As you already know, the object-oriented part of Duke is the actor system -- in contrast to that, we'll be talking about the event-oriented portion in this section.  As the name suggests, an [[event]] is a block of code that is triggered when a certain event in the game happens.  Events in the game are triggered internally whenever a certain point in the code is reached.
EDuke32 provides both an object-oriented and an event-oriented interface to the game's internal workings.  As you already know, the object-oriented part of Duke is the actor system -- in contrast to that, we'll be talking about the event-oriented portion in this section.  As the name suggests, an [[events|event]] is a block of code that is triggered when a certain event in the game happens.  Events in the game are triggered internally whenever a certain point in the code is reached.


Events are a key component in the manipulation of the game.  Using events, we can do a variety of things such as intercept keypresses, draw to the screen (more on this later), redefine what a player does when certain actions are executed, et cetera.
Events are a key component in the manipulation of the game.  Using events, we can do a variety of things such as intercept keypresses, draw to the screen (more on this later), redefine what a player does when certain actions are executed, et cetera.


===Onevent and endevent===
Using an event is very easy. Similar to the [[actor]] or [[state]] primitives, [[onevent]] is the primitive used to start the definition of a block of code to be associated with one of the [[EDuke32 event list|events]].  Furthering the similarity, onevent must be terminated by the equivalent of an [[enda]] or an [[ends]], an [[endevent]].
[[Onevent]] is the command used to start the definition of a block of code to be associated with one of the [[EDuke32 event list|events]].  Similiar to the [[actor]] keyword, onevent must be terminated by the equivalent of [[enda]], an [[endevent]].


===Special events===
The following example of event code will cause the player to have a full first aid kit every time the inventory is reset (e.g. when the player starts a game):


===A note on synchronization===
onevent EVENT_RESETINVENTORY
Events introduce the ability for CON code to be run in an unsynchronized manner. For example, display events may run a different number of times on individual systems in multiplayer. If you want your mod to function properly in multiplayer, it is important to note whether an event is synchronized or not.  Typically, you'll just want to avoid doing anything that changes the game state (including [[ifrnd]] and [[randvar]]) in display code.
    setplayer[THISACTOR].firstaid_amount 100
endevent


==Drawing to the screen==
===Drawing to the screen===
The ability to properly draw to the screen is a huge improvement over the hackish abominations involving quotes and unused characters that we were forced to endure when using 1.3d and 1.5.  There are several different drawing commands available, ranging in functionality from drawing graphics to the screen to printing text stored in quotes (see the section on string manipulation for more information).
The ability to properly draw to the screen is a huge improvement over the hackish abominations involving quotes and unused characters that we were forced to endure when using 1.3D and 1.5.  There are several different drawing commands available, ranging in functionality from drawing graphics to the screen to printing text stored in quotes (see the section on string manipulation for more information).


===Drawing commands===
*[[rotatesprite]]
*[[rotatesprite]]
*[[gametext]]
*[[screentext]]
*[[minitext]]
 
*[[digitalnumber]]
===Custom projectiles===
*[[myos]] (deprecated)
EDuke32 adds the ability to create new projectiles in addition to the default types. A few basic types exist (shorthand: melee, hitscan (bullets), and visible projectiles like RPGs) which can be used to create a nearly infinite variety of weapon types and even some entirely new effects, such as new types of debris, gibs, clouds of billowing smoke, et cetera.
*[[myospal]] (deprecated)
 
*[[myosx]] (deprecated)
Projectiles are defined via the [[defineprojectile]] command. The syntax for this command is:
*[[myospalx]] (deprecated)
defineprojectile <tilenum> <function> <value>
 
These values can be modified with the [[getprojectile]], [[getthisprojectile]] and [[setprojectile]], [[setthisprojectile]] commands.
 
===String manipulation===
 
[[:Category:String manipulation|Strings]] (lines of text) are called "quotes" in Duke Nukem 3D. String manipulation consists of some simple copying and concatenation commands, as well as the ability to print the text to the screen and to the log file.
 
==Scripting Guidelines==
 
When scripting, there are guidelines that you should follow in order to keep your code clean, concise, correct, and optimized.
 
===Design===
 
*If you are making a mini-mod, consider creating a [[CON mutator]].
 
===Formatting===
*Always comment your code! It will help you and others later, and it will also help keep the code straightforward.
*Indentation is key. Every level of code ("if" and "[[switch]]" statements, loops, and [[:Category:All commands#Block Delimiting|delimited blocks]]) should be indented with four spaces. TAB characters should be avoided.
*[[Label]]s, [[gamevar]]s, [[action]]s, [[move]]s, and [[AI]]s are case-sensitive and cannot start with numbers. Do not use the same names as CON commands.
*<code>if<something> [[nullop]] [[else]] (...)</code> is a common construction used when an if condition lacks a NOT variant.
*Only use '''{''' braces '''}''' for if conditions when you intend to execute more than one command if the condition is true. If you only have one command, do not use braces. Braces tell the CON compiler specifically to branch, which slows down your code a tiny fraction. Another if condition counts as one command.
**The exception is that if you have an [[else]] following multiple non-braced if conditions where it may be ambiguous, you need to add braces to ensure the else is used correctly.
*Because [[event]]s can be defined multiple times in an additive fashion, keep separate instances in your own organization scheme based on the '''function''' of your events. Writing a con file named '''events.con''' is HIGHLY discouraged.
 
===Methodology===
*Not all "temp" variables are created equal. [[Gamevar]]s used only to perform a mathematical calculations and not to store values afterwards should be declared GLOBAL to save memory. Remember, the game VM that runs CON code will always be single-threaded by nature. If you are using a [[gamevar]] to store a value to be preserved between [[tic]]s, simply to save it for use later, or for a construct like a counter, it should be declared PERACTOR.


==Custom projectiles==
==Links==
===Defining custom projectiles===
*full command list
===Manipulating custom projectiles mid-game===
**[[Full command list|alphabetized]]
**[[:Category:All commands|categorized]]
*[[gamevar]]s
*[[pre-defined gamevars]]
*[[constantly updated gamevars]]
*[[full list of structure members|structure members]]
*[[EDuke32 event list|event list]]
*[[:Category:Screen drawing commands|screen drawing commands]]
*[[defineprojectile|projectile information]]
*[[Tutorials]]


==String manipulation==
[[Category:Scripting documentation]]
===Quote redefinition mid-game===
===Copying and concatenation===
===Dynamic quotes===

Latest revision as of 11:58, 31 January 2014

EDuke32 Scripting

About This Guide

Intro

This guide will get you started with the basic aspects of EDuke32's commands -- those which set it apart from vanilla DN3D. This guide assumes both that the reader comprehends and understands the basic CON system as was seen in Duke Nukem 3D 1.3D/1.5, and that the reader is familiar with a few basic programming concepts.

CON Basics

If you are not already familiar with the default commands, the authors of this guide recommend the following guide, imported into the EDukeWiki for your convenience:

Other good sources of basic information are the following FAQs:

CON Mechanics

Gamevars

Overview

Gamevars are the heart and soul of modern EDuke32 CON scripting. Gamevars are signed 32-bit fixed-point integer variables, a ubiquitous staple of programming languages. Gamevars are used to store, manipulate, and compare various numerical information. Prior to gamevars' introduction in WWII GI, the only usable alternatives were manipulations of inventory item counters and move commands.

Scope

There are three scopes of gamevars.

  • Global variable: If a variable is declared as global, there is only one copy of it which may be accessed by any actor in the game.

For example:

  gamevar shots_fired 0 0 // gamevar declarations occur outside of states, events, and actor code

This line tells the game to create a variable named "shots_fired" which is initialized to 0. But wait -- what's the second 0 for? If you're asking yourself this question, go back and read the actual page on gamevars. Now that you know that gamevars have flags which must be declared along with the var's name and value, let's continue.

So, we've created a gamevar. Now what? For this example, we'll find the places in the GAME.CON code where the command "shoot SHOTSPARK1" occurs, and in each instance add the line "addvar shots_fired 1", so that in each case it looks like this:

  shoot SHOTSPARK1
  addvar shots_fired 1 // increments the counter

This will make the shots_fired var increment each time an enemy fires a SHOTSPARK1 projectile.

  • Per-player variable: If a variable is declared as per-player, there is one copy of it for each player in the game, meaning that the variable is set independently for each player. If a player performs an action that triggers a per-player variable change within an event or the APLAYER actor code, it will only change for the player that initiated that action. If an actor changes a per-player variable, it will change for the closest player to the actor what changed it. You can use the setplayervar command to set a per-player var for a particular player who is not the closest one, but this is only necessary in multiplayer games (and even then it is seldom necessary). In single player games, there is little practical difference between a global gamevar and a per-player variable.
  • Per-actor variable: If a variable is declared as per-actor, then there is separate copy of it for each sprite in the game. That means that every monster, projectile, player, decorative sprite -- pretty much anything in the game that isn't a wall or sector -- has its own copy of the variable. This is useful if you want to have actors other than the player store information for more than one tic. For example, suppose you want each monster to keep track of how many bullets it has fired:
  gamevar shots_fired 0 2

This line tells the game that there is a variable named "shots_fired" which is initialized to 0 ( the 0 ) and is per-actor( the 2 ). With the gamevar declared, you could then add code to the monsters to increment the var when firing shots (just like in the example above for global gamevars). Since the gamevar is per-actor, the same code would now increment the variable separately for each monster.

There are also some pre-defined gamevars and special constantly updated gamevars which should only be used in certain ways.

Use and manipulation

Custom variables, or "gamevars" are declared with the gamevar command.

The syntax for defining a gamevar is:

gamevar <varname> <value> <flags>

You may use upper or lowercase letters for <varname>. EDuke32 is case-sensitive, so a var named "EXAMPLE1" is different from "example1." Variable names should not begin with a digit.

The <value> of the gamevar may be positive or negative and must be a whole number.

The <flags> may be set to either 0 (global), 1 (per-player), or 2 (per-actor).

Members of game structures

EDuke32, like prior EDuke iterations, gives you access to game structures--basic elements of the game such as the player, sprites, walls, and sectors--and their predefined property variables, or members.

The most common of these structures is that of the veritable actor. Objects in the game world, such as monsters, missiles, bits of debris, and so on, are referred to as "sprites" (because they are typically rendered as 2D pictures). Sprites that execute code are usually referred to as "actors". Every sprite has its own data structure containing all of the member elements in the list linked to above. Various original CON commands, such as "cstat", "spritepal", "sizeat", etc. work by changing values in the sprite structure of the actor executing the command. These structure members can be modified directly in an EDuke32 script by using the setactor command. Commands like setactor enable us to modify every member of the structure, even those that could not be accessed with the original scripting system. See getactor and setactor to see how these commands are used.

The members of the player structure are controlled using the getplayer and setplayer commands. The player structure members deal with properties unique to the player which are not shared with other actors. These include variables associated to the players weapons, inventory items, special animations, jumping counters, etc. See the complete list of members of the player structure for details. This example shows how to get a member of the player structure into a variable, check its value, then alter a member of the player structure accordingly.

The tsprite structure is an interesting one. It operates on sprites within the context of just the renderer. This means that you can alter the properties of what is displayed without altering the sprite itself. The possibilities are endless with this functionality. It is also useful for intercepting and redirecting hardcoded behavior.

The input structure holds data about what game-defined control functions are being operated.

Check out the pages linked above for a full index of the various structures available in scripting.

Events

EDuke32 provides both an object-oriented and a procedural interface to the game's internal workings. As you already know, the object-oriented part of Duke is the actor system -- in contrast to that, we'll be talking about the event-oriented portion in this section. As the name suggests, an event is a block of code that is triggered when a certain event in the game happens. Events in the game are triggered internally whenever a certain point in the code is reached.

Events are a key component in the manipulation of the game. Using events, we can do a variety of things such as intercept keypresses, draw to the screen (more on this later), redefine what a player does when certain actions are executed, et cetera.

Using an event is very easy. Similar to the actor or state primitives, onevent is the primitive used to start the definition of a block of code to be associated with one of the events. Furthering the similarity, onevent must be terminated by the equivalent of an enda or an ends, an endevent.

The following example of event code will cause the player to have a full first aid kit every time the inventory is reset (e.g. when the player starts a game):

onevent EVENT_RESETINVENTORY
    setplayer[THISACTOR].firstaid_amount 100
endevent

Drawing to the screen

The ability to properly draw to the screen is a huge improvement over the hackish abominations involving quotes and unused characters that we were forced to endure when using 1.3D and 1.5. There are several different drawing commands available, ranging in functionality from drawing graphics to the screen to printing text stored in quotes (see the section on string manipulation for more information).

Custom projectiles

EDuke32 adds the ability to create new projectiles in addition to the default types. A few basic types exist (shorthand: melee, hitscan (bullets), and visible projectiles like RPGs) which can be used to create a nearly infinite variety of weapon types and even some entirely new effects, such as new types of debris, gibs, clouds of billowing smoke, et cetera.

Projectiles are defined via the defineprojectile command. The syntax for this command is:

defineprojectile <tilenum> <function> <value>

These values can be modified with the getprojectile, getthisprojectile and setprojectile, setthisprojectile commands.

String manipulation

Strings (lines of text) are called "quotes" in Duke Nukem 3D. String manipulation consists of some simple copying and concatenation commands, as well as the ability to print the text to the screen and to the log file.

Scripting Guidelines

When scripting, there are guidelines that you should follow in order to keep your code clean, concise, correct, and optimized.

Design

  • If you are making a mini-mod, consider creating a CON mutator.

Formatting

  • Always comment your code! It will help you and others later, and it will also help keep the code straightforward.
  • Indentation is key. Every level of code ("if" and "switch" statements, loops, and delimited blocks) should be indented with four spaces. TAB characters should be avoided.
  • Labels, gamevars, actions, moves, and AIs are case-sensitive and cannot start with numbers. Do not use the same names as CON commands.
  • if<something> nullop else (...) is a common construction used when an if condition lacks a NOT variant.
  • Only use { braces } for if conditions when you intend to execute more than one command if the condition is true. If you only have one command, do not use braces. Braces tell the CON compiler specifically to branch, which slows down your code a tiny fraction. Another if condition counts as one command.
    • The exception is that if you have an else following multiple non-braced if conditions where it may be ambiguous, you need to add braces to ensure the else is used correctly.
  • Because events can be defined multiple times in an additive fashion, keep separate instances in your own organization scheme based on the function of your events. Writing a con file named events.con is HIGHLY discouraged.

Methodology

  • Not all "temp" variables are created equal. Gamevars used only to perform a mathematical calculations and not to store values afterwards should be declared GLOBAL to save memory. Remember, the game VM that runs CON code will always be single-threaded by nature. If you are using a gamevar to store a value to be preserved between tics, simply to save it for use later, or for a construct like a counter, it should be declared PERACTOR.

Links