Files
quakemapping/mod_mine/quakec_src/monsters.qc
2019-12-30 17:23:05 +01:00

441 lines
9.7 KiB
Plaintext

/* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */
// name =[framenum, nexttime, nextthink] {code}
// expands to:
// name ()
// {
// self.frame=framenum;
// self.nextthink = time + nexttime;
// self.think = nextthink
// <code>
// };
/*
================
monster_update_total
Call this function to safely update total_monsters when the game is in
progress. It adds "n" to total_monsters, then notifies all clients of
the change. -- iw
================
*/
void(float n) monster_update_total =
{
total_monsters = total_monsters + n;
WriteByte (MSG_ALL, SVC_UPDATESTAT);
WriteByte (MSG_ALL, STAT_TOTALMONSTERS);
WriteLong (MSG_ALL, total_monsters);
};
/* From Preach's tutorial here: https://tomeofpreach.wordpress.com/2017/10/08/teleporting-monsters-flag/#more-2281 */
//define Preach's new fields dumptruck_ds
.string tele_model;
.vector tele_mins;
.vector tele_maxs;
.float tele_solid;
.float tele_movetype;
void(vector org) spawn_tfog;
void(vector org, entity death_owner) spawn_tdeath;
void() monster_teleport_go =
{
self.solid = self.tele_solid;
self.movetype = self.tele_movetype;
setmodel(self, self.tele_model);
setsize (self, self.tele_mins, self.tele_maxs);
self.delay = 0; //fix for cumulative delays for counters etc. -- dumptruck_ds
self.think1();
//override the random delay some go functions apply
self.nextthink = time + 0.1;
{
if (self.wait == 0/* || self.classname == "monster_zombie"*/) //dumptruck_ds: if wait value is >0 spawn silently, hack for zombies broke this, fixed with pos1!
spawn_tfog (self.origin);
spawn_tdeath(self.origin, self);
}
}
void() monster_teleport_delay = //new from Qmaster func coding help thread
{
self.think = monster_teleport_go;
self.nextthink = time + 0.1 + self.delay;
};
/*
================
monster_teleport_check
This detects and eliminates a common map bug: a trigger-spawned monster
which can't be activated either because it has no targetname or because
its targetname isn't targeted by any other entity. (This map bug would
otherwise make it impossible for the player to get 100% kills.) -- iw
================
*/
void() monster_teleport_check =
{
if (!SUB_IsTargeted ())
{
dprint ("WARNING: removed untargeted trigger-spawned ");
dprint (self.classname);
dprint (" at ");
dprint (vtos (self.origin));
dprint ("\n");
remove (self);
return;
}
// the targetname appears to be OK, so let's finish setting up the
// trigger-spawned monster -- iw
self.use = monster_teleport_delay; // qmaster
monster_update_total (1);
};
float (void() monster_start_fn) monster_teleport =
{
if(!(self.spawnflags & 8))
return FALSE;
//PREACH: This monster is to be teleported in, so hide it
self.tele_model= self.model;
self.tele_mins = self.mins;
self.tele_maxs = self.maxs;
self.tele_solid = self.solid;
self.tele_movetype = self.movetype;
self.model = "";
self.modelindex = 0;
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NONE;
self.think1 = monster_start_fn;
// wait for other entities to finish spawning, then check that
// something targets this -- iw
self.think = monster_teleport_check;
self.nextthink = time + 0.1;
return TRUE;
}
//end of Preach's new fields here
/*
================
monster_use
Using a monster makes it angry at the current activator
================
*/
void() monster_use =
{
if (self.enemy)
return;
if (self.health <= 0)
return;
if (activator.items & IT_INVISIBILITY)
return;
if (activator.flags & FL_NOTARGET)
return;
if (activator.classname != "player")
return;
// delay reaction so if the monster is teleported, its sound is still
// heard
self.enemy = activator;
self.nextthink = time + 0.1;
self.think = FoundTarget;
};
/*
================
monster_death_use
When a mosnter dies, it fires all of its targets with the current
enemy as activator.
================
*/
void() monster_death_use =
{
// fall to ground
if (self.flags & FL_FLY)
self.flags = self.flags - FL_FLY;
if (self.flags & FL_SWIM)
self.flags = self.flags - FL_SWIM;
if (!self.target)
return;
activator = self.enemy;
SUB_UseTargets ();
};
/*
================
monster_pain_use //dumptruck_ds
When a monster reaches pain_threshold, it fires all of its pain_targets
with the current enemy as activator.
================
*/
void() monster_pain_use =
{
if (!self.pain_target)
return;
activator = self.enemy;
SUB_UsePain ();
};
//============================================================================
void() walkmonster_start_go =
{
self.origin_z = self.origin_z + 1; // raise off floor a bit
//Preach's "check" here
// if(time <= 0.5)
if(!(self.spawnflags & 8))
{
droptofloor();
if (!walkmove(0,0))
{
dprint ("\n\n");
dprint (self.classname);
dprint (" in wall at: ");
dprint (vtos(self.origin));
dprint ("\n\n");
}
}
self.takedamage = DAMAGE_AIM;
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 20;
self.view_ofs = '0 0 25';
self.use = monster_use;
self.flags = self.flags | FL_MONSTER;
if (self.target != "")
{
self.goalentity = self.movetarget = find(world, targetname, self.target);
self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
if (!self.movetarget)
{
dprint ("Monster can't find target at ");
dprint (vtos(self.origin));
dprint ("\n");
}
// this used to be an objerror
if (self.movetarget.classname == "path_corner")
{
self.th_walk ();
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
if ((self.spawnflags & 8) && self.spawn_angry == 1) //dumptruck_ds -- using spawn_angry set to 1 in order to spawn in "angry" monsters
{
monster_use();
}
// local entity pl; //dumptruck_ds -- this is Shamblernaut's method
//
// pl = find (world, classname, "player");
//
// if (self.spawn_angry == 1)
// {
// activator = pl;
// monster_use();
// }
};
void() walkmonster_start =
//Preach's tutorial
{
if(monster_teleport(walkmonster_start_go))
return;
// delay drop to floor to make sure all doors have been spawned
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
self.think = walkmonster_start_go;
total_monsters = total_monsters + 1;
/// Trigger enemy after spawn (khreathor)
};
void() flymonster_start_go =
{
self.takedamage = DAMAGE_AIM;
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 10;
self.view_ofs = '0 0 25';
self.use = monster_use;
self.flags = self.flags | FL_FLY;
self.flags = self.flags | FL_MONSTER;
if (!walkmove(0,0))
{
dprint ("flymonster in wall at: ");
dprint (vtos(self.origin));
dprint ("\n");
}
if (self.target != "")
{
self.goalentity = self.movetarget = find(world, targetname, self.target);
if (!self.movetarget)
{
dprint ("Monster can't find target at ");
dprint (vtos(self.origin));
dprint ("\n");
}
// this used to be an objerror
if (self.movetarget.classname == "path_corner")
{
self.th_walk ();
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
if ((self.spawnflags & 8) && self.spawn_angry == 1) //dumptruck_ds -- using spawn_angry set to 1 in order to spawn in "angry" monsters
{
monster_use();
}
// local entity pl; //dumptruck_ds -- this is Shamblernaut's method
//
// local entity pl;
//
// pl = find (world, classname, "player");
//
// if (self.spawn_angry == 1)
// {
// activator = pl;
// monster_use();
// }
};
void() flymonster_start =
{
//Preach's tutorial
if(monster_teleport(flymonster_start_go))
return;
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
self.think = flymonster_start_go;
total_monsters = total_monsters + 1;
};
void() swimmonster_start_go =
{
if (deathmatch)
{
remove(self);
return;
}
self.takedamage = DAMAGE_AIM;
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 10;
self.view_ofs = '0 0 10';
self.use = monster_use;
self.flags = self.flags | FL_SWIM;
self.flags = self.flags | FL_MONSTER;
if (self.target != "")
{
self.goalentity = self.movetarget = find(world, targetname, self.target);
if (!self.movetarget)
{
dprint ("Monster can't find target at ");
dprint (vtos(self.origin));
dprint ("\n");
}
// this used to be an objerror
self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
self.th_walk ();
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
if ((self.spawnflags & 8) && self.spawn_angry == 1) //dumptruck_ds -- using spawn_angry set to 1 in order to spawn in "angry" monsters
{
monster_use();
}
// local entity pl; //dumptruck_ds -- this is Shamblernaut's method
//
// pl = find (world, classname, "player");
//
// if (self.spawn_angry == 1)
// {
// activator = pl;
// monster_use();
// }
};
void() swimmonster_start =
{
//Preach's tutorial
if(monster_teleport(swimmonster_start_go))
return;
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
self.think = swimmonster_start_go;
total_monsters = total_monsters + 1;
};