Combined light entities
Introduction
Many complex objects in game are build from more than one entity. Others consist of only one entity, but represent different things at the same time.
For instance, a candle consists of two entity: the candle stub, and an attached flame object.
The flame object itself consists of only one entity, but this entity both emits light, sound and particles as well as reacts to stimuli like fire and water.
This article deals with the complex entities consisting of more than one entity.
Two ways
There are two ways to attach two entities together:
- the entityDef (inside a .def file) can specify to spawn and attach a new entity when the first entity is spawned. This typical happens at map loading time, but can also happen later in game when a new base entity is spawned. This is from here one called "def_attached" due to the spawnarg being responsible: def_attach.
- the mapper can manually add both entities in the map, and then arrange them in the editor so they look bound together. Additionally, one can use the "bind" or "bindToJoint" spawnargs to make f.i. a key bound to a moving AI. We refer to these ways from here on as "the manual way".
The manual way has several disadvantages but also advantages:
Disadvantages:
- manually arranging the entities is error prone, f.i. often candle flames would hover over the candle, or be slightly offset
- Moving the entities in the map must always move both in sync, or 1. happens.
- when creating a moveable entity, the mapper must also take care that the second entity moves with the first by binding them together manually
- Changing the color of all attached flames is cumbersome, as you need to modify each of them. With the def_attached method, you can simple edit the entityDef file or overwrite this entity with your own definition.
Advantages:
- Both entities are present in the editor, and can thus be seen. (For combined entities, DR will hopefully later automatically show a dashed outline for any automatically attached entities, making this point moot).
- Since the attached entity is already in the editor, its name is known, so it can be easily targeted by scripts and have spawnargs set on it. This is the main disadvantage of def_attaching entities and we see later how we can overcome it.
This article, deals primarily with the def_attach method.
Example entities
Candle and Flame
Here is an example how a flame is attached to a candle:
// attach the light, so the candle can be re-lit by fire stims "def_attach" "light_candleflame_unlit" "pos_attach" "flame" // At the attach point called "flame"... "attach_pos_name_1" "flame" // ... which is defined here. "name_attach" "flame" // Set a name to pass along spawnargs "attach_pos_origin_1" "0 0 15" // Offset the flame X units in the Z direction
The name_attach is the name of the attachment, which will be important for later when setting spawnargs.
Holder and Candle (and Flame)
Here is an example entity attaching a candle (which itself has an attached flame) to a candle holder:
/* skinny tall */ entityDef atdm:moveable_candle_holder_plus_skinny_tall_candle { "inherit" "atdm:moveable_candle_holder_base" "mass" "1.5" "editor_usage" "A candle holder with an unlit tall & skinny candle." // attach the candle "def_attach" "atdm:moveable_candle_skinny_tall" "pos_attach" "candle" // At the attach point called "candle"... "attach_pos_name_1" "candle" // ... which is defined here. "name_attach" "candle" // Set a name to pass along spawnargs "attach_pos_origin_1" "0 0 17.5" // Offset the candle X units in the Z direction }
Modifying the attached entity/entities
When you place a combined entity (f.i. candle+flame) in your map, you only see the base entity, in this case the candle. However, to switch the candle off or on, you would need to link some entity (like a switch or trigger) to the candle flame entity. Likewise, if you want to change the color of the flame, you would need to set the "_color" spawnarg on the flame.
But since the candle flame does not exist yet, you can't. Here is how you can overcome this:
Linking from the holder entity
If you want, for instance, a second light to go on/off, or a door to open when a candle is extinguished, simply select the candle holder, and the second entity and press CTRL + K to link them. The holder will rely on/off signals from itself to the linked entity when the flames are extinguished or relit.
Targeting the (invisible) entity
- This will work from 2.03 onwards. It doesn't work in 2.02.
Simple target the base entity, like the holder or the candle. When triggered, these know how to extingusish or light all attached flames (even tho they don't exist at map load). The holder will then rely the signal to any targeted (either by the holder or by the flames) entities.
Turning lights off/on
Since the attached light entity is not present in your map, you do not know its name, and thus it could be difficult to do simple things like turning the lights on or off. To make this easier, three methods exist to help you:
Script Object
Each base entity has a script object attached, that features the three following methods:
- LightsOn();
- LightsOff();
- LightsToggle();
You can use them in two ways:
Write a script:
entity my_holder = sys.getEntity("Name_of_the_light_holder"); my_holder.LightsOn(); sys.wait(500); my_holder.LightsOff(); sys.wait(500);
Use an atdm:target_callobjectfunction entity:
Create an atdm:target_callobjectfunction entity and give it the following spawnargs:
"call" "LightsOff" "target0" "Name_of_the_light_holder"
Then just trigger that entity with a trigger of your choosing, or target it from a lever etc.
See the article about atdm:target_callobjectfunction about for more details.
Global functions
Es a last resort, there are three special global script functions that you can use to manipulate attached light entities when you only know the base entity:
frob_light_holder_light( holder ); # turn the light on frob_light_holder_ext( holder ); # turn the light off frob_light_holder_toggle_light( holder ); # toggle light (if on => off, if off => on)
Note: These functions are called without an object and take as first argument the base entity.
Example:
void light_on( entity zone ) { entity holder = sys.getEntity( "stone_room_light" ); if (holder) { // We need to tell the holder to turn off the bound light (as we do // not know what the bound light entity is named) frob_light_holder_light( holder ); } // better, shorter and more clean, object-oriented way: if (holder) { holder.LightsOff(); } }
Note: Don't call these functions this way:
holder.frob_light_holder_light(); # WRONG!
Setting spawnargs
Setting a spawnarg on the attached entity is simply done by setting the spawnarg set SPAWNARGNAME on ATTACHMENTNAME on the base entity. SPAWNARGNAME is the name of the spawnarg, while the attachment name can be looked up in the entityDef for the base entity, e.g. in DR by checking the "Show inherited" in the entity inspector.
Here is an example, changing the color of the attached flame:
"set _color on flame" "0.2 0.2 0.9"
In DR, enter the spawnarg name and value without the quotes. The above setting will set the spawnarg '_color' on any entity attached that has the attachement name 'flame' to "0.2 0.2 09", thus coloring the light blue.
The ATTACHMENTNAME is defined as name_attach spawnarg on either the base entity or one of its attached entities.
noshadows_lit
All entities that use the "tdm_light_holder" script object can use the following combination of spawnargs:
For starting lit entities:
"noshadows_lit" "1" "noshadows" "1" "extinguished" "0"
and for starting as unlit entites:
"noshadows_lit" "1" "noshadows" "0" "extinguished" "1"
This makes the entity only cast a shadow when the light is off, the switch is done by the light holder script object. This has the effect that when the player turns on the light, or lights the torch/candle, that the model itself does not cast a shadow, preventing it from casting ugly shadows from its own light. However, if the light is off, then other light sources still cast a shadow from the candle holder. This is a compromise between having no shadows or shadows all the time and looks a lot better than just setting "noshadows" to "1" all the time.
Changing attached entities
Sometimes you want to change the actual attached entity for another variant, but the specific combination does either not exist yet, or you have made a new entity that you want to attach instead.
Now you could create the combination entity from scratch, but that can be cumbersome and error prone. Instead, just swap the attached entity.
Here is how you take a normal combined walltorch+flame and swap the flame for a green variant: Just override the spawnarg "def_attach" with your own version:
"def_attach" "light_torchflame_green" // note: the green torch only exists from TDM 1.01 on
All the other spawnargs have already the right value and can thus be left alone.