Made Tactician Gunner lead target based on random chance and skill level.

Changed Tactican Gunner's min prox range to 320.
Added bad_area fields to monster-fired prox mines with new g_newai.c->MarkProxArea() function.
Misc AI tweaks to handle prox bad_area fields.
Fixed flechettes clipping against player movement.
Cleaned up some old code from borderless window support.
This commit is contained in:
Knightmare66 2021-07-26 02:55:41 -04:00
parent 59157b413d
commit 74c1930cf5
12 changed files with 360 additions and 126 deletions

View file

@ -320,8 +320,9 @@ void ai_charge (edict_t *self, float dist)
// circle strafe support
if (self->monsterinfo.attack_state == AS_SLIDING)
{
// if we're fighting a tesla, NEVER circle strafe
if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")))
// if we're fighting a tesla or prox, NEVER circle strafe
// if ( (self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")) )
if ( (self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")) && (!strcmp(self->enemy->classname, "prox")) )
ofs = 0;
else if (self->monsterinfo.lefty)
ofs = 90;
@ -1228,8 +1229,9 @@ qboolean M_CheckAttack (edict_t *self)
else
strafe_chance = 0.6;
// if enemy is tesla, never strafe
if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")))
// if enemy is tesla or prox, never strafe
// if ( (self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")) )
if ( (self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")) && (!strcmp(self->enemy->classname, "prox")) )
strafe_chance = 0;
if (random() < strafe_chance)

View file

@ -2219,6 +2219,7 @@ extern void M_MonsterDodge ( edict_t * self , edict_t * attacker , float eta , t
extern void drawbbox ( edict_t * self ) ;
extern qboolean below ( edict_t * self , edict_t * other ) ;
extern void PredictAim ( edict_t * target , vec3_t start , float bolt_speed , qboolean eye_height , float offset , vec3_t aimdir , vec3_t aimpoint ) ;
extern qboolean MarkProxArea ( edict_t * prox ) ;
extern qboolean MarkTeslaArea ( edict_t * self , edict_t * tesla ) ;
extern edict_t * CheckForBadArea ( edict_t * ent ) ;
extern edict_t * SpawnBadArea ( vec3_t mins , vec3_t maxs , float lifespan , edict_t * owner ) ;

View file

@ -2219,6 +2219,7 @@
{"drawbbox", (byte *)drawbbox},
{"below", (byte *)below},
{"PredictAim", (byte *)PredictAim},
{"MarkProxArea", (byte *)MarkProxArea},
{"MarkTeslaArea", (byte *)MarkTeslaArea},
{"CheckForBadArea", (byte *)CheckForBadArea},
{"SpawnBadArea", (byte *)SpawnBadArea},

View file

@ -135,7 +135,7 @@
#define FL2_TURRET_DOUBLE_ALT 0x00000002 // this turret alternates firing its barrels (style is set)
#define FL2_TURRET_DOUBLE_ALT_FIRING 0x00000004 // secondary barrel in use for alternate firing
#define FL2_CRUCIFIED 0x00000008 // insane is crucified
#define FL2_COMMANDER 0x00000008 // Gunner Commander internal flag
#define FL2_COMMANDER 0x00000008 // Medic Commander / Daedalus internal flag
#define FL2_WEAPON_ALT 0x00000010 // plasma guard has spread mode
#define FL2_DO_NOT_REFLECT 0x00000020 // do not reflect this entity
@ -255,7 +255,7 @@ typedef enum
#define AI_EVADE_GRENADE 0x80000000
// Knightmare- thes are for aiflags2
#define AI2_FREEFORALL 0x00000001 // Set by target_monsterbattle, lets dmgteam monsters
#define AI2_FREEFORALL 0x00000001 // Set by target_monsterbattle, lets dmgteam monsters
// attack monsters on opposion dmgteam
#define AI2_RANGE_PAUSE 0x00000002
#define AI2_HINT_TEST 0x00000004
@ -266,6 +266,8 @@ typedef enum
#define AI2_MONREDUCEDDAMAGE 0x00000040
#define AI2_ONESHOTTARGET 0x00000080
// end Zaero
#define AI2_LEAD_TARGET 0x00000100
// Knightmare- monster flags
#define MFL_WALK_WALLS 1
@ -309,7 +311,7 @@ typedef enum
#define SFL_CROSS_TRIGGER_6 0x00000020
#define SFL_CROSS_TRIGGER_7 0x00000040
#define SFL_CROSS_TRIGGER_8 0x00000080
#define SFL_CROSS_TRIGGER_MASK 0x000000ff
#define SFL_CROSS_TRIGGER_MASK 0x000000ff // 0xffffe0ff would allow 27 trigger bits
// noise types for PlayerNoise
@ -1772,6 +1774,7 @@ float realrange (edict_t *self, edict_t *other);
edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner);
edict_t *CheckForBadArea(edict_t *ent);
qboolean MarkTeslaArea(edict_t *self, edict_t *tesla);
qboolean MarkProxArea (edict_t *prox); // Knightmare added
void InitHintPaths (void);
void PredictAim (edict_t *target, vec3_t start, float bolt_speed, qboolean eye_height, float offset, vec3_t aimdir, vec3_t aimpoint);
qboolean below (edict_t *self, edict_t *other);

View file

@ -6,13 +6,13 @@
//===============================
/*
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_DEBUGTRAIL);
gi.WritePosition (pt1);
gi.WritePosition (pt2);
gi.multicast (pt1, MULTICAST_PVS);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_DEBUGTRAIL);
gi.WritePosition (pt1);
gi.WritePosition (pt2);
gi.multicast (pt1, MULTICAST_PVS);
self->nextthink = level.time + 10;
self->nextthink = level.time + 10;
*/
// plat states, copied from g_func.c
@ -83,20 +83,21 @@ qboolean blocked_checkshot (edict_t *self, float shotChance)
}
playerVisible = visible (self, self->enemy);
// always shoot at teslas
// always shoot at teslas and prox
if (playerVisible)
{
if (!strcmp(self->enemy->classname, "tesla"))
// if (!strcmp(self->enemy->classname, "tesla"))
if ( !strcmp(self->enemy->classname, "tesla") || !strcmp(self->enemy->classname, "prox") )
{
// if (g_showlogic && g_showlogic->value)
// gi.dprintf("blocked: taking a shot\n");
// if (g_showlogic && g_showlogic->value)
// gi.dprintf("blocked: taking a shot\n");
// turn on AI_BLOCKED to let the monster know the attack is being called
// by the blocked functions...
self->monsterinfo.aiflags |= AI_BLOCKED;
if (self->monsterinfo.attack)
self->monsterinfo.attack(self);
self->monsterinfo.attack (self);
self->monsterinfo.aiflags &= ~AI_BLOCKED;
return true;
@ -1278,21 +1279,21 @@ void badarea_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *s
// drawbbox(ent);
}
edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
edict_t *SpawnBadArea (vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
{
edict_t *badarea;
vec3_t origin;
VectorAdd(mins, maxs, origin);
VectorScale(origin, 0.5, origin);
VectorAdd (mins, maxs, origin);
VectorScale (origin, 0.5, origin);
VectorSubtract(maxs, origin, maxs);
VectorSubtract(mins, origin, mins);
VectorSubtract (maxs, origin, maxs);
VectorSubtract (mins, origin, mins);
badarea = G_Spawn();
VectorCopy(origin, badarea->s.origin);
VectorCopy(maxs, badarea->maxs);
VectorCopy(mins, badarea->mins);
VectorCopy (origin, badarea->s.origin);
VectorCopy (maxs, badarea->maxs);
VectorCopy (mins, badarea->mins);
badarea->touch = badarea_touch;
badarea->movetype = MOVETYPE_NONE;
badarea->solid = SOLID_TRIGGER;
@ -1316,7 +1317,7 @@ edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
// CheckForBadArea
// This is a customized version of G_TouchTriggers that will check
// for bad area triggers and return them if they're touched.
edict_t *CheckForBadArea(edict_t *ent)
edict_t *CheckForBadArea (edict_t *ent)
{
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
@ -1350,9 +1351,7 @@ edict_t *CheckForBadArea(edict_t *ent)
qboolean MarkTeslaArea (edict_t *self, edict_t *tesla)
{
vec3_t mins, maxs;
edict_t *e;
edict_t *tail;
edict_t *area;
edict_t *e, *tail, *area;
if (!tesla || !self)
return false;
@ -1397,18 +1396,78 @@ qboolean MarkTeslaArea (edict_t *self, edict_t *tesla)
VectorSet (mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, tesla->mins[2]);
VectorSet (maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
area = SpawnBadArea (mins, maxs, 30, tesla);
// area = SpawnBadArea (mins, maxs, 30.0f, tesla);
if (tesla->air_finished)
area = SpawnBadArea (mins, maxs, tesla->air_finished, tesla);
else
area = SpawnBadArea (mins, maxs, tesla->nextthink, tesla);
}
// if we spawned a bad area, then link it to the tesla
if (area)
{
// gi.dprintf("bad area marker spawned and linked to tesla\n");
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf("Bad area marker spawned and linked to tesla\n");
tail->teamchain = area;
}
return true;
}
// Knightmare added
#define PROX_DAMAGE_RADIUS 192
qboolean MarkProxArea (edict_t *prox)
{
vec3_t mins, maxs;
edict_t *e, *tail, *area;
if (!prox)
return false;
area = NULL;
// make sure this prox doesn't have a bad area around it already...
e = prox->teamchain;
tail = prox;
while (e)
{
tail = tail->teamchain;
if (!strcmp(e->classname, "bad_area"))
{
// gi.dprintf("prox already has a bad area marked\n");
return false;
}
e = e->teamchain;
}
// see if we can grab the dmg_radius, else use macro'd default
if (prox->dmg_radius > 0) {
VectorSet (mins, -prox->dmg_radius, -prox->dmg_radius, -prox->dmg_radius);
VectorSet (maxs, prox->dmg_radius, prox->dmg_radius, prox->dmg_radius);
}
else {
VectorSet (mins, -PROX_DAMAGE_RADIUS, -PROX_DAMAGE_RADIUS, -PROX_DAMAGE_RADIUS);
VectorSet (maxs, PROX_DAMAGE_RADIUS, PROX_DAMAGE_RADIUS, PROX_DAMAGE_RADIUS);
}
// use the prox timer if available
if (prox->delay > 0)
area = SpawnBadArea (mins, maxs, prox->delay, prox);
else if (prox->nextthink > 0)
area = SpawnBadArea (mins, maxs, prox->nextthink, prox);
else
area = SpawnBadArea (mins, maxs, 30, prox);
// if we spawned a bad area, then link it to the prox
if (area)
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf("Bad area marker spawned and linked to prox\n");
tail->teamchain = area;
}
return true;
}
// end Knightmare
// predictive calculator
// target is who you want to shoot
@ -1759,15 +1818,15 @@ void TargetTesla (edict_t *self, edict_t *tesla)
{
if (self->health <= 0)
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("bad tesla attack avoided!\n");
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("bad tesla attack avoided!\n");
return;
}
self->monsterinfo.attack(self);
self->monsterinfo.attack (self);
}
else
{
FoundTarget(self);
FoundTarget (self);
}
}
}

View file

@ -77,6 +77,7 @@ void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int sp
VectorNormalize (dir);
flechette = G_Spawn();
flechette->svflags |= SVF_DEADMONSTER; // Knightmare- don't clip players against these projectiles!
flechette->classname = "flechette";
flechette->class_id = ENTITY_FLECHETTE;
VectorCopy (start, flechette->s.origin);
@ -88,7 +89,7 @@ void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int sp
flechette->clipmask = MASK_SHOT;
flechette->solid = SOLID_BBOX;
flechette->s.renderfx = RF_FULLBRIGHT;
flechette->s.renderfx |= RF_NOSHADOW; //Knightmare- no shadow
flechette->s.renderfx |= RF_NOSHADOW; // Knightmare- no shadow
VectorClear (flechette->mins);
VectorClear (flechette->maxs);
@ -157,10 +158,10 @@ void Prox_Explode (edict_t *ent);
// Knightmare- move prox and trigger field with host
void prox_movewith_host (edict_t *self)
{
edict_t *host;
vec3_t forward, right, up, offset;
vec3_t host_angle_change, amove;
int count = 0;
edict_t *host, *field, *area;
vec3_t forward, right, up, offset;
vec3_t host_angle_change, amove;
int iteration = 0;
// explode if parent has been destroyed
if (!self->movewith_ent || !self->movewith_ent->inuse)
@ -178,8 +179,21 @@ void prox_movewith_host (edict_t *self)
self->movetype = MOVETYPE_TOSS;
self->movewith_ent = NULL;
self->movewith_set = 0;
self->teamchain->movewith_ent = NULL;
self->teamchain->movewith_set = 0;
// delink field
// self->teamchain->movewith_ent = NULL;
// self->teamchain->movewith_set = 0;
if ( self->teamchain && (self->teamchain->owner == self) )
{
field = self->teamchain;
field->movewith_ent = NULL;
field->movewith_set = 0;
// now delink badarea
if (field->teamchain) {
area = field->teamchain;
area->movewith_ent = NULL;
area->movewith_set = 0;
}
}
self->postthink = NULL;
return;
}
@ -187,6 +201,10 @@ void prox_movewith_host (edict_t *self)
movefield:
if (!self) // more paranoia
return;
// if (!strcmp(self->classname, "bad_area") && (g_showlogic) && (g_showlogic->value))
// gi.dprintf ("prox_movewith_host: Moving badarea for tesla\n");
self->movetype = MOVETYPE_PUSH;
if (!self->movewith_set)
{
@ -265,10 +283,11 @@ movefield:
}
self->s.event = host->s.event;
gi.linkentity (self);
if (count < 1 && self->teamchain) // now move the trigger field
// if (iteration < 1 && self->teamchain) // now move the trigger field
if (iteration < 2 && self->teamchain) // now move the trigger field and badarea
{
self = self->teamchain;
count++;
iteration++;
goto movefield;
}
}
@ -276,7 +295,7 @@ movefield:
void Prox_Explode (edict_t *ent)
{
vec3_t origin;
edict_t *owner;
edict_t *owner, *cur, *next;
int type;
// Grenade_Remove_From_Chain (ent);
@ -284,8 +303,20 @@ void Prox_Explode (edict_t *ent)
// free the trigger field
// PMM - changed teammaster to "mover" .. owner of the field is the prox
if (ent->teamchain && ent->teamchain->owner == ent)
G_FreeEdict(ent->teamchain);
// Knightmare- also free badarea
if ( ent->teamchain && (ent->teamchain->owner == ent) )
// G_FreeEdict(ent->teamchain);
{
cur = ent->teamchain;
while (cur)
{
// if (!strcmp(cur->classname, "bad_area") && (g_showlogic) && (g_showlogic->value))
// gi.dprintf ("Freeing badarea for tesla\n");
next = cur->teamchain;
G_FreeEdict (cur);
cur = next;
}
}
owner = ent;
if (ent->teammaster)
@ -333,7 +364,7 @@ void prox_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage,
{
// gi.dprintf("prox_die\n");
// if set off by another prox, delay a little (chained explosions)
if (strcmp(inflictor->classname, "prox"))
if (strcmp(inflictor->classname, "prox") != 0)
{
self->takedamage = DAMAGE_NO;
Prox_Explode(self);
@ -539,7 +570,7 @@ void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE))
{
if (other != ent->teammaster)
Prox_Explode(ent);
Prox_Explode (ent);
return;
}
@ -560,7 +591,7 @@ void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("bad normal for surface, exploding!\n");
Prox_Explode(ent);
Prox_Explode (ent);
return;
}
// Knightmare- stick to bmodels
@ -615,7 +646,7 @@ void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("stuck on entity, blowing up!\n");
Prox_Explode(ent);
Prox_Explode (ent);
return;
}
return;
@ -676,6 +707,11 @@ void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
// if (other->movetype == MOVETYPE_PUSHABLE)
// gi.dprintf("prox successfully attached to func_pushable\n");
gi.linkentity(ent);
// Knightmare- mark monster-fired prox mines for AI avoidance
if ( ent->owner && (ent->owner->svflags & SVF_MONSTER) ) {
MarkProxArea (ent);
}
}
//===============
@ -1986,7 +2022,7 @@ void tesla_remove (edict_t *self)
while (cur)
{
next = cur->teamchain;
G_FreeEdict ( cur );
G_FreeEdict (cur);
cur = next;
}
}

