The complete megahit game that set the world afire. Plus All-New Episode IV: Thy Flesh Consumed.The demons came and the marines died. Except one. You are the last defense against these hell-spawned hordes. Prepare for the most intense mutant-laden, blood-splattered action ever! The texture-mapped virtual world is so real, you don't just play DOOM - you live it.The Ultimate DOOM takes you beyond anything you've ever experienced. First, you get all three original episodes - that's 27 levels of awesome, explosive excitement. Then it really blows you away with an all-new episode: Thy Flesh Consumed. Now you're dead meat. Just when you think you're getting pretty good at DOOM, you get hit with Perfect Hatred, Sever the Wicked and seven other expert levels never seen before! They're so incredibly tough, the first 27 levels will seem like a walk in the park!

Post tutorial Report RSS Doom Source Code Tutorial 13

Modding the Monsters in Doom Part 6 - The Cyberdemon. This Cyborg can now fire heat seeking guided rockets.

Posted by on - Basic Client Side Coding

Doom Source Code Tutorial 13
Game: Doom and Doom2.
Level: Basic.
Objectives: to demonstrate how easy it is to make a guided missile in doom using existing functions.
Resources required: VC++ 2008 Express Edition; source code for Doom (tested with Doomsday ver 1.9.0-beta6.9).
Introduction: The rockets fired by the Cyborg or the Cyber Demon Lord as it is sometimes known, are coded to shoot in a straight line at the the player and are fairly easy to dodge specially if you are combating this boss from a distance. Let's have some "fun" and make it fire "heat seeking" missiles just like those used in other games. That should test your manoeuvring skills to the limit.
Procedure:
1. We should approach this mod in the usual logical and systematic way. First let's look at the function used in the game to make the Cyborg monster fire its rockets, here it is copied from the file p_enemy.c in jdoom:

void C_DECL A_CyberAttack(mobj_t *actor)
{
if (!actor->target)
return;
A_FaceTarget(actor);
P_SpawnMissile(MT_ROCKET, actor, actor->target);
}

2. As you can see, it is short and simple. No melee attack state and no angle modification is used, so every shot fired by the monster heads straight to its target, providing that this target remains stationary. So what we need is a mechanism that locks this missile onto the target and keeps adjusting or modifying the direction of its travel as it moves towards this target ,should that target changes direction or position on the map.
3. Next step is to examine the entries in the Objects.ded file for the rocket to look for more clues to help us make our mod,let's examine the top segment of the rocket Thing data:

Thing {
ID = "ROCKET";
DoomEd number = -1;
Spawn state = "ROCKET";
See state = "NULL";
Pain state = "NULL";
........................
.................. etc
}

4. So this monster fires one shot that has a spawn state named "ROCKET" at a speed of 20 and a damage rate of 20 amongst other things. Next logical step is to examine the State of this spawned entity called ROCKET to see what useful data it may contain, here it is from the same Objects.ded file:

State {
ID = "ROCKET";
Sprite = "MISL";
Frame = 0;
Flags = fullbright;
Tics = 1;
Next state = "ROCKET";
}

5. It is only one state with a fast 1 tic for one frame. More importantly this also tells us that this missile is static, meaning it lacks an action function. Its object will simply fly through the air until it hits the target or something else and explodes ,and that's it. What we need to make a seeker or guided missile is to add an action function that will make the rocket chase its target and we must embed this function inside the ROCKET State. We can go ahead and write a new function to do just that or we can do it the easy (but not lazy) way and use what already exists if that is possible. This is a beginner's level tutorial and in future articles when we intend to do more advanced stuff we will resort to creating new functions. Meanwhile we can learn a lot by just examining and modifying what already exists. Fortunately for us this time too there is already a function used to do this by the revenant monster and it is called A_Tracer(). I have modified this function a bit and re-named it A_Seeker() so we can use it to make our guided rockets or indeed use it with any other missile-throwing monster weaponry. Place it in the file p_enemy.c , just before A_CyberAttack() :

//Define action function A_Seeker based on the A_Tracer function,
void C_DECL A_Seeker(mobj_t* actor)
{int an;
angle_t angle;
float dist;
float slope;
mobj_t* dest, *th;
if ((int) GAMETIC & 3)
return;
// Adjust direction.
dest = actor->tracer;
//if no target or target is dead then return
if (!dest || dest->health <= 0)
return;
// Change angle.
angle = R_PointToAngle2(actor->pos[VX], actor->pos[VY],
dest->pos[VX], dest->pos[VY]);
if (angle != actor->angle)
{
if (angle - actor->angle > 0x80000000)
{
actor->angle -= TRACEANGLE;
if (angle - actor->angle < 0x80000000)
actor->angle = angle;
}
else
{
actor->angle += TRACEANGLE;
if (angle - actor->angle > 0x80000000)
actor->angle = angle;
}
}
an = actor->angle >> ANGLETOFINESHIFT;
actor->mom[MX] = actor->info->speed * FIX2FLT(finecosine[an]);
actor->mom[MY] = actor->info->speed * FIX2FLT(finesine[an]);
// Change slope.
dist = P_ApproxDistance(dest->pos[VX] - actor->pos[VX],
dest->pos[VY] - actor->pos[VY]);
dist /= actor->info->speed;
if (dist < 1)
dist = 1;
slope = (dest->pos[VZ] + 40 - actor->pos[VZ]) / dist;
if (slope < actor->mom[MZ])
actor->mom[MZ] -= FIX2FLT(FRACUNIT / 8);
else
actor->mom[MZ] += FIX2FLT(FRACUNIT / 8);
}

6. There is quite a lot of code and stuff involved here , I will not attempt to explain every single line because of the depth and scope ,but as we progress through these tutorials, they will be discussed in detail in future articles. All we need to do now is paste this new action function in inside the declaration list in acfnlink.h header file; and add it to the ROCKET state inside the CyberAttack function. We need also to modify this last function so that once the rocket is spawned and given momentum,the cyborg's target is set firmly as the destination for this missile to steer to. See the code below:First we add A_Seeker() to acfnlink.h header file:

void C_DECL A_StartFire();
void C_DECL A_Tracer();
void C_DECL A_Seeker(); //add new function here.
void C_DECL A_TroopAttack();

Then we modify the A_CyberAttack function:

//modified cyber Demon attack function in p_enemy.c, new lines added in red:
void C_DECL A_CyberAttack(mobj_t *actor){
mobj_t *mo;
if (!actor->target)
return;
//add new action function pointer, A_Seeker to the Rocket's state:
STATES[S_ROCKET].action = A_Seeker;
A_FaceTarget(actor);
// add the following modified code :
mo = P_SpawnMissile(MT_ROCKET, actor, actor->target);
if (mo)
{
mo->pos[VX] += mo->mom[MX];
mo->pos[VY] += mo->mom[MY];
mo->tracer = actor->target;
}
}

7. Everything should be fine now. Build your mod copy your jdoom.dll to the bin directory of the game folder and jump straight to level 28 of Doom (use idclev) and do battle with the final boss. I suggest you select the easiest level just to lose those annoying "lost souls" (skulls) as they will just get in your way. You will find a completely different experience this time, because no matter how skillful you are at dodging those rockets they will chase you like hell and very aggressively indeed, even around corners ,if those corner are wide enough; and the only fun you may have now is how fast you can outrun them. Enjoy!
8. As an exercise, may I suggest you repeat this mod with other monsters like the imp or the arachnotron; I am sure you'll love the experience!
Bye for now.
Adam.
Return to Tutorials Page.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: