Inside3D tutorials.
Created By:
eMail:
Difficulty Scale:


MULTI-ATTACK MONSTERS: STEP 1
Well, I'dactually have to say that this tutorial is pretty hard. But, whensuccessfully done, just about any monster can launch just aboutany weapon there is. So, to ensure a stable working environment,lock all doors, do a quick sound proofing on your room,disconnect all non-computer phone lines, and kill your family.Well, don't kill your family, maybe someone there knows QC :)Anyways, open up your text editor, and geeeet ready..

STEP 2
Now, open upweapons.qc and scroll down to where you see 'PLAYER WEAPON USE'.Now block off a section right above this and put this:

/*
===============================================================================

ENEMY WEAPONS--START CODE

===============================================================================
*/


/*
===============================================================================

ENEMY WEAPONS--END CODE

===============================================================================
*/
You may be thinking 'wellthat was useless.' Well, you're pretty much correct. But, I findit always helps me keep my bearings to have a 'start' and 'end'to work between. So, when you do this tutorial, make sureeverything goes BETWEEN those two things. Not necessary, but muchcleaner. Anyways, Step 1 will be for making monsters fire ashotgun. You can use the same code in army_fire from soldier.qc .So paste this in:

//Enemy Shotgun
void() Enemy_Fire_Shotgun =
{
    local vector dir;
    local entity en;

    ai_face();

    sound (self, CHAN_WEAPON, "soldier/sattck1.wav", 1,ATTN_NORM);
    en = self.enemy;
    dir = en.origin - en.velocity*0.2;
    dir = normalize (dir - self.origin);
    FireBullets (4, dir, '0.1 0.1 0');
};
And while your there, copy and paste the same thing right below it with the changes in RED to do the super shotgun.

//Enemy Super Shotgun
void() Enemy_Fire_SupShotgun =
{
    local vector dir;
    local entity en;

    ai_face();
    sound (self, CHAN_WEAPON, "soldier/sattck1.wav", 1,ATTN_NORM);
    en = self.enemy;
    dir = en.origin - en.velocity*0.2;
    dir = normalize (dir - self.origin);
    FireBullets (12, dir, '0.1 0.1 0');
};
Alright, now all yourmonsters can shoot either shotgun.

STEP 3
This next step will allow monsters to fire the nailgun or super nailgun. Below the Enemy_Fire_SupShotgun code, add the code below.

//Enemy Nailgun
void() Enemy_Fire_Nailgun =
{
    local vector dir;

    sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1,ATTN_NORM);

    dir = normalize (self.enemy.origin - self.origin);
    launch_spike (self.origin + '0 0 16', dir);
};
Alright,there's the nailgun code, now super...

//Enemy SuperNailgun
void() Enemy_Fire_SNailgun =
{
    local vector dir;

    sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1,ATTN_NORM);

    dir = normalize (self.enemy.origin - self.origin);
    launch_spike (self.origin + '0 0 16', dir);
    newmis.touch = superspike_touch;
    setmodel (newmis, "progs/s_spike.mdl");
    setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
};


STEP 4
Now I'll show youhow to add the code for all monsters to shoot lasers, like theenforcer. First though, go up to the void W_Precache, and add theblue lines, like this:
void() W_Precache =
{
    precache_sound ("weapons/r_exp3.wav");
    precache_sound ("weapons/rocket1i.wav");
    precache_sound ("weapons/sgun1.wav");
    precache_sound ("weapons/guncock.wav");
    precache_sound ("weapons/ric1.wav");
    precache_sound ("weapons/ric2.wav");
    precache_sound ("weapons/ric3.wav");
    precache_sound ("weapons/spike2.wav");
    precache_sound ("weapons/tink1.wav");
    precache_sound ("weapons/grenade.wav");
    precache_sound ("weapons/bounce.wav");
    precache_sound ("weapons/shotgn2.wav");

    precache_model("progs/laser.mdl");
    precache_sound("enforcer/enfire.wav");
    precache_sound ("enforcer/enfstop.wav");
};
Now add this:

void()Enemy_Laser_Touch =
{
    local vector org;

    if (other == self.owner)
        return; // don't explode on owner
    if (pointcontents(self.origin) == CONTENT_SKY)
    {
        remove(self);
        return;
    }
    sound (self, CHAN_WEAPON, "enforcer/enfstop.wav", 1,ATTN_STATIC);
    org = self.origin - 8*normalize(self.velocity);

    if (other.health)
    {
        SpawnBlood (org, self.velocity*0.2, 15);
        T_Damage (other, self, self.owner, 15);
    }
    else
    {
        WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
        WriteByte (MSG_BROADCAST, TE_GUNSHOT);
        WriteCoord (MSG_BROADCAST, org_x);
        WriteCoord (MSG_BROADCAST, org_y);
        WriteCoord (MSG_BROADCAST, org_z);
    }
    remove(self);
};

void(vector org, vector vec) Enemy_LaunchLaser =
{
    local vector vec;

    if (self.classname == "monster_enforcer")
        sound (self, CHAN_WEAPON, "enforcer/enfire.wav", 1,ATTN_NORM);

    vec = normalize(vec);
    newmis = spawn();
    newmis.owner = self;
    newmis.movetype = MOVETYPE_FLY;
    newmis.solid = SOLID_BBOX;
    newmis.effects = EF_DIMLIGHT;

    setmodel (newmis, "progs/laser.mdl");
    setsize (newmis, '0 0 0', '0 0 0');
    setorigin (newmis, org);

    newmis.velocity = vec * 600;
    newmis.angles = vectoangles(newmis.velocity);
    newmis.nextthink = time + 5;
    newmis.think = SUB_Remove;
    newmis.touch = Enemy_Laser_Touch;
};


void() Enemy_Fire_Laser =
{
    local vector org;

    self.effects = self.effects | EF_MUZZLEFLASH;
    makevectors (self.angles);
    org = self.origin + v_forward * 30 + v_right * 8.5 + '0 0 16';
    Enemy_LaunchLaser(org, self.enemy.origin - self.origin);
};


STEP 5
Ok, now we'll insert the code for the grenade launcher now.

//Enemy Grenade
void() EnemyGrenadeExplode =
{
    T_RadiusDamage (self, self.owner, 40, world);
    sound (self, CHAN_VOICE, "weapons/r_exp3.wav", 1,ATTN_NORM);

    WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
    WriteByte (MSG_BROADCAST, TE_EXPLOSION);
    WriteCoord (MSG_BROADCAST, self.origin_x);
    WriteCoord (MSG_BROADCAST, self.origin_y);
    WriteCoord (MSG_BROADCAST, self.origin_z);

    self.velocity = '0 0 0';
    self.touch = SUB_Null;
    setmodel (self, "progs/s_explod.spr");
    self.solid = SOLID_NOT;
    s_explode1 ();
};

void() EnemyGrenadeTouch =
{
    if (other == self.owner)
        return; // don't explode on owner
    if (other.takedamage == DAMAGE_AIM)
    {
        EnemyGrenadeExplode();
        return;
    }
    sound (self, CHAN_VOICE, "weapons/bounce.wav", 1,ATTN_NORM); // bounce sound
    if (self.velocity == '0 0 0')
        self.avelocity = '0 0 0';
};

void() Enemy_Fire_Grenade =
{
    local entity missile;

    self.effects = self.effects | EF_MUZZLEFLASH;
    sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1,ATTN_NORM);

    missile = spawn ();
    missile.owner = self;
    missile.movetype = MOVETYPE_BOUNCE;
    missile.solid = SOLID_BBOX;

// set missile speed
    makevectors (self.angles);

    missile.velocity = normalize(self.enemy.origin - self.origin);
    missile.velocity = missile.velocity * 600;
    missile.velocity_z = 200;
    missile.avelocity = '300 300 300';
    missile.angles = vectoangles(missile.velocity);
    missile.touch = EnemyGrenadeTouch;

// set missile duration
    missile.nextthink = time + 2.5;
    missile.think = EnemyGrenadeExplode;

    setmodel (missile, "progs/grenade.mdl");
    setsize (missile, '0 0 0', '0 0 0');
    setorigin (missile, self.origin);
};


STEP 6
Well,we're almost done. Now we just have to add the code for therocket launcher. So, here we go....

//Enemy Rocket
void() T_EnemyMisTouch = 
{
    local float damg;

    if (other == self.owner)
        return; // don't explode on owner
    if (pointcontents(self.origin) == CONTENT_SKY)
    {
        remove(self);
        return;
    }

    damg = 10 + random()*2;

    if (other.health)
    {
        if (other.classname == "monster_shambler")
            damg = damg * 0.5; // mostly immune
        T_Damage (other, self, self.owner, damg );
    }

    T_RadiusDamage (self, self.owner, 10, other); //Changed from 120to 10

    self.origin = self.origin - 8*normalize(self.velocity);

    WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
    WriteByte (MSG_BROADCAST, TE_EXPLOSION);
    WriteCoord (MSG_BROADCAST, self.origin_x);
    WriteCoord (MSG_BROADCAST, self.origin_y);
    WriteCoord (MSG_BROADCAST, self.origin_z);
    BecomeExplosion ();
};