View file

@ -400,7 +400,9 @@ void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator)
// if going to a new unit, clear cross triggers
if (strstr(self->map, "*"))
{
game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
// Knightmare- game.serverflags is ONLY used for cross-level trigger bits, so we can just zero it.
// game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
game.serverflags = 0;
game.lock_code[0] = 0;
game.lock_revealed = 0;
game.lock_hud = 0;
@ -1010,7 +1012,13 @@ Once this trigger is touched/used, any trigger_crosslevel_target with the same t
*/
void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
{
game.serverflags |= self->spawnflags;
// Knightmare- use moreflags to override spawnflags, allows use of 32 trigger bits
if (self->moreflags != 0) {
game.serverflags |= self->moreflags;
}
else {
game.serverflags |= self->spawnflags;
}
G_FreeEdict (self);
}
@ -1030,10 +1038,24 @@ killtarget also work.
*/
void target_crosslevel_target_think (edict_t *self)
{
if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
/* if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
{
G_UseTargets (self, self);
G_FreeEdict (self);
}*/
// Knightmare- use moreflags to override spawnflags, allows use of 32 trigger bits
qboolean triggered = false;
if (self->moreflags != 0) {
triggered = (self->moreflags == (game.serverflags & self->moreflags));
}
else {
triggered = (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags));
}
if (triggered) {
G_UseTargets (self, self);
G_FreeEdict (self);
}
}

