thirtyflightsofloving/game/m_flyer.c
Knightmare66 d16b46e3cf Added re-implementation of func_plat2 and func_door2 from rogue to default Lazarus DLL.
Overhauled child entity movement in default Lazarus DLL.
Added bbox versions of various triggers to default Lazarus DLL.
Added level.maptype field to default Lazarus DLL.
Added entity class IDs to default Lazarus DLL.
Incremented savegame version for default Lazarus DLL.
2020-10-27 02:00:05 -04:00

666 lines
16 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Lazarus Quake 2 Mod source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/*
==============================================================================
flyer
==============================================================================
*/
#include "g_local.h"
#include "m_flyer.h"
qboolean visible (edict_t *self, edict_t *other);
static int nextmove; // Used for start/stop frames
static int sound_sight;
static int sound_idle;
static int sound_pain1;
static int sound_pain2;
static int sound_slash;
static int sound_sproing;
static int sound_die;
void flyer_check_melee(edict_t *self);
void flyer_loop_melee (edict_t *self);
void flyer_melee (edict_t *self);
void flyer_setstart (edict_t *self);
void flyer_stand (edict_t *self);
void flyer_nextmove (edict_t *self);
void flyer_sight (edict_t *self, edict_t *other)
{
gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
}
void flyer_idle (edict_t *self)
{
// if (!(self->spawnflags & SF_MONSTER_AMBUSH))
gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
}
void flyer_pop_blades (edict_t *self)
{
gi.sound (self, CHAN_VOICE, sound_sproing, 1, ATTN_NORM, 0);
}
mframe_t flyer_frames_stand [] =
{
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,
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 flyer_move_stand = {FRAME_stand01, FRAME_stand45, flyer_frames_stand, NULL};
mframe_t flyer_frames_walk [] =
{
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL,
ai_walk, 5, NULL
};
mmove_t flyer_move_walk = {FRAME_stand01, FRAME_stand45, flyer_frames_walk, NULL};
mframe_t flyer_frames_run [] =
{
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL,
ai_run, 10, NULL
};
mmove_t flyer_move_run = {FRAME_stand01, FRAME_stand45, flyer_frames_run, NULL};
void flyer_run (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
self->monsterinfo.currentmove = &flyer_move_stand;
else
self->monsterinfo.currentmove = &flyer_move_run;
}
void flyer_walk (edict_t *self)
{
self->monsterinfo.currentmove = &flyer_move_walk;
}
void flyer_stand (edict_t *self)
{
self->monsterinfo.currentmove = &flyer_move_stand;
}
mframe_t flyer_frames_start [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, flyer_nextmove
};
mmove_t flyer_move_start = {FRAME_start01, FRAME_start06, flyer_frames_start, NULL};
mframe_t flyer_frames_stop [] =
{
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, flyer_nextmove
};
mmove_t flyer_move_stop = {FRAME_stop01, FRAME_stop07, flyer_frames_stop, NULL};
void flyer_stop (edict_t *self)
{
self->monsterinfo.currentmove = &flyer_move_stop;
}
void flyer_start (edict_t *self)
{
self->monsterinfo.currentmove = &flyer_move_start;
}
mframe_t flyer_frames_rollright [] =
{
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 flyer_move_rollright = {FRAME_rollr01, FRAME_rollr09, flyer_frames_rollright, NULL};
mframe_t flyer_frames_rollleft [] =
{
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 flyer_move_rollleft = {FRAME_rollf01, FRAME_rollf09, flyer_frames_rollleft, NULL};
mframe_t flyer_frames_pain3 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t flyer_move_pain3 = {FRAME_pain301, FRAME_pain304, flyer_frames_pain3, flyer_run};
mframe_t flyer_frames_pain2 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t flyer_move_pain2 = {FRAME_pain201, FRAME_pain204, flyer_frames_pain2, flyer_run};
mframe_t flyer_frames_pain1 [] =
{
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 flyer_move_pain1 = {FRAME_pain101, FRAME_pain109, flyer_frames_pain1, flyer_run};
mframe_t flyer_frames_defense [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL, // Hold this frame
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t flyer_move_defense = {FRAME_defens01, FRAME_defens06, flyer_frames_defense, NULL};
mframe_t flyer_frames_bankright [] =
{
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 flyer_move_bankright = {FRAME_bankr01, FRAME_bankr07, flyer_frames_bankright, NULL};
mframe_t flyer_frames_bankleft [] =
{
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 flyer_move_bankleft = {FRAME_bankl01, FRAME_bankl07, flyer_frames_bankleft, NULL};
void flyer_fire (edict_t *self, int flash_number)
{
vec3_t start;
vec3_t forward, right;
vec3_t end;
vec3_t dir;
int effect;
if ((self->s.frame == FRAME_attak204) || (self->s.frame == FRAME_attak207) || (self->s.frame == FRAME_attak210))
effect = EF_HYPERBLASTER;
else
effect = 0;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], 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, 1, 1000, flash_number, effect, BLASTER_ORANGE);
}
void flyer_fireleft (edict_t *self)
{
flyer_fire (self, MZ2_FLYER_BLASTER_1);
}
void flyer_fireright (edict_t *self)
{
flyer_fire (self, MZ2_FLYER_BLASTER_2);
}
mframe_t flyer_frames_attack2 [] =
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, -10, flyer_fireleft, // left gun
ai_charge, -10, flyer_fireright, // right gun
ai_charge, -10, flyer_fireleft, // left gun
ai_charge, -10, flyer_fireright, // right gun
ai_charge, -10, flyer_fireleft, // left gun
ai_charge, -10, flyer_fireright, // right gun
ai_charge, -10, flyer_fireleft, // left gun
ai_charge, -10, flyer_fireright, // right gun
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 flyer_move_attack2 = {FRAME_attak201, FRAME_attak217, flyer_frames_attack2, flyer_run};
void flyer_slash_left (edict_t *self)
{
vec3_t aim;
VectorSet (aim, MELEE_DISTANCE, self->mins[0], 0);
fire_hit (self, aim, 5, 0);
gi.sound (self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
}
void flyer_slash_right (edict_t *self)
{
vec3_t aim;
VectorSet (aim, MELEE_DISTANCE, self->maxs[0], 0);
fire_hit (self, aim, 5, 0);
gi.sound (self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
}
mframe_t flyer_frames_start_melee [] =
{
ai_charge, 0, flyer_pop_blades,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
mmove_t flyer_move_start_melee = {FRAME_attak101, FRAME_attak106, flyer_frames_start_melee, flyer_loop_melee};
mframe_t flyer_frames_end_melee [] =
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL
};
mmove_t flyer_move_end_melee = {FRAME_attak119, FRAME_attak121, flyer_frames_end_melee, flyer_run};
mframe_t flyer_frames_loop_melee [] =
{
ai_charge, 0, NULL, // Loop Start
ai_charge, 0, NULL,
ai_charge, 0, flyer_slash_left, // Left Wing Strike
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, flyer_slash_right, // Right Wing Strike
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL // Loop Ends
};
mmove_t flyer_move_loop_melee = {FRAME_attak107, FRAME_attak118, flyer_frames_loop_melee, flyer_check_melee};
void flyer_loop_melee (edict_t *self)
{
/* if (random() <= 0.5)
self->monsterinfo.currentmove = &flyer_move_attack1;
else */
self->monsterinfo.currentmove = &flyer_move_loop_melee;
}
void flyer_attack (edict_t *self)
{
/* if (random() <= 0.5)
self->monsterinfo.currentmove = &flyer_move_attack1;
else */
self->monsterinfo.currentmove = &flyer_move_attack2;
}
void flyer_setstart (edict_t *self)
{
nextmove = ACTION_run;
self->monsterinfo.currentmove = &flyer_move_start;
}
void flyer_nextmove (edict_t *self)
{
if (nextmove == ACTION_attack1)
self->monsterinfo.currentmove = &flyer_move_start_melee;
else if (nextmove == ACTION_attack2)
self->monsterinfo.currentmove = &flyer_move_attack2;
else if (nextmove == ACTION_run)
self->monsterinfo.currentmove = &flyer_move_run;
}
void flyer_melee (edict_t *self)
{
// flyer.nextmove = ACTION_attack1;
// self->monsterinfo.currentmove = &flyer_move_stop;
self->monsterinfo.currentmove = &flyer_move_start_melee;
}
void flyer_check_melee(edict_t *self)
{
if (range (self, self->enemy) == RANGE_MELEE)
if (random() <= 0.8)
self->monsterinfo.currentmove = &flyer_move_loop_melee;
else
self->monsterinfo.currentmove = &flyer_move_end_melee;
else
self->monsterinfo.currentmove = &flyer_move_end_melee;
}
void flyer_pain (edict_t *self, edict_t *other, float kick, int damage)
{
int n;
if (self->health < (self->max_health / 2))
self->s.skinnum |= 1;
if (level.time < self->pain_debounce_time)
return;
self->pain_debounce_time = level.time + 3;
if (skill->value == 3)
return; // no pain anims in nightmare
n = rand() % 3;
if (n == 0)
{
gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &flyer_move_pain1;
}
else if (n == 1)
{
gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &flyer_move_pain2;
}
else
{
gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &flyer_move_pain3;
}
}
void flyer_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
// Knightmare- gibs!
for (n= 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
for (n= 0; n < 2; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
BecomeExplosion1(self);
}
/*QUAKED monster_flyer (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
*/
void SP_monster_flyer (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
// fix a map bug in jail5.bsp
if (!Q_stricmp(level.mapname, "jail5") && (self->s.origin[2] == -104))
{
self->targetname = self->target;
self->target = NULL;
}
sound_sight = gi.soundindex ("flyer/flysght1.wav");
sound_idle = gi.soundindex ("flyer/flysrch1.wav");
sound_pain1 = gi.soundindex ("flyer/flypain1.wav");
sound_pain2 = gi.soundindex ("flyer/flypain2.wav");
sound_slash = gi.soundindex ("flyer/flyatck2.wav");
sound_sproing = gi.soundindex ("flyer/flyatck1.wav");
sound_die = gi.soundindex ("flyer/flydeth1.wav");
gi.soundindex ("flyer/flyatck3.wav");
// Lazarus: special purpose skins
if ( self->style )
{
PatchMonsterModel("models/monsters/flyer/tris.md2");
self->s.skinnum = self->style * 2;
}
self->s.modelindex = gi.modelindex ("models/monsters/flyer/tris.md2");
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, 16);
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->s.sound = gi.soundindex ("flyer/flyidle1.wav");
// Lazarus: mapper-configurable health
if (!self->health)
self->health = 50;
if (!self->mass)
self->mass = 50;
self->pain = flyer_pain;
self->die = flyer_die;
self->monsterinfo.stand = flyer_stand;
self->monsterinfo.walk = flyer_walk;
self->monsterinfo.run = flyer_run;
self->monsterinfo.attack = flyer_attack;
self->monsterinfo.melee = flyer_melee;
self->monsterinfo.sight = flyer_sight;
self->monsterinfo.idle = flyer_idle;
// Knightmare- added sparks and blood type
if (!self->blood_type)
self->blood_type = 3; //sparks and blood
// Lazarus
if (self->powerarmor) {
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = self->powerarmor;
}
self->common_name = "Flyer";
self->class_id = ENTITY_MONSTER_FLYER;
gi.linkentity (self);
self->monsterinfo.currentmove = &flyer_move_stand;
self->monsterinfo.scale = MODEL_SCALE;
flymonster_start (self);
}