thirtyflightsofloving/missionpack/z_sentien.c
Knightmare66 add4c7cc46 Reworked 2D pic drawing using a new, merged R_DrawPic() function. Moved old pic drawing functions to r_draw_removed.c.
Added new SCR_DrawPic() variants in cl_screen.c.
Added new graphics for text fields and sliders in menus.
Improved mouse interaction for menu sliders.
Added resettargets developer command to default Lazarus and  missionpack DLLs.
Added hint_test developer command to missionpack DLL.
Fixed freeze developer command in default Lazarus and missionpack DLLs so it can be used more than once.
More tweaks to Tactician Gunner prox mine safety checks in misssionpack DLL.
2021-08-07 00:43:46 -04:00

1321 lines
32 KiB
C

/*
==============================================================================
SETNTIEN
==============================================================================
*/
#include "g_local.h"
#include "z_anim.h"
#include "z_sentien.h"
/*=========================================================================
Sentien laser routines ... adapted from Zaero g_target.c
=========================================================================*/
void sentien_laser_think (edict_t *self)
{
edict_t *ignore;
vec3_t start;
vec3_t end;
trace_t tr;
vec3_t point;
vec3_t last_movedir;
int count;
if (self->spawnflags & 0x80000000)
count = 8;
else
count = 4;
if (self->enemy)
{
VectorCopy (self->movedir, last_movedir);
VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
VectorSubtract (point, self->s.origin, self->movedir);
VectorNormalize (self->movedir);
if (!VectorCompare(self->movedir, last_movedir))
self->spawnflags |= 0x80000000;
}
ignore = self;
VectorCopy (self->s.origin, start);
VectorMA (start, 2048, self->movedir, end);
while(1)
{
tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
if (!tr.ent)
break;
// hurt it if we can
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
// if we hit something that's not a monster or player or is immune to lasers, we're done
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
{
if (self->spawnflags & 0x80000000)
{
self->spawnflags &= ~0x80000000;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_LASER_SPARKS);
gi.WriteByte (count);
gi.WritePosition (tr.endpos);
gi.WriteDir (tr.plane.normal);
gi.WriteByte (self->s.skinnum);
gi.multicast (tr.endpos, MULTICAST_PVS);
}
break;
}
ignore = tr.ent;
VectorCopy (tr.endpos, start);
}
VectorCopy (tr.endpos, self->s.old_origin);
self->nextthink = level.time + FRAMETIME;
}
void sentien_laser_on (edict_t *self)
{
if (!self->activator)
self->activator = self;
self->spawnflags |= 0x80000001;
self->svflags &= ~SVF_NOCLIENT;
sentien_laser_think (self);
}
void sentien_laser_off (edict_t *self)
{
self->spawnflags &= ~1;
self->svflags |= SVF_NOCLIENT;
self->nextthink = 0;
}
/*=========================================================================
Sentien sound routines ... used from animation frames (and code).
=========================================================================*/
static int sound_idle1;
static int sound_idle2;
static int sound_idle3;
static int sound_walk;
static int sound_fend;
static int sound_pain1;
static int sound_pain2;
static int sound_pain3;
static int sound_die1;
static int sound_die2;
static int sound_att1;
static int sound_att2;
static int sound_att3;
void sentien_sound_footstep (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_walk, 1, ATTN_NORM, 0);
}
void sentien_sound_idle1 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_idle1, 1, ATTN_IDLE, 0);
}
void sentien_sound_idle2 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_idle2, 1, ATTN_IDLE, 0);
}
void sentien_sound_idle3 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_idle3, 1, ATTN_IDLE, 0);
}
void sentian_sound_att1 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_att1, 1, ATTN_NORM, 0);
}
void sentian_sound_att2 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_att2, 1, ATTN_NORM, 0);
}
void sentian_sound_att3 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_att3, 1, ATTN_NORM, 0);
}
void sentian_sound_fend (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_fend, 1, ATTN_NORM, 0);
}
void sentian_sound_pain1 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_pain1, 1, ATTN_NORM, 0);
}
void sentian_sound_pain2 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_pain2, 1, ATTN_NORM, 0);
}
void sentian_sound_pain3 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_pain3, 1, ATTN_NORM, 0);
}
void sentian_sound_die1 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_die1, 1, ATTN_NORM, 0);
}
void sentian_sound_die2 (edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_die2, 1, ATTN_NORM, 0);
}
/*=========================================================================
Sentien standing frames
=========================================================================*/
mframe_t sentien_frames_stand1 []=
{
ai_stand, 0, sentien_sound_idle1,
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,
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
};
mframe_t sentien_frames_stand2 []=
{
ai_stand, 0, sentien_sound_idle2,
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,
};
mframe_t sentien_frames_stand3 []=
{
ai_stand, 0, sentien_sound_idle1,
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,
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,
};
void sentien_stand(edict_t *self);
void sentien_stand_whatnow(edict_t *self);
void sentien_stand_earwax(edict_t *self);
mmove_t sentien_move_stand1 = {FRAME_stand1start, FRAME_stand1end,
sentien_frames_stand1, sentien_stand_whatnow};
mmove_t sentien_move_stand2 = {FRAME_stand2start, FRAME_stand2end,
sentien_frames_stand2, sentien_stand_whatnow};
mmove_t sentien_move_stand3 = {FRAME_stand3start, FRAME_stand3end,
sentien_frames_stand3, sentien_stand_earwax};
void sentien_stand(edict_t *self)
{
sentien_laser_off (self->flash);
self->monsterinfo.currentmove = &sentien_move_stand1;
}
void sentien_stand_whatnow (edict_t *self)
{
float r;
r = random();
if (r < self->random)
{
self->monsterinfo.currentmove = &sentien_move_stand1;
self->random -= 0.05;
}
else
{
r = random();
if (r < 0.5)
self->monsterinfo.currentmove = &sentien_move_stand2;
else
self->monsterinfo.currentmove = &sentien_move_stand3;
self->random = 1;
}
}
void sentien_stand_earwax (edict_t *self)
{
if (random() > 0.80)
{
//more ear wax damn it, try again
self->monsterinfo.currentmove = &sentien_move_stand3;
//gi.sound ... some frustration maybe
}
else
sentien_stand_whatnow(self);
}
/*=========================================================================
Sentien walking frames
=========================================================================*/
mframe_t sentien_frames_walk_start []=
{
ai_walk, 0.0, NULL,
ai_walk, 1.5, NULL,
ai_walk, 2.9, NULL,
ai_walk, 2.4, NULL,
ai_walk, 2.1, NULL,
ai_walk, 2.6, NULL,
ai_walk, 2.1, NULL,
ai_walk, 1.8, sentien_sound_footstep,
//ai_walk, 0.3, NULL
};
mframe_t sentien_frames_walk []=
{
ai_walk, 0.3, NULL,
ai_walk, 2.4, NULL,
ai_walk, 4.0, NULL,
ai_walk, 3.5, NULL,
ai_walk, 3.6, NULL,
ai_walk, 3.7 * 1.1, NULL,
ai_walk, 3.1 * 1.3, NULL,
ai_walk, 4.1 * 1.2, sentien_sound_footstep,
//ai_walk, 2.0, NULL, // change leg
ai_walk, 2.0, NULL,
ai_walk, 2.6, NULL, // 2.4
ai_walk, 3.8, NULL, // 3.9
ai_walk, 3.6, NULL,
ai_walk, 3.6, NULL,
ai_walk, 4.3, NULL,
ai_walk, 4.2 * 1.2, NULL,
ai_walk, 5.2, sentien_sound_footstep, // 4.1
//ai_walk, 0.8, NULL // 2.0 change leg
};
mframe_t sentien_frames_walk_end []=
{
ai_walk, 0.8, NULL,
ai_walk, 1.0, NULL,
ai_walk, 1.6, NULL,
ai_walk, 1.4, NULL,
ai_walk, 1.5, NULL,
ai_walk, 1.4, NULL,
ai_walk, 1.5, NULL,
ai_walk, 1.8, sentien_sound_footstep,
//ai_walk, 2.3, NULL // ??? I dunno about that one (Max)
};
void sentien_walk (edict_t *self);
mmove_t sentien_move_walk_start = {FRAME_walkStartStart, FRAME_walkStartEnd,
sentien_frames_walk_start, sentien_walk};
mmove_t sentien_move_walk = {FRAME_walkLoopStart, FRAME_walkLoopEnd,
sentien_frames_walk, NULL};
mmove_t sentien_move_walk_end = {FRAME_walkEndStart, FRAME_walkEndEnd,
sentien_frames_walk_end, sentien_stand};
void sentien_walk (edict_t *self)
{
sentien_laser_off (self->flash);
if (self->monsterinfo.currentmove == &sentien_move_walk)
return;
if (self->monsterinfo.currentmove == &sentien_move_stand1 ||
self->monsterinfo.currentmove == &sentien_move_stand2 ||
self->monsterinfo.currentmove == &sentien_move_stand3)
{
self->monsterinfo.currentmove = &sentien_move_walk_start;
}
else
{
self->monsterinfo.currentmove = &sentien_move_walk;
}
}
/*=========================================================================
Sentien running frames
=========================================================================*/
mframe_t sentien_frames_run_start []=
{
ai_run, 0.0, NULL,
ai_run, 1.5, NULL,
ai_run, 2.9, NULL,
ai_run, 2.4, NULL,
ai_run, 2.1, NULL,
ai_run, 2.6, NULL,
ai_run, 2.1, NULL,
ai_run, 1.8, sentien_sound_footstep,
//ai_run, 0.3, NULL
};
mframe_t sentien_frames_run []=
{
ai_run, 0.3 * 1.2, NULL,
ai_run, 2.4, NULL,
ai_run, 4.0, NULL,
ai_run, 3.5, NULL,
ai_run, 3.6, NULL,
ai_run, 3.7 * 1.1, NULL,
ai_run, 3.1 * 1.3, NULL,
ai_run, 4.1 * 1.2, sentien_sound_footstep,
//ai_run, 2.0, NULL, // change leg
ai_run, 2.0, NULL,
ai_run, 2.6, NULL, // 2.4
ai_run, 3.8, NULL, // 3.9
ai_run, 3.6, NULL,
ai_run, 3.6, NULL,
ai_run, 4.3, NULL,
ai_run, 4.2 * 1.2, NULL,
ai_run, 5.2, sentien_sound_footstep, // 4.1
//ai_run, 0.8, NULL // 2.0 change leg
};
mframe_t sentien_frames_run_end []=
{
ai_run, 0.8, NULL,
ai_run, 1.0, NULL,
ai_run, 1.6, NULL,
ai_run, 1.4, NULL,
ai_run, 1.5, NULL,
ai_run, 1.4, NULL,
ai_run, 1.5, NULL,
ai_run, 1.8, sentien_sound_footstep,
//ai_run, 2.3, NULL // ??? I dunno about that one (Max)
};
void sentien_run(edict_t *self);
mmove_t sentien_move_run_start = {FRAME_walkStartStart, FRAME_walkStartEnd,
sentien_frames_run_start, sentien_run};
mmove_t sentien_move_run = {FRAME_walkLoopStart, FRAME_walkLoopEnd,
sentien_frames_run, NULL};
mmove_t sentien_move_run_end = {FRAME_walkEndStart, FRAME_walkEndEnd,
sentien_frames_run_end, sentien_stand};
void sentien_run (edict_t *self)
{
sentien_laser_off (self->flash);
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
{
self->monsterinfo.currentmove = &sentien_move_stand1;
return;
}
if (self->monsterinfo.currentmove == &sentien_move_run)
return;
if (self->monsterinfo.currentmove == &sentien_move_walk ||
self->monsterinfo.currentmove == &sentien_move_run_start)
{
self->monsterinfo.currentmove = &sentien_move_run;
}
else
{
self->monsterinfo.currentmove = &sentien_move_run_start;
}
}
/*=========================================================================
Sentien blaster attack.
=========================================================================*/
void sentien_do_blast(edict_t *self);
mframe_t sentien_frames_pre_blast_attack []=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
mframe_t sentien_frames_blast_attack []=
{
ai_charge, 0, sentien_do_blast,
ai_charge, 0, sentien_do_blast,
ai_charge, 0, sentien_do_blast,
ai_charge, 0, sentien_do_blast,
ai_charge, 0, sentien_do_blast,
ai_charge, 0, sentien_do_blast,
};
mframe_t sentien_frames_post_blast_attack []=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
};
void sentien_blast_attack(edict_t *self);
void sentien_post_blast_attack(edict_t *self);
mmove_t sentien_move_pre_blast_attack = { FRAME_blastPreStart, FRAME_blastPreEnd,
sentien_frames_pre_blast_attack, sentien_blast_attack};
void sentien_post_blast_attack(edict_t *self);
mmove_t sentien_move_blast_attack = { FRAME_blastStart, FRAME_blastEnd,
sentien_frames_blast_attack, sentien_post_blast_attack};
mmove_t sentien_move_post_blast_attack = { FRAME_blastPostStart, FRAME_blastPostEnd,
sentien_frames_post_blast_attack, sentien_run};
void sentien_blast_attack (edict_t *self)
{
sentien_laser_off (self->flash);
self->monsterinfo.currentmove = &sentien_move_blast_attack;
// is a player right infront?
if (visible(self, self->enemy) &&
infront(self, self->enemy))
{
self->monsterinfo.currentmove = &sentien_move_blast_attack;
}
else
self->monsterinfo.currentmove = &sentien_move_post_blast_attack;
}
void sentien_post_blast_attack (edict_t *self)
{
float refire = 0.25;
if (visible(self, self->enemy) &&
infront(self, self->enemy))
{
if (skill->value == 1)
refire = 0.40;
else if (skill->value == 2)
refire = 0.60;
else if (skill->value >= 3)
refire = 0.75;
if (random() > refire)
self->monsterinfo.currentmove = &sentien_move_post_blast_attack;
}
else
self->monsterinfo.currentmove = &sentien_move_post_blast_attack;
}
void sentien_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage)
{
if (EMPNukeCheck(self, self->s.origin))
{
gi.sound (self, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
return;
}
ANIM_AIM(self, dir);
fire_bullet (self, start, dir, 2, 4,
DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD,
MOD_UNKNOWN);
sentian_sound_att1(self);
}
vec3_t sentien_flash_offset [] =
{
// frames 116+ (hex fire)
23.7, 25.4, 29.6,
23.7, 25.3, 26.7,
23.7, 27.7, 28.1,
23.7, 27.4, 31.2,
23.7, 24.9, 32.3,
23.7, 22.5, 30.6,
23.7, 22.7, 27.8
};
void sentien_do_blast (edict_t *self)
{
vec3_t start;
vec3_t forward, right;
vec3_t aim;
vec3_t end;
int idx;
idx = self->s.frame - FRAME_blastStart + 1;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, sentien_flash_offset[0],
forward, right, start);
VectorCopy (self->enemy->s.origin, end);
end[2] += self->enemy->viewheight;
VectorSubtract (end, start, aim);
//need to compare aim with facing to make sure we are not
//aiming too far sideways and correct if we are.
G_ProjectSource (self->s.origin, sentien_flash_offset[idx],
forward, right, start);
//monster_fire_blaster(self, start, aim, 2, 1000,
// MZ2_MEDIC_BLASTER_1, EF_BLUEHYPERBLASTER, BLASTER_BLUE);
if (EMPNukeCheck(self, start))
{
gi.sound (self, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
return;
}
sentien_fire_bullet(self, start, aim, 5);
}
/*=========================================================================
Sentien laser attack.
=========================================================================*/
void sentien_do_laser (edict_t *self);
mframe_t sentien_frames_pre_laser_attack []=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
mframe_t sentien_frames_laser_attack []=
{
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser,
NULL, 0, sentien_do_laser
};
mframe_t sentien_frames_post_laser_attack []=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
void sentien_laser_attack(edict_t *self);
void sentien_post_laser_attack(edict_t *self);
mmove_t sentien_move_pre_laser_attack = { FRAME_laserPreStart, FRAME_laserPreEnd,
sentien_frames_pre_laser_attack, sentien_laser_attack};
mmove_t sentien_move_laser_attack = { FRAME_laserStart, FRAME_laserEnd,
sentien_frames_laser_attack, sentien_post_laser_attack};
mmove_t sentien_move_post_laser_attack = { FRAME_laserPostStart, FRAME_laserPostEnd,
sentien_frames_post_laser_attack, sentien_run};
void sentien_laser_attack (edict_t *self)
{
// is a player right infront?
if (visible(self, self->enemy) &&
infront(self, self->enemy))
{
self->monsterinfo.currentmove = &sentien_move_laser_attack;
}
else
{
self->monsterinfo.currentmove = &sentien_move_post_laser_attack;
sentien_laser_off (self->flash);
}
}
void sentien_post_laser_attack (edict_t *self)
{
self->monsterinfo.currentmove = &sentien_move_post_laser_attack;
sentien_laser_off (self->flash);
}
void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
vec3_t sentien_laser_offset [] =
{
43.8, -22.8 + 1, 43.6 - 0.8,
44.2, -22.9 + 1, 43.9 - 0.8,
43.9, -22.8 + 1, 44.0 - 0.8,
43.2, -23.0 + 1, 44.0 - 0.8,
42.4, -23.4 + 1, 43.9 - 0.8,
42.0, -23.5 + 1, 44.0 - 0.8,
42.4, -23.3 + 1, 44.0 - 0.8,
43.1, -23.1 + 1, 43.9 - 0.8,
43.8, -22.9 + 1, 43.9 - 0.8,
44.2, -22.8 + 1, 44.1 - 0.8,
43.8, -22.8 + 1, 43.5 - 0.8
};
void sentien_do_laser (edict_t *self)
{
vec3_t start, end, forward, right, up;
vec3_t aim, ang;
float r;
int idx;
if (EMPNukeCheck(self, self->s.origin))
{
gi.sound (self, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
return;
}
// if enemy isn't in range anymore, then get to the post laser routine
if (self->s.frame == FRAME_laserStart)
{
sentien_laser_off (self->flash);
self->flash->s.skinnum = 0xf2f2f0f0;
sentien_laser_on (self->flash);
}
idx = (self->s.frame - FRAME_laserStart);
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, sentien_laser_offset[idx],
forward, right, start);
VectorCopy (start, self->flash->s.origin);
if (self->s.frame == FRAME_laserStart)
{
VectorCopy (self->enemy->s.origin, end);
end[2] += self->enemy->viewheight * 66/100;
r = crandom() * 20;
VectorMA (end, r, right, end);
VectorSubtract (end, start, aim);
VectorNormalize(aim);
ANIM_AIM(self, aim);
vectoangles (aim, ang);
VectorCopy (ang, self->flash->s.angles);
G_SetMovedir (self->flash->s.angles, self->flash->movedir);
sentian_sound_att2 (self);
}
}
void sentien_attack (edict_t *self)
{
vec3_t vec;
float range;
float r;
sentien_laser_off (self->flash);
// sentien_run(self); // to test walking
// return;
VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
range = VectorLength (vec);
r = random();
if (range <= 128)
self->monsterinfo.currentmove = &sentien_move_pre_blast_attack;
else if (range <= 500)
{
if (r < 0.50)
self->monsterinfo.currentmove = &sentien_move_pre_blast_attack;
else
self->monsterinfo.currentmove = &sentien_move_pre_laser_attack;
}
else
{
if (r < 0.25)
self->monsterinfo.currentmove = &sentien_move_pre_blast_attack;
else
self->monsterinfo.currentmove = &sentien_move_pre_laser_attack;
}
}
/*=========================================================================
Sentien fending.
=========================================================================*/
void sentien_fend_ready (edict_t *self)
{
if (self->monsterinfo.aiflags2 & AI2_REDUCEDDAMAGE)
return;
self->monsterinfo.pausetime = level.time + 1;
}
void sentien_fend_hold (edict_t *self)
{
if (level.time >= self->monsterinfo.pausetime)
{
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
self->monsterinfo.aiflags2 &= ~AI2_REDUCEDDAMAGE;
}
else
{
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
self->monsterinfo.aiflags2 |= AI2_REDUCEDDAMAGE;
}
}
mframe_t sentien_frames_fend [] =
{
ai_move, 0, sentian_sound_fend,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, sentien_fend_ready,
ai_move, 0, sentien_fend_hold,
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 sentien_move_fend = {FRAME_dodgeStart, FRAME_dodgeEnd, sentien_frames_fend, sentien_run};
void sentien_fend (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
{
// don't flinch if attacking
if (self->monsterinfo.currentmove == &sentien_move_laser_attack ||
self->monsterinfo.currentmove == &sentien_move_blast_attack)
return;
if (skill->value == 0)
{
if (random() > 0.45)
return;
}
else if (skill->value == 1)
{
if (random() > 0.60)
return;
}
else
{
if (random() > 0.80)
return;
}
if (!self->enemy)
self->enemy = attacker;
self->monsterinfo.currentmove = &sentien_move_fend;
}
/*=========================================================================
Touch me sentien.
=========================================================================*/
mframe_t sentien_frames_pain1 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mframe_t sentien_frames_pain2 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mframe_t sentien_frames_pain3 [] =
{
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 sentien_move_pain1 = {FRAME_pain1Start, FRAME_pain1End,
sentien_frames_pain1, sentien_run};
mmove_t sentien_move_pain2 = {FRAME_pain2Start, FRAME_pain2End,
sentien_frames_pain2, sentien_run};
mmove_t sentien_move_pain3 = {FRAME_pain3Start, FRAME_pain3End,
sentien_frames_pain3, sentien_run};
void sentien_pain (edict_t *self, edict_t *other, float kick, int damage)
{
float r;
if (self->health < (self->max_health / 2))
{
self->s.skinnum |= 1;
if (!(self->fogclip & 2)) // custom bloodtype flag check
self->blood_type = 3; // sparks and blood
}
// less than this we don't flinch
if (damage <= 10)
return;
r = random();
if (r < 0.33)
sentian_sound_pain1(self);
else if (r < 0.66)
sentian_sound_pain2(self);
else
{
// sentian_sound_pain3(self);
}
if (level.time < self->pain_debounce_time)
return;
if (self->monsterinfo.aiflags & AI_HOLD_FRAME)
{
return;
}
if (skill->value >= 1)
{
// don't flinch if attacking
if (self->monsterinfo.currentmove == &sentien_move_laser_attack ||
self->monsterinfo.currentmove == &sentien_move_blast_attack)
return;
}
if (skill->value == 3)
return; // no pain anims in nightmare
sentien_laser_off (self->flash);
r = random();
if (damage > 60 && r < 0.3)
self->monsterinfo.currentmove = &sentien_move_pain3;
else if (damage > 30 && r < 0.5)
self->monsterinfo.currentmove = &sentien_move_pain2;
else if (r < 0.7)
self->monsterinfo.currentmove = &sentien_move_pain1;
self->pain_debounce_time = level.time + 3;
}
/*=========================================================================
Kill me Elmo.
=========================================================================*/
mframe_t sentien_frames_death1 [] =
{
ai_move, 0, sentian_sound_die1,
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
};
mframe_t sentien_frames_death2 [] =
{
ai_move, 0, sentian_sound_die2,
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,
ai_move, 0, NULL
};
void sentien_dead (edict_t *self);
mmove_t sentien_move_death1 = {FRAME_die1Start, FRAME_die1End,
sentien_frames_death1, sentien_dead};
mmove_t sentien_move_death2 = {FRAME_die2Start, FRAME_die2End,
sentien_frames_death2, sentien_dead};
vec3_t sentien_death_offset [] =
{
// right, forward
// VectorSet (self->mins, -50, 6, -16);
// VectorSet (self->maxs, -12, 44, 0);
6, -50, 0,
44, -12, 0
};
#define MIN(x, y) (x < y) ? x: y
#define MAX(x, y) (x < y) ? y: x
void sentien_dead (edict_t *self)
{
vec3_t start, end, point;
vec3_t forward, right;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, sentien_death_offset[0],
forward, right, point);
VectorSubtract (point, self->s.origin, start);
G_ProjectSource (self->s.origin, sentien_death_offset[1],
forward, right, point);
VectorSubtract (point, self->s.origin, end);
VectorSet (self->mins, min(start[0], end[0]), min(start[1], end[1]), -16);
VectorSet (self->maxs, max(start[0], end[0]), max(start[1], end[1]), 0);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity(self);
}
void sentien_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
// sentien_laser_off (self->flash);
// Knightmare- free laser on death, as resurrection by a medic will call
// the spawn function again, and would leak the exisiting laser entity.
if (self->flash) {
// gi.dprintf ("removing sentien laser on death.\n");
sentien_laser_off (self->flash);
G_FreeEdict (self->flash);
self->flash = NULL;
}
// end Knightmare
// check for gib
if (self->health <= self->gib_health && !(self->spawnflags & SF_MONSTER_NOGIB))
{
gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
for (n = 0; n < 12; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", 0, 0, damage, GIB_ORGANIC);
for (n = 0; n < 12; n++)
ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", 0, 0, damage, GIB_METALLIC);
for (n = 0; n < 6; n++)
ThrowGib (self, "models/objects/gibs/gear/tris.md2", 0, 0, damage, GIB_METALLIC);
ThrowGib (self, "models/objects/gibs/chest/tris.md2", 0, 0, damage, GIB_ORGANIC);
ThrowHead (self, "models/objects/gibs/gear/tris.md2", 0, 0, damage, GIB_METALLIC);
self->deadflag = DEAD_DEAD;
return;
}
if (self->deadflag == DEAD_DEAD)
return;
// regular death
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->s.skinnum |= 1;
if (!(self->fogclip & 2)) // custom bloodtype flag check
self->blood_type = 3; // sparks and blood
if (random() < 0.80)
self->monsterinfo.currentmove = &sentien_move_death1;
else
self->monsterinfo.currentmove = &sentien_move_death2;
}
/*=========================================================================
Spawn code.
=========================================================================*/
void create_sentien_laser (edict_t *self)
{
vec3_t start;
vec3_t forward, right;
self->flash = G_Spawn();
self->flash->movetype = MOVETYPE_NONE;
self->flash->solid = SOLID_BBOX;//SOLID_NOT;
self->flash->s.renderfx = RF_BEAM|RF_TRANSLUCENT;
self->flash->s.modelindex = 2;
// self->flash->s.sound = gi.soundindex ("world/laser.wav");
self->flash->classname = "laser_yaya";
self->flash->s.frame = 2;
self->flash->owner = self;
self->flash->s.skinnum = 0xd0d1d2d3;
self->flash->dmg = 8;
self->flash->think = sentien_laser_think;
AngleVectors(self->s.angles, forward, right, NULL);
G_ProjectSource(self->s.origin, sentien_laser_offset[0],
forward, right, start);
VectorCopy(start, self->flash->s.origin);
VectorCopy(self->s.angles, self->flash->s.angles);
G_SetMovedir(self->flash->s.angles, self->flash->movedir);
gi.linkentity (self->flash);
sentien_laser_off (self->flash);
}
void SP_monster_sentien_precache (void)
{
sound_idle1 = gi.soundindex("monsters/sentien/sen_idle1.wav");
sound_idle2 = gi.soundindex("monsters/sentien/sen_idle2.wav");
// sound_idle3 = gi.soundindex("monsters/sentien/sen_idle3.wav");
sound_walk = gi.soundindex("monsters/sentien/sen_walk.wav");
sound_fend = gi.soundindex("monsters/sentien/sen_fend.wav");
sound_pain1 = gi.soundindex("monsters/sentien/sen_pain1.wav");
sound_pain2 = gi.soundindex("monsters/sentien/sen_pain2.wav");
// sound_pain3 = gi.soundindex("monsters/sentien/sen_pain3.wav");
sound_die1 = gi.soundindex("monsters/sentien/sen_die1.wav");
sound_die2 = gi.soundindex("monsters/sentien/sen_die2.wav");
sound_att1 = gi.soundindex("monsters/sentien/sen_att1.wav");
sound_att2 = gi.soundindex("monsters/sentien/sen_att2.wav");
// sound_att3 = gi.soundindex("monsters/sentien/sen_att3.wav");
}
/*QUAKED monster_sentien (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight GoodGuy NoGib
model="models/monsters/sentien/"
*/
void SP_monster_sentien (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
SP_monster_sentien_precache();
if (!self->health)
self->health = 900;
if (!self->gib_health)
self->gib_health = -425;
if (!self->mass)
self->mass = 500;
// Lazarus: special purpose skins
if ( self->style )
{
PatchMonsterModel("models/monsters/sentien/tris.md2");
self->s.skinnum = self->style * 2;
}
self->s.modelindex = gi.modelindex ("models/monsters/sentien/tris.md2");
VectorSet (self->mins, -32, -32, -16);
VectorSet (self->maxs, 32, 32, 72);
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->yaw_speed = 10;
self->random = 1;
// setup the functions
self->pain = sentien_pain;
self->die = sentien_die;
self->monsterinfo.stand = sentien_stand;
self->monsterinfo.walk = sentien_walk;
self->monsterinfo.run = sentien_run;
self->monsterinfo.dodge = sentien_fend;
self->monsterinfo.attack = sentien_attack;
self->monsterinfo.melee = NULL;
self->monsterinfo.sight = NULL;
self->monsterinfo.idle = NULL;
self->monsterinfo.reducedDamageAmount = 0.85;
// Lazarus
if (!self->blood_type)
self->blood_type = 2; // sparks
else
self->fogclip |= 2; // custom bloodtype flag
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;
}
if (!self->monsterinfo.flies)
self->monsterinfo.flies = 0.10;
self->common_name = "Sentien";
self->class_id = ENTITY_MONSTER_SENTIEN;
// end Lazarus
gi.linkentity (self);
self->flash = NULL;
gi.linkentity(self);
create_sentien_laser (self);
if (skill->value == 2)
{
self->flash->dmg *= 1.5;
self->yaw_speed *= 1.5;
}
else if (skill->value >= 3)
{
self->flash->dmg *= 2.5;
self->yaw_speed *= 2;
}
self->monsterinfo.currentmove = &sentien_move_stand1;
if (self->health < 0)
{
mmove_t *deathmoves[] = {&sentien_move_death1,
&sentien_move_death2,
NULL};
M_SetDeath (self, (mmove_t **)&deathmoves);
}
self->monsterinfo.scale = MODEL_SCALE;
walkmonster_start(self);
}