VERC: Dynamic Lighting Last edited 4 years ago2019-04-21 11:54:32 UTC

You are viewing an older revision of this wiki page. The current revision may be more detailed and up-to-date. Click here to see the current revision of this page.
Ever noticed that when a monster attacks (human grunts for example), only models will light up? Well, that doesn't seem very realistic does it? So I'll show you how to make monsters attacks light up world brushes. This uses a temporary entity called TE_DLIGHT, for more information on temporary entities, go here.

TE_DLIGHT Setup

First you'll want to know about what does this, a temporary entity called TE_DLIGHT. This is the dynamic light entity and if you search the SDK, you'll notice it in the alien slave. Here's how its set up:
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_DLIGHT );
    //WRITE_SHORT( entindex() ); // entity, attachment
    WRITE_COORD( pev->origin.x );     // origin
    WRITE_COORD( pev->origin.y );
    WRITE_COORD( pev->origin.z );
    WRITE_BYTE( 16 );     // radius
    WRITE_BYTE( 128 );     // R
    WRITE_BYTE( 0 );     // G
    WRITE_BYTE( 128 );     // B
    WRITE_BYTE( 10 );     // life * 10
    WRITE_BYTE( 32 ); // decay
MESSAGE_END();
You may want to note that TE_DLIGHTs are always set up this way.

The WRITE_BYTE just states what temporary entity you want, in this case, a TE_DLIGHT (dynamic light). There are other temporary entities such as the TE_BEAMCYLINDER, which is used for the houndeyes attack, but they are set up differently.

The WRITE_SHORT is no longer used, it is used by TE_ELIGHTs (entity lights), but Valve removed it. If you do use it, the DLL will compile fine, but an error will pop up in-game saying its an illegal message. So to put it basically, DONT USE IT.

The three WRITE_COORDs are used for the coordinates in the map for where the effect will take place. The pev->origin.x-z will place this in the center of the entity its assigned to (alien grunt, garg, whatever). If you put zeros for each one, the effect would appear at the origin of the level.

The first WRITE_BYTE specifies the radius of the light, while the next three are RGB values (in this example it makes a purple light). The one after that specifies how long the effect will 'live' (milliseconds I think). The final WRITE_BYTE is the decay rate, this affects how long the light will fade out.

Implementing the TE_DLIGHT

So now you know what the values in the DLIGHT are and how to set it up, let's implement it into a few monsters. You will notice that the WRITE_COORDs will differ monster to monster.

Hint: Temporary Entities are set apart from other parts of code with their TE_ (which stands for Temporary Entity)

Barney

Let's try this on our good pal, barney. Open up barney.cpp and scroll down to the BarneyFirePistol function. Right under the m_cAmmoLoaded--, add this:
// Teh_Freak: World Lighting!
    MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
        WRITE_BYTE( TE_DLIGHT );
        WRITE_COORD( vecShootOrigin.x ); // origin
        WRITE_COORD( vecShootOrigin.y );
        WRITE_COORD( vecShootOrigin.z );
        WRITE_BYTE( 16 );     // radius
        WRITE_BYTE( 255 );     // R
        WRITE_BYTE( 255 );     // G
        WRITE_BYTE( 128 );     // B
        WRITE_BYTE( 5 );     // life * 10
        WRITE_BYTE( 32 ); // decay
    MESSAGE_END();
// Teh_Freak: World Lighting!
Most of this setup should be self-explanitory, except for the WRITE_COORDs. you'll notice that they are vectors. The vector vecShootOrigin was initialized at the top of the function, so I decided to use it.

Hint: If you don't know what to put in the WRITE_COORDs, just put pev->origin.x-z, this will put it in the center of the entity, which looks just as good.

You may also want to notice where you put the DLIGHT. Putting DLIGHTs in certain areas will affect when it will turn on. For example, you want the Garg's flame attack to light up, you would put it in the FlameUpdate function. It's also a good idea that if there are ELIGHTs, you make the values match up (remember, DLIGHTs don't use the WRITE_SHORT, but ELIGHTs do).

Alien Grunt

Let's implement this into the alien grunt. This is done a little differently than Barney of HGrunts. First, open up agrunt.cpp and scroll down to the HandleAnimEvent function. After the TE_SPRITE setup, add this:
// Teh_Freak: World Lighting!
    MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
        WRITE_BYTE( TE_DLIGHT );
        WRITE_COORD( vecArmPos.x );     // origin
        WRITE_COORD( vecArmPos.y );
        WRITE_COORD( vecArmPos.z );
        WRITE_BYTE( 16 );     // radius
        WRITE_BYTE( 128 );     // R
        WRITE_BYTE( 0 );     // G
        WRITE_BYTE( 128 );     // B
        WRITE_BYTE( 10 );     // life * 10
        WRITE_BYTE( 32 ); // decay
    MESSAGE_END();
// Teh_Freak: World Lighting!
Notice the vectors. If you look at the vectors in the TE_SPRITE, they match up with the vectors here. In case you wanted to know, the TE_SPRITE creates the muzzleflash for the Alien Grunt, so it was a good choice to use the same vectors.

Hint: If there is a TE_ELIGHT or any other kind of temporary entity that uses vectors, place the TE_DLIGHT right after it and use the same vectors (but make sure the DLIGHT is in the right function)

Alien Controller

The Alien Controller is unique for this. Not only will the world around it light up when its about to shoot, but we will make dynamic lights follow the fire balls it shoots.

First, open up controller.cpp and scroll down to the HandlAnimEvent function. You will notice some TE_ELIGHTs, so guess where we will place the DLIGHTs? If you said after the ELIGHT, you were right. So put this after the ELIGHT in the CONTROLLER_AE_HEAD_OPEN event:
// Teh_Freak: World Lighting!
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_DLIGHT );
    WRITE_COORD( vecStart.x ); // origin
    WRITE_COORD( vecStart.y );
    WRITE_COORD( vecStart.z );
    WRITE_BYTE( 10 );     // radius
    WRITE_BYTE( 255 );     // R
    WRITE_BYTE( 192 );     // G
    WRITE_BYTE( 64 );     // B
    WRITE_BYTE( 20 );     // life * 10
    WRITE_BYTE( -32 ); // decay
