How to make a new entity type from an exsisting one.
Lets make a brief for this quick tut.
Brief: to make an entity that moves to different entitys as set in the entity inspector window using keys of "target", "target1" etc etc and has a model if specified. Also allowing you to specify a time in seconds to wait at each entity.
Well we already have an entity that will do most of the work for us, a Func_mover, its got script commands to be able to move it around and also includes a model if specified, so lets base our new one off of that.
Lets have a quick look at the def file for a func_mover, found in func.def.
Code:
entityDef func_mover {
"editor_color" "0 .5 .8"
"editor_mins" "?"
"editor_maxs" "?"
"editor_usage" "Movers are objects meant to be controlled through scripts."
"editor_snd snd_accel" "sound to play when acceleration starts. Played on SND_CHANNEL_BODY & SND_CHANNEL_BODY2."
"editor_snd snd_move" "sound to play when movement starts. Played on SND_CHANNEL_BODY & SND_CHANNEL_BODY2."
"editor_snd snd_decel" "sound to play when deceleration starts. Played on SND_CHANNEL_BODY & SND_CHANNEL_BODY2."
"editor_var accel_time" "how long it takes the mover to accelerate to full speed."
"editor_var decel_time" "how long it takes the mover to deccelerate to a stop."
"editor_var move_time" "how long it takes the mover to complete a move. cannot be used with 'move_speed'."
"editor_var move_speed" "how many units the mover travels per second. cannot be used with 'move_time'."
"editor_var solidForTeam" "whether mover is solid to objects bound to it, or objects it is bound to."
"editor_var damage" "how much damage to cause entities that block mover."
"editor_var solid" "whether the mover is solid to other entities."
"editor_var nopush" "set to 1 to disable pushing objects that block the mover."
"editor_var guiTarget" "GUI's associated with mover."
"editor_var health" "when non-zero, how much damage to take before triggering targets."
"spawnclass" "idMover"
}
Heres a list of what these key/values do.
Code:
Editor settings for this entity
"editor_color" "R G B" color of the entity in the editor orthagonal view where R,G and B are red green
and blue value divided 255 (so number ranges from 0 to 1)
"editor_mins" "#" size of the bounding box in orthagonal view, where # is a value in game units..
"editor_maxs" "#" ..if # is ? then bounding box is the size of brush before applying entity.
"editor_showangle" "1" orthagonal view displays arrow showing facing direction
"editor_rotatable" "1" can use editor rotate tools on this entity
"editor_light" "1" can use the light inspector window with this entity
"editor_material" "mtr name" texture that shows on entity in cam window e.g like triggers show common/trigger
"editor_ragdoll" "1" ? maybe somthing to do with the save ragdolls command , not sure
"editor_model" "model name" displays this model in the orthagonal view when placing this entity
"editor_combatnode" "1" uses the fov,max and offset keys to display cone, used on ai_attackcone entitys
Editor settings for entity inspector window
All of these should be followed by "text" where text is the description for the key displayed in the inspector top window.
"editor_usage#" usage note in entity inspector top window, if # is used (being 1,2,3 etc) it displays
text on a new line
"editor_model <key name>" once placed in the bottom window clicking on the value will display a ...
box that opens the model selection screen
"editor_snd <key name>" as above but displays sound selection screen
"editor_gui <key name>" as above but displays gui selection screen
"editor_mat <key name>" as above but displays material selection screen
"editor_color <key name>" as above but displays color selection screen
"editor_bool <key name>" treats this keys value as numerical can access this keys value from scripts using
getIntKey
"editor_var <key name>" treats this keys value as a string allowing you access from script by getFloatKey,
getVectorKey, getKey and getEntityKey
"editor_copy#" "<key name>" copys a key value from its def or from inherited def into the inspector bottom window.
This key is not followed by a text description
Others
"<key name>" "<value>" key and value to be placed on entity at spawn time
"scriptobject" "<script name>" name of script to run for this entity
"spawnclass" "<class name>" class type the engine will use to spawn this entity
"inherit" "<entity def name>" entity to inherit key/values from
Ok now we got that information under our belt lets make our entityDef for our entity.
def/func_locator.def( cant think of a better name at the moment )
Code:
entityDef func_locator {
"editor_color" "0 0 1"
"editor_mins" "?"
"editor_maxs" "?"
"editor_usage" "entity that moves to different locations"
"editor_var target" "Entity to move to, can be given several by using target1, target2 etc etc"
"editor_model model" "Model to use for this locator"
"editor_var waitAt" "Time in seconds to wait at target, use waitAt for target, waitAt1 for target1 etc etc"
"inherit" "func_mover"
"scriptobject" "func_locator"
}
Well thats our def made. If you were to load up the editor now and right click in the orthagonal view you will find our new entity under func's. On placing this entity in a map you should find that the entity inspector window shows all our information on our new entity. If you were to try runnig a map with this entity in it though, it will spit an error out at you complaining about scriptobject on func_locator not found so lets sort that out next.
Giving our entity something to do, a scriptobject.
We can basically split a scriptobject up into 3 parts, the first is our #defines the second is our object and the third is all our functions.
Defines.
Written like
Code:
#define MYDEFINE 1
Basically all this does is everywhere in our script that the engine finds MYDEFINE it will replace it with a 1, so these can also be thought of as constants as this value is unchangable. You can also include little scripts this way aswell, an example of which you can find in doom_defs.script
Code:
#define DEG2RAD( a ) ( (a) * (M_PI / 180.0f ) )
instead of having to type that formula out every where you need it, you can just say sys.print(DEG2RAD(20)); for example.
Object.
This holds all function definitions telling it what variable names and types the functions expect to recieve and what variable type to return. It also defines our global variables. An object can also inherit these things from other objects much like we did with our def.
To make our object we write it like so.
Code:
object <object name>:<inherit from object name> {
//functions and variables definitions in here
};
Thats enough, lets write our object for our func_locator.
script/func_locator.script
Code:
object func_locator { //no inheritance as we dont need it
//variables. All objects need at least one global variable to function properly
entity this; //define our this entity variable
//functions
void init (); //define our init function
void idle (); //define our idle function
};
void func_locator::init() { //this function is like main in map scripts it is where our script starts
this = sys.getEntity( getName()); //reference to this scripts entity
idle(); //start idle function
}
void func_locator::idle() {
while( 1 ){ //this script runs for ever
float loop = 0; //reset our loop variable
for( loop; loop < numTargets(); loop++ ){ //loop through all our target keys
entity locationEntity = getTarget( loop ); //get the entity our target key holds
moveTo( locationEntity ); //move to the entity
sys.waitFor( this ); //wait for move to finish
float waitsec; //set up a variable to hold our wait time
if( loop > 0 ){ //first target is called target with no number..
waitsec = getFloatKey( "waitAt"+loop ); //get waitAt# for target number#
}else{
waitsec = getFloatKey( "waitAt" ); //if first target drop the #
}
if( waitsec ){ //make sure theres a valid number of seconds
sys.wait( waitsec ); //wait given time
}
}
}
}
You may notice if you are used to writting map scripts that we have some commands in here with no entity specified before them, this means these commands will be executed on the scriptobjects entity (in this case our func_locator).
All that leaves us to do is put the line
Code:
#include "script/func_locator.script"
in the doom_main.script file or in the map script of every map that uses this entity.
And build a test map.
Create a large room with a light, put in several target_nulls, put in a func_locator and choose a model for it if you wish. Deselect everything then select your func_locator then in the order you wish it to visit select each target_null then press ctrl+k, this should place them all in the func_locator's key boxes in the entity inspector, add any waitAt# keys and values for targets you would like it to wait at, save bsp and load the map and watch it fly around.
Pretty usless really.
If you have any trouble with the above you can get all the files here in one pk4.
http://www.btinternet.com/~tangentizer/locator.pk4
It includes def, script and map called locator_test.map. Place the
locator.pk4 file in your mod folder (base if this is where you make your maps), load your mod and type 'map locator_test' in the console.
Hope this helps. Have fun.