Added Zaero autocannon to missionpack DLL.

This commit is contained in:
Knightmare66 2020-04-23 03:18:05 -04:00
parent 9272d5ea85
commit 02755e0181
17 changed files with 1101 additions and 90 deletions

View file

@ -49,7 +49,7 @@ qboolean CanDamage (edict_t *targ, edict_t *inflictor)
// from inflictor to targ is blocked by a func_tracktrain, AND the targ is riding/driving
// the tracktrain, go ahead and hurt him.
if(trace.ent && (trace.ent->flags & FL_TRACKTRAIN) && ((trace.ent->owner == targ) || (targ->groundentity == trace.ent)) )
if (trace.ent && (trace.ent->flags & FL_TRACKTRAIN) && ((trace.ent->owner == targ) || (targ->groundentity == trace.ent)) )
return true;
VectorCopy (targ->s.origin, dest);
@ -385,7 +385,7 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
{
edict_t *teammate;
if(!targ || !attacker)
if (!targ || !attacker)
return;
// Knightmare- skip this for insanes
@ -393,28 +393,28 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
return;
// Lazarus dmgteam stuff
if( (attacker->client && !(attacker->flags & FL_NOTARGET)) || (attacker->svflags & SVF_MONSTER))
if ( (attacker->client && !(attacker->flags & FL_NOTARGET)) || (attacker->svflags & SVF_MONSTER))
{ // the attacker is a player or a monster
if( (targ->svflags & SVF_MONSTER) && (targ->dmgteam) && (targ->health > 0) )
if ( (targ->svflags & SVF_MONSTER) && (targ->dmgteam) && (targ->health > 0) )
{ // the target is a live monster on a dmgteam
if( !attacker->dmgteam || strcmp(targ->dmgteam,attacker->dmgteam) )
if ( !attacker->dmgteam || strcmp(targ->dmgteam,attacker->dmgteam) )
{ // attacker is not on same dmgteam as target
if( !Q_stricmp(targ->dmgteam,"player") && attacker->client )
if ( !Q_stricmp(targ->dmgteam,"player") && attacker->client )
{ // Target is a GOOD_GUY, attacked by the player. Allow self-defense,
// but don't get other dmgteam teammates involved.
// Special case for misc_actors - if ACTOR_BAD_GUY isn't set,
// actor stays on the same team and only taunts player
if(!(targ->monsterinfo.aiflags & AI_ACTOR) || (targ->spawnflags & SF_ACTOR_BAD_GUY))
if (!(targ->monsterinfo.aiflags & AI_ACTOR) || (targ->spawnflags & SF_ACTOR_BAD_GUY))
{
targ->enemy = targ->movetarget = targ->goalentity = attacker;
targ->monsterinfo.aiflags &= ~AI_FOLLOW_LEADER;
if(visible(targ,targ->enemy))
if (visible(targ,targ->enemy))
FoundTarget(targ);
else
HuntTarget(targ);
}
}
else if( !(targ->svflags & SVF_MONSTER) || !(attacker->svflags & SVF_MONSTER) ||
else if ( !(targ->svflags & SVF_MONSTER) || !(attacker->svflags & SVF_MONSTER) ||
(targ->monsterinfo.aiflags2 & AI2_FREEFORALL) ||
((targ->monsterinfo.aiflags & AI_GOOD_GUY) != (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) )
{
@ -422,15 +422,15 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
// they're both monsters but one is AI_GOOD_GUY and the other is not,
// or we've turned the game into a free-for-all with a target_monsterbattle
teammate = G_Find(NULL,FOFS(dmgteam),targ->dmgteam);
while(teammate)
while (teammate)
{
if(teammate != targ)
if (teammate != targ)
{
if(teammate->svflags & SVF_MONSTER)
if (teammate->svflags & SVF_MONSTER)
{
if(teammate->health > 0 && (teammate->enemy != attacker) && !(teammate->monsterinfo.aiflags & AI_CHASE_THING))
if (teammate->health > 0 && (teammate->enemy != attacker) && !(teammate->monsterinfo.aiflags & AI_CHASE_THING))
{
if(!teammate->enemy || !teammate->enemy->dmgteam || !attacker->dmgteam )
if (!teammate->enemy || !teammate->enemy->dmgteam || !attacker->dmgteam )
{
// If either 1) this teammate doesn't currently have an enemy,
// or 2) the teammate's enemy is not a member of a dmgteam
@ -438,7 +438,7 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
// then set the attacker as the enemy of this teammate
DefendMyFriend(teammate,attacker);
}
else if(strcmp(teammate->enemy->dmgteam,attacker->dmgteam))
else if (strcmp(teammate->enemy->dmgteam,attacker->dmgteam))
{
// attacker is a member of a team different than the
// current enemy
@ -446,7 +446,7 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
}
}
}
else if(!(teammate->svflags & SVF_DEADMONSTER))
else if (!(teammate->svflags & SVF_DEADMONSTER))
G_UseTargets(teammate,attacker);
}
teammate = G_Find(teammate,FOFS(dmgteam),targ->dmgteam);
@ -463,16 +463,16 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
teammate = G_Find(NULL,FOFS(dmgteam),"player");
while (teammate)
{
if((teammate->health > 0) && !(teammate->monsterinfo.aiflags & AI_CHASE_THING) && (teammate != attacker))
if ((teammate->health > 0) && !(teammate->monsterinfo.aiflags & AI_CHASE_THING) && (teammate != attacker))
{
// Can teammate see player?
// tr = gi.trace(teammate->s.origin,vec3_origin,vec3_origin,targ->s.origin,teammate,MASK_OPAQUE);
// if(tr.fraction == 1.0)
if(gi.inPVS(teammate->s.origin,targ->s.origin))
// if (tr.fraction == 1.0)
if (gi.inPVS(teammate->s.origin,targ->s.origin))
{
teammate->enemy = attacker;
FoundTarget(teammate);
if(teammate->monsterinfo.aiflags & AI_ACTOR)
if (teammate->monsterinfo.aiflags & AI_ACTOR)
{
teammate->monsterinfo.aiflags |= AI_FOLLOW_LEADER;
teammate->monsterinfo.old_leader = NULL;
@ -509,8 +509,8 @@ void CallMyFriends (edict_t *targ, edict_t *attacker)
}
}
// 1.6.1.3 change - one chance and one chance only to call friends
/* if(targ->dmgteam)
if(Q_stricmp(targ->dmgteam,"player"))
/* if (targ->dmgteam)
if (Q_stricmp(targ->dmgteam,"player"))
targ->dmgteam = NULL; */
}
@ -525,7 +525,7 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
// if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
// return;
if( targ->health <= 0 )
if ( targ->health <= 0 )
return;
// If targ is currently chasing a "thing" or we're running a hint_path test, he
@ -586,9 +586,9 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
VectorCopy(targ->mins,mins);
mins[2] += 16; // max step height? not sure about this
if(mins[2] > 0) mins[2] = 0;
if (mins[2] > 0) mins[2] = 0;
VectorCopy(targ->maxs,maxs);
if( (attacker==world) ||
if ( (attacker==world) ||
(!Q_stricmp(attacker->classname,"func_door") ) ||
(!Q_stricmp(attacker->classname,"func_water")) ||
(!Q_stricmp(attacker->classname,"func_pushable")) )
@ -611,12 +611,12 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
}
}
}
else if(!Q_stricmp(attacker->classname,"target_laser"))
else if (!Q_stricmp(attacker->classname,"target_laser"))
{
// Send the monster in a direction perpendicular to laser
// path, whichever direction is closest to current angles
thing = SpawnThing();
if(attacker->movedir[2] > 0.7)
if (attacker->movedir[2] > 0.7)
{
// Just move straight ahead and hope for the best
AngleVectors(targ->s.angles,best_dir,NULL,NULL);
@ -629,7 +629,7 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
best_dir[1] = best_dir[2];
best_dir[2] = 0;
AngleVectors(targ->s.angles,dir,NULL,NULL);
if(DotProduct(best_dir,dir) < 0)
if (DotProduct(best_dir,dir) < 0)
VectorNegate(best_dir,best_dir);
}
}
@ -638,7 +638,7 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
// Attacked by a point entity or moving brush model
// not covered above. Find a vector that will hide the
// monster from the attacker.
if(!VectorLength(attacker->size))
if (!VectorLength(attacker->size))
{
// point entity
VectorCopy(attacker->s.origin,atk);
@ -650,9 +650,9 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
}
VectorClear(best_dir);
AngleVectors(targ->s.angles,forward,NULL,NULL);
for(i=0; i<32 && best_dist == 0; i++) {
for (i=0; i<32 && best_dist == 0; i++) {
// Weight escape route tests in favor of forward-facing direction
if(random() > 0.5)
if (random() > 0.5)
{
dir[0] = forward[0] + 0.5*crandom();
dir[1] = forward[1] + 0.5*crandom();
@ -668,20 +668,20 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
VectorMA(targ->s.origin, WORLD_SIZE, dir, end); // was 8192
trace1 = gi.trace(targ->s.origin,mins,maxs,end,targ,MASK_MONSTERSOLID);
trace2 = gi.trace(trace1.endpos,NULL,NULL,atk,targ,MASK_SOLID);
if(trace2.fraction == 1.0) continue;
if (trace2.fraction == 1.0) continue;
dist = trace1.fraction * WORLD_SIZE; // was 8192
if(dist > best_dist)
if (dist > best_dist)
{
best_dist = dist;
VectorCopy(dir, best_dir);
}
}
if(best_dist == 0.)
if (best_dist == 0.)
return;
thing = SpawnThing();
vectoangles(best_dir,thing->s.angles);
}
if( (!Q_stricmp(attacker->classname,"func_door")) ||
if ( (!Q_stricmp(attacker->classname,"func_door")) ||
(!Q_stricmp(attacker->classname,"func_pushable")) )
run = 256;
else
@ -691,7 +691,7 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
dist = trace1.fraction * run;
VectorMA(targ->s.origin, dist, best_dir, thing->s.origin);
// If monster already has an enemy, use a short lifespan for thing
if(targ->enemy)
if (targ->enemy)
thing->touch_debounce_time = level.time + 2.0;
else
thing->touch_debounce_time = level.time + max(5.0,dist/50.);
@ -714,13 +714,19 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
{
if (attacker->health > targ->health)
{
if(ai_chicken(targ,attacker))
if (ai_chicken(targ,attacker))
return;
}
}
}
}
// Zaero- handle autocanon
// if ( !(attacker->client) && !(attacker->svflags & SVF_MONSTER) &&
// (strncmp (attacker->classname, "monster_autocannon", 18) != 0) )
if ( !strncmp (attacker->classname, "monster_autocannon", 18) )
return;
if (attacker == targ || attacker == targ->enemy)
return;
@ -740,10 +746,10 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
float percentHealth;
// make sure whatever we were pissed at is still around.
if(targ->enemy->inuse)
if (targ->enemy->inuse)
{
percentHealth = (float)(targ->health) / (float)(targ->max_health);
if( targ->enemy->inuse && percentHealth > 0.33)
if ( targ->enemy->inuse && percentHealth > 0.33)
return;
}
@ -760,7 +766,7 @@ void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
percentHealth = (float)(targ->health) / (float)(targ->max_health);
// ignore it some of the time
if( targ->enemy->inuse && percentHealth > 0.25)
if ( targ->enemy->inuse && percentHealth > 0.25)
return;
// remove the medic flag
@ -895,7 +901,7 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
// Lazarus: If monster/actor is currently being forced to use
// specific animations due to target_animation, release that
// control over it.
if((targ->think == target_animate) && (targ->svflags & SVF_MONSTER))
if ((targ->think == target_animate) && (targ->svflags & SVF_MONSTER))
{
targ->think = monster_think;
targ->nextthink = level.time + FRAMETIME;
@ -1086,9 +1092,9 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
{
take = 0;
save = damage;
//Knightmare
if (mod != MOD_SHOCK_SPLASH) //no sparks from shockwave detonation effect!!!
//end Knightmare
// Knightmare
if (mod != MOD_SHOCK_SPLASH) // no sparks from shockwave detonation effect!!!
// end Knightmare
SpawnDamage (TE_SPARKS, point, normal, save);
}
@ -1117,7 +1123,7 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
}
// ROGUE
//Knightmare- falling doesn't damage armor
// Knightmare- falling doesn't damage armor
if (mod == MOD_FALLING && !falling_armor_damage->value)
{
psave = 0;
@ -1131,7 +1137,7 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
take -= asave;
//treat cheat/powerup savings the same as armor
// treat cheat/powerup savings the same as armor
asave += save;
}
@ -1139,18 +1145,18 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
return;
// ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
// ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
if (dflags & DAMAGE_DESTROY_ARMOR)
{
if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
if (!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
!(client && client->invincible_framenum > level.framenum))
{
take = damage;
}
}
// ROGUE
// ROGUE
// do the damage
// do the damage
if (take)
{
// Lazarus: dmgteam stuff
@ -1160,13 +1166,13 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
if ( (mod != MOD_SHOCK_SPLASH) && ((damage > 0) || ((mod != MOD_TRIGGER_HURT) && (mod != MOD_TARGET_LASER))) )
{
//PGM- need more blood for chainfist.
if(targ->flags & FL_MECHANICAL)
if (targ->flags & FL_MECHANICAL)
{
SpawnDamage (TE_ELECTRIC_SPARKS, point, normal, take);
}
else if ((targ->svflags & SVF_MONSTER) || (client))
{
if(targ->blood_type == 1)
if (targ->blood_type == 1)
SpawnDamage (TE_GREENBLOOD, point, normal, take);
else if (targ->blood_type == 2)
{
@ -1266,10 +1272,10 @@ void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_
//PGM - spheres need to know who to shoot at
if (!sphere_notified)
{
if(client && client->owned_sphere)
if (client && client->owned_sphere)
{
sphere_notified = true;
if(client->owned_sphere->pain)
if (client->owned_sphere->pain)
client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
}
}

View file

@ -1,6 +1,7 @@
extern void fire_flare ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , float damage_radius , int radius_damage ) ;
extern void flare_think ( edict_t * self ) ;
extern void flare_flash ( edict_t * ent ) ;
extern void angleToward ( edict_t * self , vec3_t point , float speed ) ;
extern void SP_trigger_laser ( edict_t * self ) ;
extern void trigger_laser_on ( edict_t * self ) ;
extern void trigger_laser_think ( edict_t * self ) ;
@ -160,6 +161,24 @@ extern int zSchoolMonsters ( edict_t * self , float dist , int runStyle , float
extern int zFindRoamYaw ( edict_t * self , float distcheck ) ;
extern int zSchoolAllVisiable ( edict_t * self ) ;
extern void zCreateRaduisList ( edict_t * self ) ;
extern void SP_monster_autocannon_floor ( edict_t * self ) ;
extern void SP_monster_autocannon ( edict_t * self ) ;
extern void monster_autocannon_usestub ( edict_t * self ) ;
extern void monster_autocannon_use ( edict_t * self , edict_t * other , edict_t * activator ) ;
extern void monster_autocannon_act ( edict_t * self ) ;
extern void monster_autocannon_deactivate ( edict_t * self ) ;
extern void monster_autocannon_activate ( edict_t * self ) ;
extern void monster_autocannon_pain ( edict_t * self , edict_t * other , float kick , int damage ) ;
extern void monster_autocannon_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ;
extern void monster_autocannon_explode ( edict_t * ent ) ;
extern void monster_autocannon_think ( edict_t * self ) ;
extern void monster_autocannon_turn ( edict_t * self ) ;
extern void monster_autocannon_findenemy ( edict_t * self ) ;
extern qboolean autocannonInfront ( edict_t * self , edict_t * other ) ;
extern qboolean canShoot ( edict_t * self , edict_t * e ) ;
extern float mod180 ( float val ) ;
extern qboolean angleBetween ( float * ang , float * , float * ) ;
extern void monster_autocannon_fire ( edict_t * self ) ;
extern void Info_SetValueForKey ( char * s , char * key , char * value ) ;
extern qboolean Info_Validate ( char * s ) ;
extern void Info_RemoveKey ( char * s , char * key ) ;

View file

@ -1,6 +1,7 @@
{"fire_flare", (byte *)fire_flare},
{"flare_think", (byte *)flare_think},
{"flare_flash", (byte *)flare_flash},
{"angleToward", (byte *)angleToward},
{"SP_trigger_laser", (byte *)SP_trigger_laser},
{"trigger_laser_on", (byte *)trigger_laser_on},
{"trigger_laser_think", (byte *)trigger_laser_think},
@ -160,6 +161,24 @@
{"zFindRoamYaw", (byte *)zFindRoamYaw},
{"zSchoolAllVisiable", (byte *)zSchoolAllVisiable},
{"zCreateRaduisList", (byte *)zCreateRaduisList},
{"SP_monster_autocannon_floor", (byte *)SP_monster_autocannon_floor},
{"SP_monster_autocannon", (byte *)SP_monster_autocannon},
{"monster_autocannon_usestub", (byte *)monster_autocannon_usestub},
{"monster_autocannon_use", (byte *)monster_autocannon_use},
{"monster_autocannon_act", (byte *)monster_autocannon_act},
{"monster_autocannon_deactivate", (byte *)monster_autocannon_deactivate},
{"monster_autocannon_activate", (byte *)monster_autocannon_activate},
{"monster_autocannon_pain", (byte *)monster_autocannon_pain},
{"monster_autocannon_die", (byte *)monster_autocannon_die},
{"monster_autocannon_explode", (byte *)monster_autocannon_explode},
{"monster_autocannon_think", (byte *)monster_autocannon_think},
{"monster_autocannon_turn", (byte *)monster_autocannon_turn},
{"monster_autocannon_findenemy", (byte *)monster_autocannon_findenemy},
{"autocannonInfront", (byte *)autocannonInfront},
{"canShoot", (byte *)canShoot},
{"mod180", (byte *)mod180},
{"angleBetween", (byte *)angleBetween},
{"monster_autocannon_fire", (byte *)monster_autocannon_fire},
{"Info_SetValueForKey", (byte *)Info_SetValueForKey},
{"Info_Validate", (byte *)Info_Validate},
{"Info_RemoveKey", (byte *)Info_RemoveKey},

View file

@ -1066,7 +1066,7 @@ qboolean monster_start (edict_t *self)
}
// Lazarus: Good guys
//Knightmare- gekks and stalkers use different spawnflag
// Knightmare- gekks and stalkers use different spawnflag
if ( (UseRegularGoodGuyFlag(self) && (self->spawnflags & SF_MONSTER_GOODGUY))
|| (UseSpecialGoodGuyFlag(self) && (self->spawnflags & 16)) )
{

View file

@ -829,9 +829,11 @@ void target_effect_lightning(edict_t *self, edict_t *activator)
{
edict_t *target;
if(!self->target) return;
if (!self->target)
return;
target = G_Find(NULL,FOFS(targetname),self->target);
if(!target) return;
if (!target)
return;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (self->style);

View file

@ -175,6 +175,8 @@ void SP_monster_gladb (edict_t *self);
void SP_monster_boss5 (edict_t *self);
// Zaero
void SP_monster_autocannon (edict_t *self);
void SP_monster_autocannon_floor (edict_t *self);
void SP_monster_hound (edict_t *self);
void SP_monster_handler (edict_t *self);
void SP_monster_sentien(edict_t *self);
@ -554,6 +556,8 @@ spawn_t spawns[] = {
{"monster_widow2", SP_monster_widow2},
// Zaero
{"monster_autocannon", SP_monster_autocannon},
{"monster_autocannon_floor", SP_monster_autocannon_floor},
{"monster_hound", SP_monster_hound},
{"monster_handler", SP_monster_handler},
{"monster_sentien", SP_monster_sentien},

View file

@ -12,11 +12,12 @@
// right now, only the tesla has this
#define SVF_DAMAGEABLE 0x00000008
//ROGUE end
#define SVF_GIB 0x00000010 //Knightmare- gib flag
#define SVF_GIB 0x00000010 // Knightmare- gib flag
#define SVF_TRIGGER_CAMOWNER 0x00000020
#define SVF_MUD 0x00000040 //mud flag
#define SVF_CLONED 0x00000080 //How to tell if this entity is a clone
#define SVF_OLDPLAYER 0x00000080 //How to tell if this entity is a clone
#define SVF_MUD 0x00000040 // mud flag
#define SVF_CLONED 0x00000080 // How to tell if this entity is a clone
#define SVF_OLDPLAYER 0x00000080 // How to tell if this entity is a clone
#define SVF_AUTOMATON 0x00000100 // Not exactly a monster (but causes client obit)...
// edict->solid values

View file

@ -206,7 +206,6 @@ cvar_t *sk_max_fuel;
/*
cvar_t *sk_max_flares;
cvar_t *sk_max_tbombs;
cvar_t *sk_max_a2k;
cvar_t *sk_max_empnuke;
cvar_t *sk_max_plasmashield;
*/
@ -242,7 +241,6 @@ cvar_t *sk_pack_fuel;
/*
cvar_t *sk_pack_flares; // 60
cvar_t *sk_pack_tbombs; // 100
cvar_t *sk_pack_a2k; // 1
cvar_t *sk_pack_empnuke; // 100
cvar_t *sk_pack_plasmashield; // 40
*/
@ -295,11 +293,7 @@ cvar_t *sk_ir_time;
cvar_t *sk_double_time;
cvar_t *sk_quad_fire_time;
cvar_t *sk_stasis_time;
/*
cvar_t *sk_visor_time;
cvar_t *sk_sniper_charge_time;
cvar_t *sk_a2k_detonate_time;
*/
//cvar_t *sk_visor_time;
void InitLithiumVars (void)
@ -505,7 +499,6 @@ void InitLithiumVars (void)
/*
sk_max_flares = gi.cvar("sk_max_flares", "30", 0);
sk_max_tbombs = gi.cvar("sk_max_tbombs", "30", 0);
sk_max_a2k = gi.cvar("sk_max_a2k", "1", 0);
sk_max_empnuke = gi.cvar("sk_max_empnuke", "50", 0);
sk_max_plasmashield = gi.cvar("sk_max_plasmashield", "20", 0);
*/
@ -541,7 +534,6 @@ void InitLithiumVars (void)
/*
sk_pack_flares = gi.cvar("sk_pack_flares", "100", 0);
sk_pack_tbombs = gi.cvar("sk_pack_tbombs", "100", 0);
sk_pack_a2k = gi.cvar("sk_pack_a2k", "1", 0);
sk_pack_empnuke = gi.cvar("sk_pack_empnuke", "100", 0);
sk_pack_plasmashield = gi.cvar("sk_pack_plasmashield", "40", 0);
*/
@ -595,9 +587,5 @@ void InitLithiumVars (void)
sk_double_time = gi.cvar("sk_double_time", "30", 0);
sk_quad_fire_time = gi.cvar("sk_quad_fire_time", "30", 0);
sk_stasis_time = gi.cvar("sk_stasis_time", "30", 0);
/*
sk_visor_time = gi.cvar("sk_visor_time", "30", 0);
sk_sniper_charge_time = gi.cvar("sk_sniper_charge_time", "3", 0);
sk_a2k_detonate_time = gi.cvar("sk_a2k_detonate_time", "5", 0);
*/
// sk_visor_time = gi.cvar("sk_visor_time", "30", 0);
}

View file

@ -203,7 +203,6 @@ extern cvar_t *sk_max_fuel;
/*
extern cvar_t *sk_max_flares;
extern cvar_t *sk_max_tbombs;
extern cvar_t *sk_max_a2k;
extern cvar_t *sk_max_empnuke;
extern cvar_t *sk_max_plasmashield;
*/
@ -237,7 +236,6 @@ extern cvar_t *sk_pack_fuel;
/*
extern cvar_t *sk_pack_flares;
extern cvar_t *sk_pack_tbombs;
extern cvar_t *sk_pack_a2k;
extern cvar_t *sk_pack_empnuke;
extern cvar_t *sk_pack_plasmashield;
*/
@ -291,8 +289,4 @@ extern cvar_t *sk_double_time;
extern cvar_t *sk_quad_fire_time;
extern cvar_t *sk_doppleganger_time;
extern cvar_t *sk_stasis_time;
/*
extern cvar_t *sk_visor_time;
extern cvar_t *sk_sniper_charge_time;
extern cvar_t *sk_a2k_detonate_time;
*/
//extern cvar_t *sk_visor_time;

View file

@ -521,6 +521,10 @@ SOURCE=.\q_shared.c
# End Source File
# Begin Source File
SOURCE=.\z_acannon.c
# End Source File
# Begin Source File
SOURCE=.\z_ai.c
# End Source File
# Begin Source File

View file

@ -842,6 +842,10 @@
RelativePath="q_shared.c"
>
</File>
<File
RelativePath=".\z_acannon.c"
>
</File>
<File
RelativePath=".\z_ai.c"
>

View file

@ -587,8 +587,8 @@ void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
return;
}
}
//Knightmare- Single-player obits
if (attacker->svflags & SVF_MONSTER)
// Knightmare- Single-player obits
if (attacker->svflags & (SVF_MONSTER|SVF_AUTOMATON))
{ // Light Guard
if (!strcmp(attacker->classname, "monster_soldier_light"))
message = "was blasted by a";
@ -907,6 +907,24 @@ void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
else
message = "was slashed to death by a";
}
// Autocannon
else if (!strcmp(attacker->classname, "monster_autocannon") || !strcmp(attacker->classname, "monster_autocannon_floor"))
{
if (mod == MOD_HYPERBLASTER || mod == MOD_BLASTER) {
message = "was melted by an";
message2 = "'s blaster";
}
else if (mod == MOD_ROCKET) {
message = "ate an";
message2 = "'s rocket";
}
else if (mod == MOD_R_SPLASH) {
message = "almost dodged an";
message2 = "'s rocket";
}
else
message = "was pumped full of lead by an";
}
// Sentien
else if (!strcmp(attacker->classname, "monster_sentien"))
{
@ -1003,7 +1021,7 @@ void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
}
// end Knightmare
}
//Knightmare- for diagnostics, print name of killing entity
// Knightmare- for diagnostics, print name of killing entity
/*if (strlen(attacker->classname))
gi.bprintf (PRINT_MEDIUM,"%s was killed by %s.\n", self->client->pers.netname, attacker->classname);
else*/
@ -1638,7 +1656,7 @@ void InitClientPersistant (gclient_t *client, int style)
// These are currently unused, but init them anyway.
client->pers.max_flares = 30; // sk_max_flares->value;
client->pers.max_tbombs = 30; // sk_max_tbombs->value;
client->pers.max_a2k = 1; // sk_max_a2k->value;
client->pers.max_a2k = 1;
client->pers.max_empnuke = 50; // sk_max_empnuke->value;
client->pers.max_plasmashield = 20; // sk_max_plasmashield->value;
// end Zaero

945
missionpack/z_acannon.c Normal file
View file

@ -0,0 +1,945 @@
#include "g_local.h"
void angleToward(edict_t *self, vec3_t point, float speed);
// spawnflags
#define AC_SF_START_OFF 1
#define AC_SF_BERSERK 2
#define AC_SF_BERSERK_TOGGLE 4
// variables
#define AC_RANGE 2048
#define AC_TIMEOUT 2.0
#define AC_EXPLODE_DMG 150
#define AC_EXPLODE_RADIUS 384
#define AC_TURN_SPEED 6.0
#define AC_TURN_DELAY 1.0
// states
#define AC_S_IDLE 0
#define AC_S_ACTIVATING 1
#define AC_S_ACTIVE 2
#define AC_S_DEACTIVATING 3
// models
char* models[] = { NULL,
"models/objects/acannon/chain/tris.md2",
"models/objects/acannon/rocket/tris.md2",
"models/objects/acannon/laser/tris.md2",
"models/objects/acannon/laser/tris.md2" };
char* floorModels[] = { NULL,
"",
"models/objects/acannon/rocket2/tris.md2",
"models/objects/acannon/laser2/tris.md2",
"models/objects/acannon/laser2/tris.md2" };
// pitch extents
const int acPitchExtents[2][2] = { {0,60}, // max, min
{-60,0}
};
// frames filler/chain/rocket/laser
const int acIdleStart[] = { 0, 0, 0, 0, 0 };
const int acIdleEnd[] = { 0, 0, 0, 0, 0 };
const int acActStart[] = { 0, 1, 1, 1, 1 };
const int acActEnd[] = { 0, 9, 9, 9, 9 };
const int acActiveStart[] = { 0, 10, 10, 10, 10 };
const int acActiveEnd[] = { 0, 10, 10, 10, 10 };
typedef struct ac_anim_frame_s
{
qboolean last;
qboolean fire;
int frame;
} ac_anim_frame_t;
typedef struct ac_anim_s
{
int firstNonPause;
ac_anim_frame_t frames[32];
} ac_anim_t;
ac_anim_t acFiringFrames[5] =
{
// dummy
{
0,
{ true, false, -1 }
},
// chaingun
{
6,
{
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
// start of firing sequence
{ false, true, 11 },
{ false, false, 12 },
{ false, true, 13 },
{ false, false, 14 },
{ false, true, 15 },
{ false, false, 16 },
{ false, true, 17 },
{ false, false, 18 },
{ false, true, 19 },
{ false, false, 20 },
{ false, true, 21 },
{ true, false, 2 },
}
},
// rockets
{
6,
{
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
// start of firing sequence
{ false, true, 11 },
{ false, false, 11 },
{ false, false, 12 },
{ false, false, 12 },
{ false, false, 13 },
{ false, false, 13 },
{ false, false, 14 },
{ false, false, 14 },
{ false, false, 15 },
{ false, false, 15 },
{ false, false, 16 },
{ true, false, 16 },
}
},
// laser
{
6,
{
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
// start of firing sequence
{ false, true, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ true, false, 11 },
}
},
// slow laser
{
6,
{
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
// start of firing sequence
{ false, true, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ false, false, 11 },
{ true, false, 11 },
}
}
};
vec3_t fireOffset[5] = { {0,0,0},
{24,-4,0},
{0,-4,0},
{24,-5,0},
{24,-5,0} };
const int acDeactStart[] = { 0, 23, 23, 23, 23 };
const int acDeactEnd[] = { 0, 31, 31, 31, 31 };
const qboolean turretIdle[] = { false, false, true, true }; // collapse when idle?
// turret animations
const int turretIdleStart = 0;
const int turretIdleEnd = 0;
const int turretActStart = 1;
const int turretActEnd = 9;
const int turretActiveStart = 10;
const int turretActiveEnd = 10;
const int turretDeactStart = 23;
const int turretDeactEnd = 31;
// bullet params
#define AC_BULLET_DMG 4.0
#define AC_BULLET_KICK 2.0
// rocket params
#define AC_ROCKET_DMG 100
#define AC_ROCKET_SPEED 650
#define AC_ROCKET_RADIUS_DMG 120
#define AC_ROCKET_DMG_RADIUS 120
// blaster params
#define AC_BLASTER_DMG 20
#define AC_BLASTER_SPEED 1000
void monster_autocannon_fire (edict_t *self)
{
vec3_t forward, right, start;
// fire straight ahead
AngleVectors (self->s.angles, forward, right, NULL);
if (self->onFloor)
VectorNegate(right, right);
VectorMA(self->s.origin, 24, forward, start);
G_ProjectSource (self->s.origin, fireOffset[self->style], forward, right, start);
if (EMPNukeCheck(self, start))
{
gi.sound (self, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
return;
}
// what to fire?
switch (self->style)
{
case 1:
default:
fire_bullet (self, start, forward, AC_BULLET_DMG, AC_BULLET_KICK, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_AUTOCANNON);
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ_CHAINGUN2);
gi.multicast (self->s.origin, MULTICAST_PVS);
break;
case 2:
fire_rocket (self, start, forward, AC_ROCKET_DMG, AC_ROCKET_SPEED, AC_ROCKET_RADIUS_DMG, AC_ROCKET_DMG_RADIUS, NULL);
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ_ROCKET);
gi.multicast (self->s.origin, MULTICAST_PVS);
break;
case 3:
case 4:
fire_blaster (self, start, forward, AC_BLASTER_DMG, AC_BLASTER_SPEED, EF_HYPERBLASTER, true, BLASTER_ORANGE);
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ_HYPERBLASTER);
gi.multicast (self->s.origin, MULTICAST_PVS);
break;
}
}
qboolean angleBetween (float *ang, float *min, float *max)
{
// directly between?
if (*ang > *min && *ang < *max)
return true;
// make positive
while (*min < 0)
*min += 360.0;
while (*ang < *min)
*ang += 360.0;
while (*max < *min)
*max += 360.0;
if (*ang > *min && *ang < *max)
return true;
else
return false;
}
float mod180 (float val)
{
while (val > 180)
val -= 360.0;
while (val < -180)
val += 360.0;
return val;
}
qboolean canShoot (edict_t *self, edict_t *e)
{
vec3_t delta;
vec3_t dangles;
VectorSubtract(e->s.origin, self->s.origin, delta);
vectoangles(delta, dangles);
dangles[PITCH] = mod180(dangles[PITCH]);
if ((!self->onFloor && dangles[PITCH] < 0) ||
(self->onFloor && dangles[PITCH] > 0)) // facing up or down
return false;
if (self->monsterinfo.linkcount > 0)
{
float ideal_yaw = self->monsterinfo.attack_state;
float max_yaw = anglemod(ideal_yaw + self->monsterinfo.linkcount);
float min_yaw = anglemod(ideal_yaw - self->monsterinfo.linkcount);
if (!angleBetween(&dangles[YAW], &min_yaw, &max_yaw))
return false;
}
return true;
}
qboolean autocannonInfront (edict_t *self, edict_t *other)
{
vec3_t vec;
vec3_t angle;
float dot;
float min = -30.0;
float max = 30.0;
// what's the yaw distance between the 2?
VectorSubtract (other->s.origin, self->s.origin, vec);
vectoangles(vec, angle);
dot = angle[YAW] - self->s.angles[YAW];
if (angleBetween(&dot, &min, &max))
return true;
return false;
}
void monster_autocannon_findenemy (edict_t *self)
{
edict_t *e = NULL;
// can we still use our enemy?
if (self->enemy)
{
if (!canShoot(self, self->enemy))
{
self->oldenemy = NULL;
self->enemy = NULL;
}
else if (!visible(self, self->enemy))
{
self->oldenemy = self->enemy;
self->enemy = NULL;
}
else if (self->enemy->flags & FL_NOTARGET)
{
self->oldenemy = NULL;
self->enemy = NULL;
}
else if (self->enemy->health <= 0)
{
self->oldenemy = NULL;
self->enemy = NULL;
}
}
while (self->enemy == NULL)
{
e = findradius(e, self->s.origin, AC_RANGE);
if (e == NULL)
{
if (self->oldenemy == NULL)
return;
if (level.time > self->timeout)
{
self->oldenemy = NULL;
return;
}
self->enemy = self->oldenemy;
break;
}
if (self->spawnflags & AC_SF_BERSERK)
{
// attack clients and monsters
if (!e->client && !(e->svflags & SVF_MONSTER))
continue;
}
else
{
// only attack clients
if (!e->client)
continue;
}
// don't target dead stuff
if (e->health <= 0)
continue;
// don't target notarget stuff
if (e->flags & FL_NOTARGET)
continue;
// don't target other autocannons
if (Q_stricmp(e->classname, "monster_autocannon") == 0)
continue;
// don't target self
if (e == self)
continue;
// can it be seen?
if (!visible(self, e))
continue;
if (!autocannonInfront(self, e))
continue;
if (canShoot(self, e))
self->enemy = e;
}
}
void monster_autocannon_turn (edict_t *self)
{
vec3_t old_angles;
VectorCopy(self->s.angles, old_angles);
if (!self->enemy)
{
if (self->monsterinfo.linkcount > 0)
{
int ideal_yaw = self->monsterinfo.attack_state;
int max_yaw = anglemod(ideal_yaw + self->monsterinfo.linkcount);
int min_yaw = anglemod(ideal_yaw - self->monsterinfo.linkcount);
while (max_yaw < min_yaw)
max_yaw += 360.0;
self->s.angles[YAW] += (self->monsterinfo.lefty ? -AC_TURN_SPEED : AC_TURN_SPEED);
// back and forth
if (self->s.angles[YAW] > max_yaw)
{
self->monsterinfo.lefty = 1;
self->s.angles[YAW] = max_yaw;
}
else if (self->s.angles[YAW] < min_yaw)
{
self->monsterinfo.lefty = 0;
self->s.angles[YAW] = min_yaw;
}
}
else
{
self->s.angles[YAW] = anglemod(self->s.angles[YAW] + AC_TURN_SPEED);
}
// angle pitch towards 5 to 10...
if (!self->onFloor)
{
if (self->s.angles[PITCH] > 10)
self->s.angles[PITCH] -= 4;
else if (self->s.angles[PITCH] < 5)
self->s.angles[PITCH] += 4;
}
else
{
if (self->s.angles[PITCH] < -10)
self->s.angles[PITCH] += 4;
else if (self->s.angles[PITCH] > -5)
self->s.angles[PITCH] -= 4;
}
}
else
{
// look toward enemy mid point
if (visible(self, self->enemy))
{
vec3_t offset, dest;
VectorCopy(self->enemy->mins, offset);
VectorAdd(offset, self->enemy->maxs, offset);
VectorScale(offset, 0.65, offset);
VectorAdd(self->enemy->s.origin, offset, dest);
angleToward(self, dest, AC_TURN_SPEED);
VectorCopy(dest, self->monsterinfo.last_sighting);
self->timeout = level.time + AC_TIMEOUT;
// restrict our range of movement if need be
if (self->monsterinfo.linkcount > 0)
{
float amax = anglemod(self->monsterinfo.attack_state + self->monsterinfo.linkcount);
float amin = anglemod(self->monsterinfo.attack_state - self->monsterinfo.linkcount);
self->s.angles[YAW] = anglemod(self->s.angles[YAW]);
if (!angleBetween(&self->s.angles[YAW], &amin, &amax))
{
// which is closer?
if (self->s.angles[YAW] - amax < amin - self->s.angles[YAW])
self->s.angles[YAW] = amin;
else
self->s.angles[YAW] = amax;
}
}
}
else // not visible now, so head toward last known spot
angleToward(self, self->monsterinfo.last_sighting, AC_TURN_SPEED);
}
// get our angles between 180 and -180
while (self->s.angles[PITCH] > 180)
self->s.angles[PITCH] -= 360.0;
while (self->s.angles[PITCH] < -180)
self->s.angles[PITCH] += 360;
// outside of the pitch extents?
if (self->s.angles[PITCH] > acPitchExtents[self->onFloor][1])
self->s.angles[PITCH] = acPitchExtents[self->onFloor][1];
else if (self->s.angles[PITCH] < acPitchExtents[self->onFloor][0])
self->s.angles[PITCH] = acPitchExtents[self->onFloor][0];
// make sure the turret's angles match the gun's
self->chain->s.angles[YAW] = self->s.angles[YAW];
self->chain->s.angles[PITCH] = 0;
// setup the sound
if (VectorCompare(self->s.angles, old_angles))
self->chain->s.sound = 0;
else
self->chain->s.sound = gi.soundindex("objects/acannon/ac_idle.wav");
}
void monster_autocannon_think (edict_t *self)
{
ac_anim_frame_t frame;
ac_anim_t anim;
int lefty = 0;
edict_t *old_enemy;
self->nextthink = level.time + FRAMETIME;
// get an enemy
old_enemy = self->enemy;
monster_autocannon_findenemy(self);
if (self->enemy != NULL && old_enemy != self->enemy)
gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_act.wav"), 1, ATTN_NORM, 0);
// turn whereever
lefty = self->monsterinfo.lefty;
if (level.time > self->delay)
{
monster_autocannon_turn(self);
if (self->monsterinfo.lefty != lefty)
self->delay = level.time + AC_TURN_DELAY;
}
anim = acFiringFrames[self->style];
frame = anim.frames[self->seq];
// ok, we don't have an enemy
if (self->enemy == NULL)
{
if (self->seq == 0)
{
// get into idle animation
self->s.frame++;
if (self->s.frame > acActiveEnd[self->style] ||
self->s.frame < acActiveStart[self->style])
self->s.frame = acActiveStart[self->style];
return; // done, we want to wait here
}
// set the frame
self->s.frame = frame.frame;
// fire
if (frame.fire)
monster_autocannon_fire(self);
// if we're not done with the firing sequence, we need to finish it off
if (frame.last) // end of the loop or firing frame?
self->seq = 0;
else
self->seq++;
return;
}
// we have an enemy but he's not infront, go to the beginning of the firing sequence
if (!autocannonInfront(self, self->enemy))
{
self->s.frame = frame.frame;
if (self->seq == anim.firstNonPause)
return; // done, we want to wait here
if (frame.last) // end of the loop or firing frame?
self->seq = anim.firstNonPause;
else
self->seq++;
return;
}
// we have an enemy, AND he's visible
// let's kick his ass
self->s.frame = frame.frame;
if (frame.fire)
monster_autocannon_fire(self);
if (frame.last) // end of the loop?
self->seq = anim.firstNonPause;
else
self->seq++;
}
void monster_autocannon_explode (edict_t *ent)
{
vec3_t origin;
T_RadiusDamage(ent, ent, AC_EXPLODE_DMG, ent->enemy, AC_EXPLODE_RADIUS, MOD_TRIPBOMB);
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
gi.WriteByte (svc_temp_entity);
if (ent->waterlevel)
{
if (ent->groundentity)
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
else
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
}
else
{
if (ent->groundentity)
gi.WriteByte (TE_GRENADE_EXPLOSION);
else
gi.WriteByte (TE_ROCKET_EXPLOSION);
}
gi.WritePosition (origin);
gi.multicast (ent->s.origin, MULTICAST_PHS);
// set the pain skin
ent->chain->chain->s.skinnum = 1; // pain
ent->chain->chain->rideWith[0] = NULL;
ent->chain->chain->rideWith[1] = NULL;
G_FreeEdict(ent->chain);
G_FreeEdict(ent);
}
void monster_autocannon_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
// explode
self->takedamage = DAMAGE_NO;
self->think = monster_autocannon_explode;
self->nextthink = level.time + FRAMETIME;
}
void monster_autocannon_pain (edict_t *self, edict_t *other, float kick, int damage)
{
// keep the enemy
if (other->client || other->svflags & SVF_MONSTER)
self->enemy = other;
}
void monster_autocannon_activate (edict_t *self)
{
self->active = AC_S_ACTIVATING;
self->nextthink = level.time + FRAMETIME;
// go thru the activation frames
if (self->s.frame >= acActStart[self->style] &&
self->s.frame < acActEnd[self->style])
{
if (self->s.frame == acActStart[self->style])
{
// gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_out.wav"), 1, ATTN_NORM, 0);
}
// continue
self->s.frame++;
self->chain->s.frame++;
}
else if (self->s.frame == acActEnd[self->style])
{
self->s.frame = acActiveStart[self->style];
self->chain->s.frame = turretActiveStart;
self->think = monster_autocannon_think;
self->active = AC_S_ACTIVE;
}
else
{
self->s.frame = acActStart[self->style];
self->chain->s.frame = turretActStart;
}
}
void monster_autocannon_deactivate (edict_t *self)
{
self->active = AC_S_DEACTIVATING;
self->nextthink = level.time + FRAMETIME;
// go thru the deactivation frames
if (self->s.angles[PITCH] != 0)
{
if (self->s.angles[PITCH] > 0)
{
self->s.angles[PITCH] -= 5;
if (self->s.angles[PITCH] < 0)
self->s.angles[PITCH] = 0;
}
else
{
self->s.angles[PITCH] += 5;
if (self->s.angles[PITCH] > 0)
self->s.angles[PITCH] = 0;
}
}
else if (self->s.frame >= acDeactStart[self->style] &&
self->s.frame < acDeactEnd[self->style])
{
self->chain->s.sound = 0;
if (self->s.frame == acDeactStart[self->style])
{
//gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_away.wav"), 1, ATTN_NORM, 0);
}
// continue
self->s.frame++;
self->chain->s.frame++;
}
else if (self->s.frame == acDeactEnd[self->style])
{
self->s.frame = acIdleStart[self->style];
self->chain->s.frame = turretIdleStart;
self->think = NULL;
self->nextthink = 0;
self->chain->s.sound = 0;
self->active = AC_S_IDLE;
}
else
{
self->s.frame = acDeactStart[self->style];
self->chain->s.frame = turretDeactStart;
}
}
void monster_autocannon_act (edict_t *self)
{
if (self->active == AC_S_IDLE)
{
if (acActStart[self->style] != -1)
self->think = monster_autocannon_activate;
else
{
self->s.frame = acActiveStart[self->style];
self->chain->s.frame = turretActiveStart;
self->think = monster_autocannon_think;
self->active = AC_S_ACTIVE;
}
self->nextthink = level.time + FRAMETIME;
}
else if (self->active == AC_S_ACTIVE)
{
if (acDeactStart[self->style] != -1)
{
self->nextthink = level.time + FRAMETIME;
self->think = monster_autocannon_deactivate;
}
else
{
if (turretIdle[self->style])
self->chain->s.frame = turretIdleStart;
else
self->chain->s.frame = turretActiveStart;
self->s.frame = acActiveStart[self->style];
self->think = NULL;
self->active = AC_S_IDLE;
self->nextthink = 0;
}
}
}
void monster_autocannon_use (edict_t *self, edict_t *other, edict_t *activator)
{
// on/off or berserk toggle?
if (self->spawnflags & AC_SF_BERSERK_TOGGLE)
{
if (self->spawnflags & AC_SF_BERSERK)
self->spawnflags &= ~AC_SF_BERSERK;
else
self->spawnflags |= AC_SF_BERSERK;
}
else
monster_autocannon_act(self);
}
void monster_autocannon_usestub (edict_t *self)
{
// stub
monster_autocannon_act(self);
}
/*QUAKED monster_autocannon (1 .5 0) (-12 -12 -28) (12 12 16) Ambush Trigger_Spawn Sight GoodGuy
Ceiling autoturret.
"style"
1: chaingun
2: rocket
3: blaster
4: slow blaster
model="models/objects/acannon/base/"
model2="models/objects/acannon/turret/"
*/
/*QUAKED monster_autocannon_floor (1 .5 0) (-12 -12 -16) (12 12 28) Ambush Trigger_Spawn Sight GoodGuy
Floor autoturret.
"style"
2: rocket
3: blaster
4: slow blaster
model="models/objects/acannon/base2/"
model2="models/objects/acannon/turret2/"
*/
void SP_monster_autocannon (edict_t *self)
{
edict_t *base, *turret;
vec3_t offset;
if (deathmatch->value)
{
G_FreeEdict(self);
return;
}
if (self->style > 4 || self->style < 1)
self->style = 1;
// if we're on hard or nightmare, use fast lasers
if (skill->value >= 2 && self->style == 4)
self->style = 3;
// precache some sounds and models
gi.soundindex("objects/acannon/ac_idle.wav");
gi.soundindex("objects/acannon/ac_act.wav");
//gi.soundindex("objects/acannon/ac_out.wav");
//gi.soundindex("objects/acannon/ac_away.wav");
gi.modelindex("models/objects/rocket/tris.md2");
gi.modelindex("models/objects/laser/tris.md2");
// create the base
base = G_Spawn();
base->classname = "autocannon base";
base->solid = SOLID_BBOX;
VectorCopy(self->s.origin, base->s.origin);
if (!self->onFloor)
base->movetype = MOVETYPE_NONE;
else
base->movetype = MOVETYPE_RIDE; // make the base MOVETYPE_RIDE so that it can ride on trains
if (!self->onFloor)
base->s.modelindex = gi.modelindex("models/objects/acannon/base/tris.md2");
else
base->s.modelindex = gi.modelindex("models/objects/acannon/base2/tris.md2");
gi.linkentity(base);
// create the turret
turret = G_Spawn();
turret->classname = "autocannon turret";
turret->solid = SOLID_BBOX;
turret->movetype = MOVETYPE_NONE;
turret->chain = base;
VectorCopy(self->s.origin, turret->s.origin);
if (!self->onFloor)
turret->s.modelindex = gi.modelindex("models/objects/acannon/turret/tris.md2");
else
turret->s.modelindex = gi.modelindex("models/objects/acannon/turret2/tris.md2");
if (turretIdle[self->style])
turret->s.frame = turretIdleStart;
else
turret->s.frame = turretActiveStart;
turret->s.angles[YAW] = self->s.angles[YAW];
turret->s.angles[PITCH] = 0;
gi.linkentity(turret);
// fill in the details about ourself
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_NONE;
if (!self->onFloor)
VectorSet(offset, 0, 0, -20); // offset down a bit
else
VectorSet(offset, 0, 0, 20); // offset up a bit
VectorAdd(self->s.origin, offset, self->s.origin);
// set the bounding box
if (!self->onFloor)
{
VectorSet(self->mins, -12, -12, -28);
VectorSet(self->maxs, 12, 12, 16);
}
else
{
VectorSet(self->mins, -12, -12, -16);
VectorSet(self->maxs, 12, 12, 28);
}
self->chain = turret;
if (!self->onFloor)
self->s.modelindex = gi.modelindex(models[self->style]);
else
self->s.modelindex = gi.modelindex(floorModels[self->style]);
self->s.frame = acIdleStart[self->style];
self->active = AC_S_IDLE;
self->monsterinfo.lefty = 0;
self->monsterinfo.attack_state = self->s.angles[YAW]; // used for centre of back-and-forth "search"
self->seq = 0;
if (st.lip)
self->monsterinfo.linkcount = (st.lip > 0 ? st.lip : 0);
// self->svflags = SVF_MONSTER;
self->svflags |= SVF_AUTOMATON; // Knightmare added
// default health
if (!self->health)
self->health = 100;
// enable/disable? ... berserk/not
if (self->targetname)
self->use = monster_autocannon_use;
if (self->spawnflags & AC_SF_BERSERK_TOGGLE || !(self->spawnflags & AC_SF_START_OFF))
{
self->think = monster_autocannon_usestub;
self->nextthink = level.time + FRAMETIME;
}
self->takedamage = DAMAGE_AIM;
self->die = monster_autocannon_die;
self->pain = monster_autocannon_pain;
// last but not least, setup the "rideWith" information
base->rideWith[0] = turret;
VectorSubtract(turret->s.origin, base->s.origin, base->rideWithOffset[0]);
base->rideWith[1] = self;
VectorSubtract(self->s.origin, base->s.origin, base->rideWithOffset[1]);
// Lazarus
self->common_name = "Autocannon";
gi.linkentity(self);
}
void SP_monster_autocannon_floor (edict_t *self)
{
if (self->style == 1)
{
gi.error("monster_autocannon_floor does not permit bullet style");
G_FreeEdict(self);
return;
}
if (self->style < 1 || self->style > 4)
self->style = 2;
self->onFloor = 1; // signify floor mounted
// call the other one
SP_monster_autocannon(self);
}

View file

@ -1732,7 +1732,10 @@ void SP_monster_zboss_precache (void)
}
/*QUAKED monster_zboss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
/*QUAKED monster_zboss (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight GoodGuy NoGib
model="models/monsters/bossz/mech/"
model2="models/monsters/bossz/pilot/"
model3="models/monsters/bossz/grapple/"
*/
void SP_monster_zboss (edict_t *self)
{

View file

@ -422,7 +422,10 @@ void SP_monster_handler_precache(void)
}
/*QUAKED monster_handler (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
/*QUAKED monster_handler (1 .5 0) (-32 -32 -24) (32 32 32) Ambush Trigger_Spawn Sight
Enforcer with hound. Releases hound on sight.
model="models/monsters/guard/handler/"
model2="models/monsters/guard/hound/"
*/
void SP_monster_handler (edict_t *self)
{

View file

@ -570,7 +570,8 @@ void SP_monster_hound_precache(void)
}
/*QUAKED monster_hound (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
/*QUAKED monster_hound (1 .5 0) (-16 -16 -24) (16 16 24) Ambush Trigger_Spawn Sight GoodGuy NoGib
model="models/monsters/guard/hound/"
*/
void SP_monster_hound (edict_t *self)
{

View file

@ -42,6 +42,7 @@ vec_t VectorLengthSquared (vec3_t v)
return length;
}
#endif
void angleToward (edict_t *self, vec3_t point, float speed)
{
@ -65,7 +66,6 @@ void angleToward (edict_t *self, vec3_t point, float speed)
vel = VectorLength(self->velocity);
VectorScale(forward, vel, self->velocity);
}
#endif
#define MAXROTATION 20