void() W_EnemyRocket = 
{
    local entity missile;

    sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1,ATTN_NORM);
    missile = spawn ();
    missile.owner = self;
    missile.movetype = MOVETYPE_FLYMISSILE;
    missile.solid = SOLID_BBOX;
    missile.classname = "missile";
    missile.velocity = normalize(self.enemy.origin - self.origin);
    missile.velocity = missile.velocity * 600;
    missile.angles = vectoangles(missile.velocity);
    missile.touch = T_EnemyMisTouch;
    missile.nextthink = time + 5;
    missile.think = SUB_Remove;

    setmodel (missile, "progs/missile.mdl");
    setsize (missile, '0 0 0', '0 0 0');
    setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

void() Enemy_Fire_Rocket =
{
    local vector dir;
    local entity en;

    ai_face();

    sound (self, CHAN_WEAPON, "soldier/sattck1.wav", 1,ATTN_NORM);
    en = self.enemy;
    dir = en.origin - en.velocity*0.2;
    dir = normalize (dir - self.origin);

    W_EnemyRocket();
};
Wow....that was loooong...anyways, we're almost done. In fact, you could skipthis next step if you want, but I think it's more fun this way.

STEP 7
Alrighty, in thisstep, we're going to create a void that will randomize the enemies choice of attack, choosing either shotgun, super shotgun,nailgun, super nailgun, grenade launcher, laser, or rocketlauncher.

//EnemyRandomizer
void() Enemy_Weapons_Random =
{
    local float ewrandom;

    ewrandom = random();
    if (ewrandom < 0.16 )
        Enemy_Fire_Shotgun();
    else if ((ewrandom > 0.15) && (ewrandom < 0.32)
        Enemy_Fire_SupShotgun();
    else if ((ewrandom > 0.31) && (ewrandom < 0.48)
        Enemy_Fire_Nailgun();
    else if ((ewrandom > 0.47) && (ewrandom < 0.64)
        Enemy_Fire_SNailgun();
    else if ((ewrandom > 0.63) && (ewrandom < 0.8)
        Enemy_Fire_Grenade();
    else if ((ewrandom > 0.79) && (ewrandom < 0.96)
        Enemy_Fire_Rocket();
    else
        Enemy_Fire_Shotgun();
};


STEP 8
Yes, here we are at Step #8, and STILL working. Now, I'll give you a short little part on how to add this for every weapon firing monster. You can add this to non-shooting monsters, but it'd look really bizzare if a dog or a fiend started shooting rockets out of its mouth :) (Remember, if you have a specific weapon you want the monster to shoot, just replace Enemy_Weapons_Random(); with Enemy_FireShotgun(); or whatever.)

ENFORCER: Lines 163 &167 (of a clean version, if you dont have it clean, just find these lines) read:

void()enf_atk6 =[ $attack6, enf_atk7 ] {enforcer_fire();};
void() enf_atk10 =[ $attack6, enf_atk11 ] {enforcer_fire();};

Change them to:

void()enf_atk6 =[ $attack6, enf_atk7 ] {Enemy_Weapons_Random();};
void() enf_atk10 =[ $attack6, enf_atk11 ]{Enemy_Weapons_Random();};

OGRE: Line256:

void()ogre_nail4 =[ $shoot3, ogre_nail5 ]{ai_face();OgreFireGrenade();};

Change to:

void()ogre_nail4 =[ $shoot3, ogre_nail5 ]{ai_face();Enemy_Weapons_Random();};

SOLDIER: Line101:

void()army_atk5 =[ $shoot5, army_atk6 ] {ai_face();army_fire();

Change to:

void()army_atk5 =[ $shoot5, army_atk6 ]{ai_face();Enemy_Weapons_Random();
Of course, you can apply this to more monsters, but these three are the best for it.

STEP 9
Well, I know youmust be glad to be finished. I sure as hell am, this thing tookme 6 hours. But, it has been checked for any and every flaw, and none are there. So, load it up and have fun. ((I may write an extension to this tutorial later that will tell the monsters which weapons to use close range and long range))