View file

@ -16,9 +16,21 @@ static int sound_idle;
static int sound_open;
static int sound_search;
static int sound_sight;
// Knightmare- Tactician Gunner sounds
#ifndef KMQUAKE2_ENGINE_MOD
static int sound_fire_flechette;
static int tactician_sound_fire_flechette;
#endif // KMQUAKE2_ENGINE_MOD
/*
static int tactician_sound_pain;
static int tactician_sound_pain2;
static int tactician_sound_death;
static int tactician_sound_idle;
static int tactician_sound_open;
static int tactician_sound_search;
static int tactician_sound_sight;
*/
// end Knightmare
// NOTE: Original gunner grenade velocity was 600 units/sec, but then
// fire_grenade added 200 units/sec in a direction perpendicular
@ -31,18 +43,28 @@ static int sound_fire_flechette;
void gunner_idlesound (edict_t *self)
{
if (!(self->spawnflags & SF_MONSTER_AMBUSH))
gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
if ( !(self->spawnflags & SF_MONSTER_AMBUSH) ) {
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_idle, 1, ATTN_IDLE, 0);
else */
gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
}
}
void gunner_sight (edict_t *self, edict_t *other)
{
gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_sight, 1, ATTN_NORM, 0);
else */
gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
}
void gunner_search (edict_t *self)
{
gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_search, 1, ATTN_NORM, 0);
else */
gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
}
@ -291,10 +313,18 @@ void gunner_pain (edict_t *self, edict_t *other, float kick, int damage)
self->pain_debounce_time = level.time + 3;
if (rand()&1)
gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
else
gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
if (rand() & 1) {
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_pain, 1, ATTN_NORM, 0);
else */
gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
}
else {
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_pain2, 1, ATTN_NORM, 0);
else */
gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
}
if (skill->value == 3)
return; // no pain anims in nightmare
@ -371,13 +401,16 @@ void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damag
return;
// regular death
gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_death, 1, ATTN_NORM, 0);
else */
gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->monsterinfo.currentmove = &gunner_move_death;
}
qboolean gunner_grenade_check(edict_t *self)
qboolean gunner_grenade_check (edict_t *self)
{
vec3_t start;
vec3_t forward, right;
@ -385,7 +418,7 @@ qboolean gunner_grenade_check(edict_t *self)
trace_t tr;
vec3_t dir;
vec3_t vhorz;
float horz,vertmax;
float horz, vertmax, dangerClose;
if (!self->enemy)
return false;
@ -397,15 +430,19 @@ qboolean gunner_grenade_check(edict_t *self)
// Lazarus: We can do better than that... see below
// check to see that we can trace to the player before we start
// tossing grenades around.
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_GUNNER_GRENADE_1], forward, right, start);
// see if we're too close
// Knightmare- Tactician Gunner's prox mines stick around, so only use at longer range
if (self->moreflags & FL2_COMMANDER)
dangerClose = 320.0f;
else
dangerClose = 100.0f;
VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
if (VectorLength(dir) < 100)
if (VectorLength(dir) < dangerClose)
return false;
// Lazarus: Max vertical distance - this is approximate and conservative
@ -476,7 +513,10 @@ mmove_t gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunn
void gunner_opengun (edict_t *self)
{
gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
/* if (self->moreflags & FL2_COMMANDER)
gi.sound (self, CHAN_VOICE, tactician_sound_open, 1, ATTN_IDLE, 0);
else */
gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
}
void GunnerFire (edict_t *self)
@ -485,7 +525,9 @@ void GunnerFire (edict_t *self)
vec3_t forward, right;
vec3_t target;
vec3_t aim;
vec3_t targ_vel;
int flash_number;
float dist, time, chance, flechetteSpeed = 850.0f;
if (!self->enemy || !self->enemy->inuse) //PGM
return; //PGM
@ -516,6 +558,17 @@ void GunnerFire (edict_t *self)
target[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
}
// Knightmare- Tactician Gunner leads the target
if ( (self->moreflags & FL2_COMMANDER) && (self->monsterinfo.aiflags2 & AI2_LEAD_TARGET) )
{
VectorSubtract (target, start, aim);
dist = VectorLength(aim);
time = dist / flechetteSpeed; // was 1000.0f
VectorCopy (self->enemy->velocity, targ_vel);
targ_vel[2] = min(targ_vel[2], 0.0f); // ignore z-velocity of player jumping
VectorMA (target, time, targ_vel, target);
}
VectorSubtract (target, start, aim);
VectorNormalize (aim);
@ -527,9 +580,9 @@ void GunnerFire (edict_t *self)
gi.WriteByte (MZ_MACHINEGUN | 128);
gi.multicast (self->s.origin, MULTICAST_PVS);
gi.sound (self, CHAN_WEAPON|CHAN_RELIABLE, sound_fire_flechette, 1.0, ATTN_NORM, 0);
gi.sound (self, CHAN_WEAPON|CHAN_RELIABLE, tactician_sound_fire_flechette, 1.0, ATTN_NORM, 0);
#endif // KMQUAKE2_ENGINE_MOD
monster_fire_flechette (self, start, aim, 4, 850, 75, 8, flash_number);
monster_fire_flechette (self, start, aim, 4, flechetteSpeed, 75, 8, flash_number);
}
else
monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
@ -683,8 +736,8 @@ void GunnerGrenade (edict_t *self)
// Knightmare- Tactician Gunner fires prox mines
if (self->moreflags & FL2_COMMANDER)
{
float prox_timer = (blindfire) ? 60.0f : 2.5f;
monster_fire_prox (self, start, aim, 80, 1, 600, 20, prox_timer, 192, flash_number);
float prox_timer = (blindfire) ? 60.0f : 30.0f;
monster_fire_prox (self, start, aim, 90, 1, 600, 20, prox_timer, 192, flash_number);
}
else
monster_fire_grenade (self, start, aim, 50, 600, flash_number, false);
@ -777,7 +830,7 @@ mframe_t gunner_frames_attack_grenade [] =
};
mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run};
void gunner_attack(edict_t *self)
void gunner_attack (edict_t *self)
{
float chance, r;
@ -814,7 +867,7 @@ void gunner_attack(edict_t *self)
// turn on manual steering to signal both manual steering and blindfire
// self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
self->monsterinfo.monsterflags |= AI_MANUAL_STEERING;
if (gunner_grenade_check(self))
if ( gunner_grenade_check(self) )
{
// if the check passes, go for the attack
self->monsterinfo.currentmove = &gunner_move_attack_grenade;
@ -832,6 +885,8 @@ void gunner_attack(edict_t *self)
}
// pmm
self->monsterinfo.aiflags2 &= ~AI2_LEAD_TARGET; // Knightmare- reset Tactican Gunner leading target flag
// PGM - gunner needs to use his chaingun if he's being attacked by a tesla.
if ((range (self, self->enemy) == RANGE_MELEE) || self->bad_area)
{
@ -839,19 +894,60 @@ void gunner_attack(edict_t *self)
}
else
{
if (random() <= 0.5 && gunner_grenade_check(self))
if (random() <= 0.5 && gunner_grenade_check(self)) {
self->monsterinfo.currentmove = &gunner_move_attack_grenade;
}
else
{
// Knightmare- Tactician Gunner leads the target
if (self->moreflags & FL2_COMMANDER)
{
vec3_t forward, right, start, target, aim;
float dist, chance;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_GUNNER_MACHINEGUN_1], forward, right, start);
VectorCopy (self->enemy->s.origin, target);
target[2] += self->enemy->viewheight;
// Lazarus fog reduction of accuracy
if (self->monsterinfo.visibility < FOG_CANSEEGOOD)
{
target[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
target[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
target[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
}
VectorSubtract (target, start, aim);
dist = VectorLength(aim);
if (dist < 640.0f) // chance for leading fire if distance is less than 640
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("Tactician Gunner: target in range, rolling for chance to lead- ");
chance = random(); // chance = 50% easy, 60% medium, 70% hard, 80% hard+
chance += (3.0f - skill->value) * 0.1f;
if (chance < 0.8f)
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("chance passed, leading target\n");
self->monsterinfo.aiflags2 |= AI2_LEAD_TARGET;
}
// else if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("chance failed, not leading target\n");
}
}
// end Knightmare
self->monsterinfo.currentmove = &gunner_move_attack_chain;
}
}
}
void gunner_fire_chain(edict_t *self)
void gunner_fire_chain (edict_t *self)
{
self->monsterinfo.currentmove = &gunner_move_fire_chain;
}
void gunner_refire_chain(edict_t *self)
void gunner_refire_chain (edict_t *self)
{
if (self->enemy->health > 0)
if ( visible (self, self->enemy) )
@ -1170,11 +1266,22 @@ void SP_monster_gunner (edict_t *self)
{ // precache
gi.modelindex ("models/weapons/g_prox/tris.md2");
gi.modelindex ("models/proj/flechette/tris.md2");
gi.soundindex ("weapons/proxopen.wav");
gi.soundindex ("weapons/proxwarn.wav");
#ifdef KMQUAKE2_ENGINE_MOD
gi.soundindex ("weapons/nail1.wav");
#else
sound_fire_flechette = gi.soundindex ("weapons/nail1.wav");
tactician_sound_fire_flechette = gi.soundindex ("weapons/nail1.wav");
#endif // KMQUAKE2_ENGINE_MOD
/*
tactician_sound_death = gi.soundindex ("tactician_gunner/death1.wav");
tactician_sound_pain = gi.soundindex ("tactician_gunner/gunpain2.wav");
tactician_sound_pain2 = gi.soundindex ("tactician_gunner/gunpain1.wav");
tactician_sound_idle = gi.soundindex ("tactician_gunner/gunidle1.wav");
tactician_sound_open = gi.soundindex ("tactician_gunner/gunatck1.wav");
tactician_sound_search = gi.soundindex ("tactician_gunner/gunsrch1.wav");
tactician_sound_sight = gi.soundindex ("tactician_gunner/sight1.wav");
*/
if (!self->health)
self->health = 400;
@ -1183,8 +1290,20 @@ void SP_monster_gunner (edict_t *self)
if (!self->mass)
self->mass = 300;
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = 200;
// 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;
}
else
{
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = 300;
}
self->common_name = "Tactician Gunner";
self->class_id = ENTITY_MONSTER_GUNNER_TACTICIAN;
@ -1201,6 +1320,16 @@ void SP_monster_gunner (edict_t *self)
if (!self->mass)
self->mass = 200;
// 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 = "Gunner";
self->class_id = ENTITY_MONSTER_GUNNER;
}
@ -1227,16 +1356,6 @@ void SP_monster_gunner (edict_t *self)
if (!self->blood_type)
self->blood_type = 3; //sparks and blood
// 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;
}
if ( !self->monsterinfo.flies && strcmp(self->classname, "monster_gunner_tactician") == 0 )
self->monsterinfo.flies = 0.20;
else if (!self->monsterinfo.flies)