MESSAGE_END();
// Teh_Freak: World Lighting!
If you look at the ELIGHT and DLIGHT, they are set up pretty much the same. Now scroll down till you see the CONTROLLER_AE_BALL_SHOOT event, and add this after the ELIGHT:
// Teh_Freak: World Lighting!
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_DLIGHT );
    WRITE_COORD( vecStart.x );     // origin
    WRITE_COORD( vecStart.y );
    WRITE_COORD( vecStart.z );
    WRITE_BYTE( 32 );     // radius
    WRITE_BYTE( 255 );     // R
    WRITE_BYTE( 192 );     // G
    WRITE_BYTE( 64 );     // B
    WRITE_BYTE( 10 );     // life * 10
    WRITE_BYTE( 32 ); // decay
MESSAGE_END();
// Teh_Freak: World Lighting!
You should know what does what here. Now scroll down to void CControllerHeadBall :: HuntThink(void).

Hint: If you're going to make a DLIGHT follow an entity like the bullsquids spit or the controllers fire balls, put the DLIGHT in the Think function (it may be called something else, like HuntThink)

Put this after the ELIGHT in the HuntThink function:
// Teh_Freak: World Lighting!
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_DLIGHT );
    WRITE_COORD( pev->origin.x );     // origin
    WRITE_COORD( pev->origin.y );
    WRITE_COORD( pev->origin.z );
    WRITE_BYTE( pev->renderamt / 16 );// radius
    WRITE_BYTE( 255 );     // R
    WRITE_BYTE( 192 );     // G
    WRITE_BYTE( 255 );     // B
    WRITE_BYTE( 2 );     // life * 10
    WRITE_BYTE( 0 ); // decay
MESSAGE_END();
// Teh_Freak: World Lighting!
You may notice the pev->origin.x-z. If there were vectors, I'd still use this, because it is in the center of the ball. You should also not the pev->renderamt / 16. This makes the light slowly fade out with the render amount of the head ball (you will notice that the head ball fades away if you've ever had a controller head ball not hit you).

Almost done, find the AnimateThink function of the ControllerZapBall and add this at the bottom of the function:
// Teh_Freak: World Lighting!
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_DLIGHT );
    WRITE_COORD( pev->origin.x );     // origin
    WRITE_COORD( pev->origin.y );
    WRITE_COORD( pev->origin.z );
    WRITE_BYTE( 8 );     // radius
    WRITE_BYTE( 255 );     // R
    WRITE_BYTE( 192 );     // G
    WRITE_BYTE( 64 );     // B
    WRITE_BYTE( 2 );     // life * 10
    WRITE_BYTE( 0 ); // decay
MESSAGE_END();
// Teh_Freak: World Lighting!
What this will do is make a dynamic light follow the zap ball, lighting up world brushes as it passes them, but entities won't get light up! So for the first (and last) time in this tutorial, we'll add an ELIGHT. Put it after the DLIGHT:
// Teh_Freak: Entity Lighting!
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
    WRITE_BYTE( TE_ELIGHT );
    WRITE_SHORT( entindex( ) );     // entity, attachment
    WRITE_COORD( pev->origin.x );     // origin
    WRITE_COORD( pev->origin.y );
    WRITE_COORD( pev->origin.z );
    WRITE_COORD( 8 );     // radius
    WRITE_BYTE( 255 );     // R
    WRITE_BYTE( 192 );     // G
    WRITE_BYTE( 64 );     // B
    WRITE_BYTE( 2 );     // life * 10
    WRITE_COORD( 0 ); // decay
MESSAGE_END();
// Teh_Freak: Entity Lighting!
You will notice that the ELIGHT and DLIGHT are almost the same, so using it shouldn't be that hard. Well, now compile your DLL, make a cube room with one very faint light (ZHLT will make the room full bright if you have no lights) and start up a map with the three monsters we just edited.

Enjoy DLIGHTing people with some neat shots of your mod using this effect!
This article was originally published on the Valve Editing Resource Collective (VERC).
TWHL only archives articles from defunct websites. For more information on TWHL's archiving efforts, please visit the TWHL Archiving Project page.

Comments

You must log in to post a comment. You can login or register a new account.