thirtyflightsofloving/missionpack/m_fixbot.c
Knightmare66 8401c475f6 Improved Tactician Gunner's prox mine aiming in missionpack DLL.
Added powerarmortype field to default Lazaus DLL.  This changes the savegame version.
Added support for blood_type value of 4 to default Lazaurus, missionpack, and Zaero DLLs.
Added support for Lazarus initially dead monsters (health < 0) to missionpack DLL.
Added support for Lazarus monsterjump cvar (berserk, gunner, infantry, mutant, parasite) to missionpack DLL.
Cleaned up surface rendering code from mpolyvertex_t refactoring.
2021-07-31 02:07:06 -04:00

1393 lines
31 KiB
C

/*
fixbot.c
*/
#include "g_local.h"
#include "m_fixbot.h"
#define MZ2_fixbot_BLASTER_1 MZ2_HOVER_BLASTER_1
qboolean visible (edict_t *self, edict_t *other);
qboolean infront (edict_t *self, edict_t *other);
static int sound_pain1;
static int sound_die;
static int sound_weld1;
static int sound_weld2;
static int sound_weld3;
void fixbot_run (edict_t *self);
void fixbot_stand (edict_t *self);
void fixbot_dead (edict_t *self);
void fixbot_attack (edict_t *self);
void fixbot_fire_blaster (edict_t *self);
void fixbot_fire_welder (edict_t *self);
void fixbot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
void use_scanner (edict_t *self);
void change_to_roam (edict_t *self);
void fly_vertical (edict_t *self);
extern mmove_t fixbot_move_forward;
extern mmove_t fixbot_move_stand;
extern mmove_t fixbot_move_stand2;
extern mmove_t fixbot_move_roamgoal;
extern mmove_t fixbot_move_weld_start;
extern mmove_t fixbot_move_weld;
extern mmove_t fixbot_move_weld_end;
extern mmove_t fixbot_move_takeoff;
extern mmove_t fixbot_move_landing;
extern mmove_t fixbot_move_turn;
extern void roam_goal (edict_t *self);
void ED_CallSpawn (edict_t *ent);
float crand(void)
{
return (rand()&32767)*(2.0/32767)-1;
};
edict_t *fixbot_FindDeadMonster (edict_t *self)
{
edict_t *ent = NULL;
edict_t *best = NULL;
while ((ent = findradius(ent, self->s.origin, 1024)) != NULL)
{
if (ent == self)
continue;
if (!(ent->svflags & SVF_MONSTER))
continue;
if (ent->monsterinfo.aiflags & AI_GOOD_GUY)
continue;
if (ent->owner)
continue;
if (ent->health > 0)
continue;
if (ent->nextthink)
continue;
if (!visible(self, ent))
continue;
if (!best)
{
best = ent;
continue;
}
if (ent->max_health <= best->max_health)
continue;
best = ent;
}
return best;
}
int fixbot_search (edict_t *self)
{
edict_t *ent;
if (!self->goalentity)
{
ent = fixbot_FindDeadMonster(self);
if (ent)
{
self->oldenemy = self->enemy;
self->enemy = ent;
self->enemy->owner = self;
self->monsterinfo.aiflags |= AI_MEDIC;
FoundTarget (self);
return (1);
}
}
return (0);
}
void landing_goal (edict_t *self)
{
trace_t tr;
vec3_t forward, right, up;
vec3_t end;
edict_t *ent;
ent = G_Spawn ();
ent->classname = "bot_goal";
ent->solid = SOLID_BBOX;
ent->owner = self;
gi.linkentity (ent);
// Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34)
VectorSet (ent->mins, -24, -24, -16);
VectorSet (ent->maxs, 24, 24, 16);
AngleVectors (self->s.angles, forward, right, up);
VectorMA (self->s.origin, 32, forward, end);
VectorMA (self->s.origin, -8096, up, end);
tr = gi.trace (self->s.origin, ent->mins, ent->maxs, end, self, MASK_MONSTERSOLID);
VectorCopy (tr.endpos, ent->s.origin);
self->goalentity = self->enemy = ent;
self->monsterinfo.currentmove = &fixbot_move_landing;
}
void takeoff_goal (edict_t *self)
{
trace_t tr;
vec3_t forward, right, up;
vec3_t end;
edict_t *ent;
ent = G_Spawn ();
ent->classname = "bot_goal";
ent->solid = SOLID_BBOX;
ent->owner = self;
gi.linkentity (ent);
// Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34)
VectorSet (ent->mins, -24, -24, -16);
VectorSet (ent->maxs, 24, 24, 16);
AngleVectors (self->s.angles, forward, right, up);
VectorMA (self->s.origin, 32, forward, end);
VectorMA (self->s.origin, 128, up, end);
tr = gi.trace (self->s.origin, ent->mins, ent->maxs, end, self, MASK_MONSTERSOLID);
VectorCopy (tr.endpos, ent->s.origin);
self->goalentity = self->enemy = ent;
self->monsterinfo.currentmove = &fixbot_move_takeoff;
}
void change_to_roam (edict_t *self)
{
if (fixbot_search(self))
return;
self->monsterinfo.currentmove = &fixbot_move_roamgoal;
if (self->spawnflags & 16)
{
landing_goal (self);
self->monsterinfo.currentmove = &fixbot_move_landing;
self->spawnflags &= ~16;
self->spawnflags = 32;
}
if (self->spawnflags & 8)
{
takeoff_goal (self);
self->monsterinfo.currentmove = &fixbot_move_takeoff;
self->spawnflags &= ~8;
self->spawnflags = 32;
}
if (self->spawnflags & 4)
{
self->monsterinfo.currentmove = &fixbot_move_roamgoal;
self->spawnflags &= ~4;
self->spawnflags = 32;
}
if (!self->spawnflags)
{
self->monsterinfo.currentmove = &fixbot_move_stand2;
}
}
void roam_goal (edict_t *self)
{
trace_t tr;
vec3_t forward, right, up;
vec3_t end;
edict_t *ent;
vec3_t dang;
int len, oldlen, whichi, i;
vec3_t vec;
vec3_t whichvec;
ent = G_Spawn ();
ent->classname = "bot_goal";
ent->solid = SOLID_BBOX;
ent->owner = self;
gi.linkentity (ent);
oldlen = 0;
whichi = 0;
for (i=0; i<12; i++)
{
VectorCopy (self->s.angles, dang);
if (i < 6)
dang[YAW] += 30 * i;
else
dang[YAW] -= 30 * (i-6);
AngleVectors (dang, forward, right, up);
VectorMA (self->s.origin, WORLD_SIZE, forward, end); // was 8192
tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT);
VectorSubtract (self->s.origin, tr.endpos, vec);
len = VectorNormalize (vec);
if (len > oldlen)
{
oldlen=len;
VectorCopy (tr.endpos, whichvec);
}
}
VectorCopy (whichvec, ent->s.origin);
self->goalentity = self->enemy = ent;
self->monsterinfo.currentmove = &fixbot_move_turn;
}
void use_scanner (edict_t *self)
{
edict_t *ent = NULL;
float radius = 1024;
vec3_t vec;
int len;
int oldlen = 0x10000;
edict_t *tempent = NULL;
while ((ent = findradius(ent, self->s.origin, radius)) != NULL)
{
if (ent->health >= 100)
{
if (strcmp (ent->classname, "object_repair") == 0)
{
if (visible(self, ent))
{
// remove the old one
if (strcmp (self->goalentity->classname, "bot_goal") == 0)
{
self->goalentity->nextthink = level.time + 0.1;
self->goalentity->think = G_FreeEdict;
}
self->goalentity = self->enemy = ent;
VectorSubtract (self->s.origin, self->goalentity->s.origin, vec);
len = VectorNormalize (vec);
if (len < 32)
{
self->monsterinfo.currentmove = &fixbot_move_weld_start;
return;
}
return;
}
}
}
}
VectorSubtract (self->s.origin, self->goalentity->s.origin, vec);
len = VectorLength (vec);
if (len < 32)
{
if (strcmp (self->goalentity->classname, "object_repair") == 0)
{
self->monsterinfo.currentmove = &fixbot_move_weld_start;
}
else
{
self->goalentity->nextthink = level.time + 0.1;
self->goalentity->think = G_FreeEdict;
self->goalentity = self->enemy = NULL;
self->monsterinfo.currentmove = &fixbot_move_stand;
}
return;
}
VectorSubtract (self->s.origin, self->s.old_origin, vec);
len = VectorLength (vec);
/*
bot is stuck get new goalentity
*/
if (len == 0)
{
if (strcmp (self->goalentity->classname , "object_repair") == 0)
{
self->monsterinfo.currentmove = &fixbot_move_stand;
}
else
{
self->goalentity->nextthink = level.time + 0.1;
self->goalentity->think = G_FreeEdict;
self->goalentity = self->enemy = NULL;
self->monsterinfo.currentmove = &fixbot_move_stand;
}
}
}
/*
when the bot has found a landing pad
it will proceed to its goalentity
just above the landing pad and
decend translated along the z the current
frames are at 10fps
*/
void blastoff (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread)
{
trace_t tr;
vec3_t dir;
vec3_t forward, right, up;
vec3_t end;
float r;
float u;
vec3_t water_start;
qboolean water = false;
int content_mask = MASK_SHOT | MASK_WATER;
hspread+= (self->s.frame - FRAME_takeoff_01);
vspread+= (self->s.frame - FRAME_takeoff_01);
tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
if (!(tr.fraction < 1.0))
{
vectoangles (aimdir, dir);
AngleVectors (dir, forward, right, up);
r = crandom()*hspread;
u = crandom()*vspread;
VectorMA (start, WORLD_SIZE, forward, end); // was 8192
VectorMA (end, r, right, end);
VectorMA (end, u, up, end);
if (gi.pointcontents (start) & MASK_WATER)
{
water = true;
VectorCopy (start, water_start);
content_mask &= ~MASK_WATER;
}
tr = gi.trace (start, NULL, NULL, end, self, content_mask);
// see if we hit water
if (tr.contents & MASK_WATER)
{
int color;
water = true;
VectorCopy (tr.endpos, water_start);
if (!VectorCompare (start, tr.endpos))
{
if (tr.contents & CONTENTS_WATER)
{
if (strcmp(tr.surface->name, "*brwater") == 0)
color = SPLASH_BROWN_WATER;
else
color = SPLASH_BLUE_WATER;
}
else if (tr.contents & CONTENTS_SLIME)
color = SPLASH_SLIME;
else if (tr.contents & CONTENTS_LAVA)
color = SPLASH_LAVA;
else
color = SPLASH_UNKNOWN;
if (color != SPLASH_UNKNOWN)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_SPLASH);
gi.WriteByte (8);
gi.WritePosition (tr.endpos);
gi.WriteDir (tr.plane.normal);
gi.WriteByte (color);
gi.multicast (tr.endpos, MULTICAST_PVS);
}
// change bullet's course when it enters water
VectorSubtract (end, start, dir);
vectoangles (dir, dir);
AngleVectors (dir, forward, right, up);
r = crandom()*hspread*2;
u = crandom()*vspread*2;
VectorMA (water_start, WORLD_SIZE, forward, end); // was 8192
VectorMA (end, r, right, end);
VectorMA (end, u, up, end);
}
// re-trace ignoring water this time
tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
}
}
// send gun puff / flash
if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
{
if (tr.fraction < 1.0)
{
if (tr.ent->takedamage)
{
T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, MOD_BLASTOFF);
}
else
{
if (strncmp (tr.surface->name, "sky", 3) != 0)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (te_impact);
gi.WritePosition (tr.endpos);
gi.WriteDir (tr.plane.normal);
gi.multicast (tr.endpos, MULTICAST_PVS);
if (self->client)
PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
}
}
}
}
// if went through water, determine where the end and make a bubble trail
if (water)
{
vec3_t pos;
VectorSubtract (tr.endpos, water_start, dir);
VectorNormalize (dir);
VectorMA (tr.endpos, -2, dir, pos);
if (gi.pointcontents (pos) & MASK_WATER)
VectorCopy (pos, tr.endpos);
else
tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
VectorAdd (water_start, tr.endpos, pos);
VectorScale (pos, 0.5, pos);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_BUBBLETRAIL);
gi.WritePosition (water_start);
gi.WritePosition (tr.endpos);
gi.multicast (pos, MULTICAST_PVS);
}
}
void fly_vertical (edict_t *self)
{
int i;
vec3_t v;
vec3_t forward, right, up;
vec3_t start;
vec3_t tempvec;
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
self->ideal_yaw = vectoyaw(v);
M_ChangeYaw (self);
if (self->s.frame == FRAME_landing_58 || self->s.frame == FRAME_takeoff_16)
{
self->goalentity->nextthink = level.time + 0.1;
self->goalentity->think = G_FreeEdict;
self->monsterinfo.currentmove = &fixbot_move_stand;
self->goalentity = self->enemy = NULL;
}
// kick up some particles
VectorCopy (self->s.angles, tempvec);
tempvec[PITCH] += 90;
AngleVectors (tempvec, forward, right, up);
VectorCopy (self->s.origin, start);
for (i=0; i< 10; i++)
blastoff (self, start, forward, 2, 1, TE_SHOTGUN, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD);
// needs sound
}
void fly_vertical2 (edict_t *self)
{
vec3_t v;
int len;
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
len = VectorLength (v);
self->ideal_yaw = vectoyaw(v);
M_ChangeYaw (self);
if (len < 32)
{
self->goalentity->nextthink = level.time + 0.1;
self->goalentity->think = G_FreeEdict;
self->monsterinfo.currentmove = &fixbot_move_stand;
self->goalentity = self->enemy = NULL;
}
// needs sound
}
mframe_t fixbot_frames_landing [] =
{
ai_move, 0, NULL,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2,
ai_move, 0, fly_vertical2
};
mmove_t fixbot_move_landing = { FRAME_landing_01, FRAME_landing_58, fixbot_frames_landing, NULL };
/*
generic ambient stand
*/
mframe_t fixbot_frames_stand [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, change_to_roam
};
mmove_t fixbot_move_stand = {FRAME_ambient_01, FRAME_ambient_19, fixbot_frames_stand, NULL};
mframe_t fixbot_frames_stand2 [] =
{
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL
};
mmove_t fixbot_move_stand2 = {FRAME_ambient_01, FRAME_ambient_19, fixbot_frames_stand2, NULL};
/*
will need the pickup offset for the front pincers
object will need to stop forward of the object
and take the object with it ( this may require a variant of liftoff and landing )
*/
mframe_t fixbot_frames_pickup [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t fixbot_move_pickup = { FRAME_pickup_01, FRAME_pickup_27, fixbot_frames_pickup, NULL};
/*
generic frame to move bot
*/
mframe_t fixbot_frames_roamgoal [] =
{
ai_move, 0, roam_goal
};
mmove_t fixbot_move_roamgoal = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_roamgoal, NULL};
void ai_facing (edict_t *self, float dist)
{
vec3_t v;
if (infront (self, self->goalentity))
self->monsterinfo.currentmove = &fixbot_move_forward;
else
{
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
self->ideal_yaw = vectoyaw(v);
M_ChangeYaw (self);
}
};
mframe_t fixbot_frames_turn [] =
{
ai_facing, 0, NULL
};
mmove_t fixbot_move_turn = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_turn, NULL};
void go_roam (edict_t *self)
{
self->monsterinfo.currentmove = &fixbot_move_stand;
}
/*
takeoff
*/
mframe_t fixbot_frames_takeoff [] =
{
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical,
ai_move, 0.01, fly_vertical
};
mmove_t fixbot_move_takeoff = {FRAME_takeoff_01, FRAME_takeoff_16, fixbot_frames_takeoff, NULL};
/* findout what this is */
mframe_t fixbot_frames_paina [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t fixbot_move_paina = {FRAME_paina_01, FRAME_paina_06, fixbot_frames_paina, fixbot_run};
/* findout what this is */
mframe_t fixbot_frames_painb [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t fixbot_move_painb = {FRAME_painb_01, FRAME_painb_08, fixbot_frames_painb, fixbot_run};
/*
backup from pain
call a generic painsound
some spark effects
*/
mframe_t fixbot_frames_pain3 [] =
{
ai_move, -1, NULL
};
mmove_t fixbot_move_pain3 = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_pain3, fixbot_run};
/*
bot has compleated landing
and is now on the grownd
( may need second land if the bot is releasing jib into jib vat )
*/
mframe_t fixbot_frames_land [] =
{
ai_move, 0, NULL
};
mmove_t fixbot_move_land = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_land, NULL};
void M_MoveToGoal (edict_t *ent, float dist);
void ai_movetogoal (edict_t *self, float dist)
{
M_MoveToGoal (self, dist);
}
/*
*/
mframe_t fixbot_frames_forward [] =
{
ai_movetogoal, 5, use_scanner
};
mmove_t fixbot_move_forward = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_forward, NULL};
/*
*/
mframe_t fixbot_frames_walk [] =
{
ai_walk, 5, NULL
};
mmove_t fixbot_move_walk = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_walk, NULL};
/*
*/
mframe_t fixbot_frames_run [] =
{
ai_run, 10, NULL
};
mmove_t fixbot_move_run = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_run, NULL};
/*
raf
note to self
they could have a timer that will cause
the bot to explode on countdown
*/
mframe_t fixbot_frames_death1 [] =
{
ai_move, 0, NULL
};
mmove_t fixbot_move_death1 = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_death1, fixbot_dead};
//
mframe_t fixbot_frames_backward [] =
{
ai_move, 0, NULL
};
mmove_t fixbot_move_backward = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_backward, NULL};
//
mframe_t fixbot_frames_start_attack [] =
{
ai_charge, 0, NULL
};
mmove_t fixbot_move_start_attack = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_start_attack, fixbot_attack};
/*
TBD:
need to get laser attack anim
attack with the laser blast
*/
mframe_t fixbot_frames_attack1 [] =
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, -10, fixbot_fire_blaster
};
mmove_t fixbot_move_attack1 = {FRAME_shoot_01, FRAME_shoot_06, fixbot_frames_attack1, NULL};
int check_telefrag (edict_t *self)
{
vec3_t start = { 0, 0, 0 };
vec3_t forward, right, up;
trace_t tr;
AngleVectors (self->enemy->s.angles, forward, right, up);
VectorMA (start, 48, up, start);
tr = gi.trace (self->enemy->s.origin, self->enemy->mins, self->enemy->maxs, start, self, MASK_MONSTERSOLID);
if (tr.ent->takedamage)
{
tr.ent->health = -1000;
return (0);
}
return (1);
}
void fixbot_fire_laser (edict_t *self)
{
vec3_t forward, right, up;
vec3_t tempang, start;
vec3_t dir, angles, end;
edict_t *ent;
// critter dun got blown up while bein' fixed
if (self->enemy->health <= self->enemy->gib_health)
{
self->monsterinfo.currentmove = &fixbot_move_stand;
self->monsterinfo.aiflags &= ~AI_MEDIC;
return;
}
gi.sound(self, CHAN_AUTO, gi.soundindex("misc/lasfly.wav"), 1, ATTN_STATIC, 0);
VectorCopy (self->s.origin, start);
VectorCopy (self->enemy->s.origin, end);
VectorSubtract (end, start, dir);
vectoangles (dir, angles);
ent = G_Spawn ();
VectorCopy (self->s.origin, ent->s.origin);
VectorCopy (angles, tempang);
AngleVectors (tempang, forward, right, up);
VectorCopy (tempang, ent->s.angles);
VectorCopy (ent->s.origin, start);
VectorMA (start, 16, forward, start);
VectorCopy (start, ent->s.origin);
ent->enemy = self->enemy;
ent->owner = self;
ent->dmg = -1;
monster_fire_dabeam (ent);
if (self->enemy->health > (self->enemy->mass/10))
{
// sorry guys but had to fix the problem this way
// if it doesn't do this then two creatures can share the same space
// and its real bad.
if (check_telefrag (self))
{
self->enemy->spawnflags = 0;
self->enemy->monsterinfo.aiflags = 0;
self->enemy->target = NULL;
self->enemy->targetname = NULL;
self->enemy->combattarget = NULL;
self->enemy->deathtarget = NULL;
self->enemy->owner = self;
//Knightmare added
self->enemy->monsterinfo.monsterflags |= MFL_DO_NOT_COUNT;
self->enemy->item = NULL;
// Lazarus: reset initially dead monsters to use the INVERSE of their
// initial health, and force gib_health to default value
if (self->enemy->max_health < 0)
{
self->enemy->max_health = -self->enemy->max_health;
self->enemy->gib_health = 0;
}
self->enemy->health = self->enemy->max_health;
self->enemy->s.skinnum = 0;
//end Knightmare
ED_CallSpawn (self->enemy);
self->enemy->owner = NULL;
self->s.origin[2] += 1;
self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
self->monsterinfo.currentmove = &fixbot_move_stand;
self->monsterinfo.aiflags &= ~AI_MEDIC;
}
}
else
self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
}
mframe_t fixbot_frames_laserattack [] =
{
ai_charge, 0, fixbot_fire_laser,
ai_charge, 0, fixbot_fire_laser,
ai_charge, 0, fixbot_fire_laser,
ai_charge, 0, fixbot_fire_laser,
ai_charge, 0, fixbot_fire_laser,
ai_charge, 0, fixbot_fire_laser
};
mmove_t fixbot_move_laserattack = {FRAME_shoot_01, FRAME_shoot_06, fixbot_frames_laserattack, NULL};
/*
need to get forward translation data
for the charge attack
*/
mframe_t fixbot_frames_attack2 [] =
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, -10, NULL,
ai_charge, 0, fixbot_fire_blaster,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
mmove_t fixbot_move_attack2 = {FRAME_charging_01, FRAME_charging_31, fixbot_frames_attack2, fixbot_run};
void weldstate (edict_t *self)
{
if (self->s.frame == FRAME_weldstart_10)
self->monsterinfo.currentmove = &fixbot_move_weld;
else if (self->s.frame == FRAME_weldmiddle_07)
{
if (self->goalentity->health < 0)
{
self->enemy->owner = NULL;
self->monsterinfo.currentmove = &fixbot_move_weld_end;
}
else
self->goalentity->health-=10;
}
else
{
self->goalentity = self->enemy = NULL;
self->monsterinfo.currentmove = &fixbot_move_stand;
}
}
void ai_move2 (edict_t *self, float dist)
{
vec3_t v;
if (dist)
M_walkmove (self, self->s.angles[YAW], dist);
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
self->ideal_yaw = vectoyaw(v);
M_ChangeYaw (self);
};
mframe_t fixbot_frames_weld_start [] =
{
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, NULL,
ai_move2, 0, weldstate
};
mmove_t fixbot_move_weld_start = {FRAME_weldstart_01, FRAME_weldstart_10, fixbot_frames_weld_start, NULL};
mframe_t fixbot_frames_weld [] =
{
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, fixbot_fire_welder,
ai_move2, 0, weldstate
};
mmove_t fixbot_move_weld = {FRAME_weldmiddle_01, FRAME_weldmiddle_07, fixbot_frames_weld, NULL};
mframe_t fixbot_frames_weld_end [] =
{
ai_move2, -2, NULL,
ai_move2, -2, NULL,
ai_move2, -2, NULL,
ai_move2, -2, NULL,
ai_move2, -2, NULL,
ai_move2, -2, NULL,
ai_move2, -2, weldstate
};
mmove_t fixbot_move_weld_end = {FRAME_weldend_01, FRAME_weldend_07, fixbot_frames_weld_end, NULL};
void fixbot_fire_welder (edict_t *self)
{
vec3_t start;
vec3_t forward, right, up;
vec3_t end;
vec3_t dir;
int count = 2;
vec3_t vec;
float r;
if (!self->enemy)
return;
vec[0]=24.0;
vec[1]=-0.8;
vec[2]=-10.0;
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, vec, forward, right, start);
VectorCopy (self->enemy->s.origin, end);
VectorSubtract (end, start, dir);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_WELDING_SPARKS);
gi.WriteByte (10);
gi.WritePosition (start);
gi.WriteDir (vec3_origin);
gi.WriteByte (0xe0 + (rand()&7));
gi.multicast (self->s.origin, MULTICAST_PVS);
if (random() > 0.8)
{
r = random();
if (r < 0.33)
gi.sound (self, CHAN_VOICE, sound_weld1, 1, ATTN_IDLE, 0);
else if (r < 0.66)
gi.sound (self, CHAN_VOICE, sound_weld2, 1, ATTN_IDLE, 0);
else
gi.sound (self, CHAN_VOICE, sound_weld3, 1, ATTN_IDLE, 0);
}
}
void fixbot_fire_blaster (edict_t *self)
{
vec3_t start;
vec3_t forward, right, up;
vec3_t end;
vec3_t dir;
if (!visible(self, self->enemy))
{
self->monsterinfo.currentmove = &fixbot_move_run;
}
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_fixbot_BLASTER_1], forward, right, start);
VectorCopy (self->enemy->s.origin, end);
end[2] += self->enemy->viewheight;
// Lazarus fog reduction of accuracy
if (self->monsterinfo.visibility < FOG_CANSEEGOOD)
{
end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
}
VectorSubtract (end, start, dir);
monster_fire_blaster (self, start, dir, 15, 1000, MZ2_fixbot_BLASTER_1, EF_BLASTER, BLASTER_ORANGE);
}
void fixbot_stand (edict_t *self)
{
self->monsterinfo.currentmove = &fixbot_move_stand;
}
void fixbot_run (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
self->monsterinfo.currentmove = &fixbot_move_stand;
else
self->monsterinfo.currentmove = &fixbot_move_run;
}
void fixbot_walk (edict_t *self)
{
vec3_t vec;
int len;
if (strcmp (self->goalentity->classname, "object_repair") == 0)
{
VectorSubtract (self->s.origin, self->goalentity->s.origin, vec);
len = VectorLength (vec);
if (len < 32)
{
self->monsterinfo.currentmove = &fixbot_move_weld_start;
return;
}
}
self->monsterinfo.currentmove = &fixbot_move_walk;
}
void fixbot_start_attack (edict_t *self)
{
self->monsterinfo.currentmove = &fixbot_move_start_attack;
}
void fixbot_attack(edict_t *self)
{
vec3_t vec;
int len;
if (self->monsterinfo.aiflags & AI_MEDIC)
{
if (!visible (self, self->goalentity))
return;
VectorSubtract (self->s.origin, self->enemy->s.origin, vec);
len = VectorLength(vec);
if (len > 128)
return;
else
self->monsterinfo.currentmove = &fixbot_move_laserattack;
}
else
self->monsterinfo.currentmove = &fixbot_move_attack2;
}
void fixbot_pain (edict_t *self, edict_t *other, float kick, int damage)
{
// if (self->health < (self->max_health / 2))
// self->s.skinnum = 1;
// gi.dprintf("pain\n");
if (level.time < self->pain_debounce_time)
return;
self->pain_debounce_time = level.time + 3;
gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
if (damage <= 10)
self->monsterinfo.currentmove = &fixbot_move_pain3;
else if (damage <= 25)
self->monsterinfo.currentmove = &fixbot_move_painb;
else
self->monsterinfo.currentmove = &fixbot_move_paina;
}
void fixbot_dead (edict_t *self)
{
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, -8);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity (self);
}
void fixbot_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n; // Knightmare added
// shards
#ifdef KMQUAKE2_ENGINE_MOD // use fewer unique models for stock engine
for (n = 0; n < 8; n++)
ThrowGib (self, "models/objects/mech_gibs/gib2.md2", 0, 0, damage, GIB_METALLIC);
for (n = 0; n < 4; n++)
ThrowGib (self, "models/objects/mech_gibs/gib5.md2", 0, 0, damage, GIB_METALLIC);
#else
for (n = 0; n < 8; n++)
ThrowGib (self, "models/objects/debris1/tris.md2", 0, 0, damage, GIB_METALLIC);
for (n = 0; n < 4; n++)
ThrowGib (self, "models/objects/debris2/tris.md2", 0, 0, damage, GIB_METALLIC);
#endif
gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
BecomeExplosion1(self);
}
/*QUAKED monster_fixbot (1 .5 0) (-32 -32 -24) (32 32 24) Ambush Trigger_Spawn Fixit Takeoff Landing
*/
void SP_monster_fixbot (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
// Lazarus: special purpose skins
if ( self->style )
{
PatchMonsterModel("models/monsters/fixbot/tris.md2");
self->s.skinnum = self->style;
}
// precache gibs
#ifdef KMQUAKE2_ENGINE_MOD // use fewer unique models for stock engine
gi.modelindex ("models/objects/mech_gibs/gib2.md2");
gi.modelindex ("models/objects/mech_gibs/gib5.md2");
#else
gi.modelindex ("models/objects/debris1/tris.md2");
gi.modelindex ("models/objects/debris2/tris.md2");
#endif
sound_pain1 = gi.soundindex ("flyer/flypain1.wav");
sound_die = gi.soundindex ("flyer/flydeth1.wav");
sound_weld1 = gi.soundindex ("misc/welder1.wav");
sound_weld2 = gi.soundindex ("misc/welder2.wav");
sound_weld3 = gi.soundindex ("misc/welder3.wav");
self->s.modelindex = gi.modelindex ("models/monsters/fixbot/tris.md2");
// Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34)
VectorSet (self->mins, -24, -24, -16);
VectorSet (self->maxs, 24, 24, 16);
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
if (!self->health)
self->health = 150;
if (!self->mass)
self->mass = 150;
self->pain = fixbot_pain;
self->die = fixbot_die;
self->monsterinfo.stand = fixbot_stand;
self->monsterinfo.walk = fixbot_walk;
self->monsterinfo.run = fixbot_run;
self->monsterinfo.attack = fixbot_attack;
// Knightmare - sparks, not blood, on damage
if (!self->blood_type)
self->blood_type = 2; // smoking, not bloody gibs
// Lazarus
if (self->powerarmor)
{
if (self->powerarmortype == 1)
self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
else
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = self->powerarmor;
}
self->common_name = "Repair Bot";
gi.linkentity (self);
self->monsterinfo.currentmove = &fixbot_move_stand;
self->monsterinfo.scale = MODEL_SCALE;
flymonster_start (self);
}