View file

@ -214,9 +214,10 @@ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
{
ent->bad_area = current_bad;
if (ent->enemy && !strcmp(ent->enemy->classname, "tesla"))
// if ( ent->enemy && !strcmp(ent->enemy->classname, "tesla") )
if ( ent->enemy && (!strcmp(ent->enemy->classname, "tesla") || !strcmp(ent->enemy->classname, "prox")) )
{
// if the tesla is in front of us, back up...
// if the tesla or prox is in front of us, back up...
if (IsBadAhead (ent, current_bad, move))
VectorScale(move, -1, move);
}
@ -663,21 +664,23 @@ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf("Blocked -");
if (!strcmp(new_bad->owner->classname, "tesla"))
// if (!strcmp(new_bad->owner->classname, "tesla"))
if ( !strcmp(new_bad->owner->classname, "tesla") || !strcmp(new_bad->owner->classname, "prox") )
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("it's a tesla -");
// gi.dprintf ("it's a tesla/prox -");
if ((!(ent->enemy)) || (!(ent->enemy->inuse)))
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("I don't have a valid enemy, attacking tesla!\n");
// gi.dprintf ("I don't have a valid enemy, attacking tesla/prox!\n");
TargetTesla (ent, new_bad->owner);
ent->monsterinfo.aiflags |= AI_BLOCKED;
}
else if (!strcmp(ent->enemy->classname, "telsa"))
// else if (!strcmp(ent->enemy->classname, "telsa"))
else if ( !strcmp(ent->enemy->classname, "telsa") || !strcmp(ent->enemy->classname, "prox") )
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("but we're already mad at a tesla\n");
// gi.dprintf ("but we're already mad at a tesla/prox -");
}
else if ((ent->enemy) && (ent->enemy->client))
{
@ -691,7 +694,7 @@ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
else
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("can't see him, kill the tesla! -");
// gi.dprintf ("can't see him, kill the tesla/prox! -");
TargetTesla (ent, new_bad->owner);
ent->monsterinfo.aiflags |= AI_BLOCKED;
}
@ -699,17 +702,17 @@ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
else
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("the enemy isn't a player, killing tesla -");
// gi.dprintf ("the enemy isn't a player, killing tesla/prox -");
TargetTesla (ent, new_bad->owner);
ent->monsterinfo.aiflags |= AI_BLOCKED;
}
}
/* else if ((g_showlogic) && (g_showlogic->value))
{
gi.dprintf(" by non-tesla bad area!");
}*/
/* else if ((g_showlogic) && (g_showlogic->value)) {
gi.dprintf(" by non-tesla, non-prox bad area!");
} */
}
// gi.dprintf ("\n");
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("\n");
VectorCopy (oldorg, ent->s.origin);
return false;
@ -1043,8 +1046,8 @@ void M_MoveToGoal (edict_t *ent, float dist)
{
if (ent->monsterinfo.aiflags & AI_BLOCKED)
{
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("tesla attack detected, not changing direction!\n");
// if ((g_showlogic) && (g_showlogic->value))
// gi.dprintf ("tesla attack detected, not changing direction!\n");
ent->monsterinfo.aiflags &= ~AI_BLOCKED;
return;
}

View file

@ -388,7 +388,7 @@ void TurretFire (edict_t *self)
chance = DotProduct(dir, forward);
if (chance < 0.98)
{
// gi.dprintf("off-angle\n");
// gi.dprintf("off-angle\n");
return;
}
@ -430,9 +430,9 @@ void TurretFire (edict_t *self)
// aim for the head.
if ((self->enemy) && (self->enemy->client))
end[2]+=self->enemy->viewheight;
end[2] += self->enemy->viewheight;
else
end[2]+=22;
end[2] += 22;
// Lazarus fog reduction of accuracy
if (self->monsterinfo.visibility < FOG_CANSEEGOOD)
@ -449,7 +449,7 @@ void TurretFire (edict_t *self)
if (!(self->spawnflags & SPAWN_INSTANT_WEAPON) && (dist<512))
{
chance = random();
// ramp chance. easy - 50%, avg - 60%, hard - 70%, nightmare - 80%
// ramp chance easy - 50%, med - 60%, hard - 70%, nightmare - 80%
chance += (3 - skill->value) * 0.1;
if (chance < 0.8)
{

View file

@ -1259,7 +1259,6 @@ R_SetMode
qboolean R_SetMode (void)
{
rserr_t err;
// qboolean fullscreen;
dispType_t fullscreen; // borderless support
if ( vid_fullscreen->modified && !glConfig.allowCDS )
@ -1290,7 +1289,6 @@ qboolean R_SetMode (void)
Cvar_SetValue( "vid_fullscreen", 0);
vid_fullscreen->modified = false;
VID_Printf (PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n" );
// if ( ( err = GLimp_SetMode( &vid.width, &vid.height, r_mode->integer, false ) ) == rserr_ok )
if ( ( err = GLimp_SetMode( &vid.width, &vid.height, r_mode->integer, dt_windowed ) ) == rserr_ok )
return true;
}
@ -1310,7 +1308,6 @@ qboolean R_SetMode (void)
}
// try setting it back to something safe
// if ( ( err = GLimp_SetMode( &vid.width, &vid.height, glState.prev_mode, false ) ) != rserr_ok )
if ( ( err = GLimp_SetMode( &vid.width, &vid.height, glState.prev_mode, dt_windowed ) ) != rserr_ok )
{
VID_Printf (PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n" );

View file

@ -206,7 +206,6 @@ static qboolean VerifyDriver( void )
#define WINDOW_CLASS_NAME2 "KMQuake2 - The Reckoning" // changed
#define WINDOW_CLASS_NAME3 "KMQuake2 - Ground Zero" // changed
//qboolean VID_CreateWindow (int width, int height, qboolean fullscreen)
qboolean VID_CreateWindow (int width, int height, dispType_t fullscreen)
{
WNDCLASS wc;
@ -243,7 +242,6 @@ qboolean VID_CreateWindow (int width, int height, dispType_t fullscreen)
if (!RegisterClass (&wc) )
VID_Error (ERR_FATAL, "Couldn't register window class");
// if (fullscreen)
if ( fullscreen == dt_fullscreen ) // borderless support
{
exstyle = WS_EX_TOPMOST;
@ -272,7 +270,6 @@ qboolean VID_CreateWindow (int width, int height, dispType_t fullscreen)
w = r.right - r.left;
h = r.bottom - r.top;
// if (fullscreen)
if ( fullscreen == dt_fullscreen ) // borderless support
{
x = 0;
@ -343,11 +340,9 @@ qboolean VID_CreateWindow (int width, int height, dispType_t fullscreen)
/*
** GLimp_SetMode
*/
//rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, qboolean fullscreen)
rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscreen)
{
int width, height;
// const char *win_fs[] = { "W", "FS" };
const char *win_fs[] = { "W", "FS", "BL" }; // borderless support
VID_Printf( PRINT_ALL, "Initializing OpenGL display\n");
@ -360,7 +355,7 @@ rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscree
return rserr_invalid_mode;
}
VID_Printf( PRINT_ALL, " %dx%d %s\n", width, height, win_fs[fullscreen] );
VID_Printf( PRINT_ALL, " %dx%d %s\n", width, height, win_fs[min(max(fullscreen, 0), 2)] ); // clamp index
// destroy the existing window
if (glw_state.hWnd)
@ -369,7 +364,6 @@ rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscree
}
// do a CDS if needed
// if ( fullscreen )
if ( fullscreen == dt_fullscreen ) // borderless support
{
DEVMODE fullscreenMode;
@ -457,7 +451,6 @@ rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscree
*pwidth = width;
*pheight = height;
glState.fullscreen = false;
// if ( !VID_CreateWindow (width, height, false) )
if ( !VID_CreateWindow (width, height, dt_windowed) )
return rserr_invalid_mode;
return rserr_invalid_fullscreen;
@ -465,7 +458,6 @@ rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscree
else
{
VID_Printf( PRINT_ALL, " ok\n" );
// if ( !VID_CreateWindow (width, height, true) )
if ( !VID_CreateWindow (width, height, dt_fullscreen) )
return rserr_invalid_mode;
@ -495,7 +487,6 @@ rserr_t GLimp_SetMode (int *pwidth, int *pheight, int mode, dispType_t fullscree
*pwidth = width;
*pheight = height;
glState.fullscreen = false;
// if ( !VID_CreateWindow (width, height, false) )
if ( !VID_CreateWindow (width, height, dt_windowed) )
return rserr_invalid_mode;
}