#include "g_local.h" #include "m_player.h" extern qboolean is_quad; extern qboolean is_double; extern qboolean is_quadfire; extern byte is_silenced; //void playQuadSound(edict_t *ent); void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent)); void NoAmmoWeaponChange (edict_t *ent); void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed); void Grenade_Explode(edict_t *ent); void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); #ifdef USE_ZAERO_WEAPONS void fire_sconnan (edict_t *self); void fire_sconnanEffects (edict_t *self); #endif // USE_ZAERO_WEAPONS const int SC_MAXFIRETIME = 5; // in seconds... const int SC_BASEDAMAGE = 10; // minimum damage const int SC_DAMGERANGE = 990; // maximum damaged range (max damage possible is SC_BASEDAMAGE + SC_DAMGERANGE) const int SC_MAXRADIUS = 500; // maximum blast radius const int SC_MAXCELLS = 100; // maximum number of cells #if 0 vec_t VectorLengthSquared (vec3_t v) { int i; float length; length = 0; for (i=0 ; i< 3 ; i++) length += v[i]*v[i]; return length; } #endif void angleToward (edict_t *self, vec3_t point, float speed) { vec3_t forward; float yaw = 0.0; float vel = 0.0; vec3_t delta; vec3_t destAngles; VectorSubtract(point, self->s.origin, delta); vectoangles(delta, destAngles); self->ideal_yaw = destAngles[YAW]; self->yaw_speed = speed; M_ChangeYaw(self); yaw = self->s.angles[YAW]; self->ideal_yaw = destAngles[PITCH]; self->s.angles[YAW] = self->s.angles[PITCH]; M_ChangeYaw(self); self->s.angles[PITCH] = self->s.angles[YAW]; self->s.angles[YAW] = yaw; AngleVectors (self->s.angles, forward, NULL, NULL); vel = VectorLength(self->velocity); VectorScale(forward, vel, self->velocity); } #define MAXROTATION 20 /* Laser Trip Bombs */ #define TBOMB_KILLABLE // spawnflags #define CHECK_BACK_WALL 1 // variables #define TBOMB_DELAY 1.0 #define TBOMB_TIMEOUT 180 #define TBOMB_DAMAGE 150 #define TBOMB_RADIUS_DAMAGE 384 #define TBOMB_HEALTH 100 #define TBOMB_SHRAPNEL 5 #define TBOMB_SHRAPNEL_DMG 15 #define TBOMB_MAX_EXIST 25 //#ifdef USE_ZAERO_ITEMS_WEAPONS void shrapnel_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { // do damage if we can if (!other->takedamage) return; if (VectorCompare(ent->velocity, vec3_origin)) return; T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, sk_tbomb_shrapnel_damage->value, 8, 0, MOD_TRIPBOMB); G_FreeEdict(ent); } void TripBomb_Explode (edict_t *ent) { vec3_t origin; int i = 0, type; T_RadiusDamage(ent, ent->owner ? ent->owner : ent, ent->dmg, ent->enemy, ent->dmg_radius, MOD_TRIPBOMB); VectorMA (ent->s.origin, -0.02, ent->velocity, origin); if (ent->waterlevel) { if (ent->groundentity) // gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); type = TE_GRENADE_EXPLOSION_WATER; else // gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); type = TE_ROCKET_EXPLOSION_WATER; } else { if (ent->groundentity) // gi.WriteByte (TE_GRENADE_EXPLOSION); type = TE_GRENADE_EXPLOSION; else // gi.WriteByte (TE_ROCKET_EXPLOSION); type = TE_ROCKET_EXPLOSION; } gi.WriteByte (svc_temp_entity); gi.WriteByte (type); gi.WritePosition (origin); gi.multicast (ent->s.origin, MULTICAST_PHS); // Lazarus reflections if (level.num_reflectors) ReflectExplosion (type, origin); // throw off some debris for (i = 0; i < (int)sk_tbomb_shrapnel->value; i++) { edict_t *sh = G_Spawn(); vec3_t forward, right, up; sh->classname = "shrapnel"; sh->movetype = MOVETYPE_BOUNCE; sh->solid = SOLID_BBOX; sh->s.effects |= EF_GRENADE; sh->s.modelindex = gi.modelindex("models/objects/shrapnel/tris.md2"); sh->owner = ent->owner; VectorSet (sh->avelocity, 300, 300, 300); VectorCopy(ent->s.origin, sh->s.origin); AngleVectors (ent->s.angles, forward, right, up); VectorScale(forward, 500, forward); VectorMA(forward, crandom()*500, right, forward); VectorMA(forward, crandom()*500, up, forward); VectorCopy(forward, sh->velocity); sh->touch = shrapnel_touch; #ifdef KMQUAKE2_ENGINE_MOD sh->think = gib_fade; // Knightmare- have the debris fade away #else sh->think = G_FreeEdict; #endif // KMQUAKE2_ENGINE_MOD sh->nextthink = level.time + 3.0 + crandom() * 1.5; } G_FreeEdict (ent); } // Knightmare added #ifdef TBOMB_KILLABLE void tripbomb_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { // gi.dprintf("tripbomb_die\n"); // if set off by another TripBomb, delay a little (chained explosions) if ( strcmp(inflictor->classname, "ired") ) { self->takedamage = DAMAGE_NO; if (self->chain) { G_FreeEdict (self->chain); self->chain = NULL; } TripBomb_Explode (self); } else { self->takedamage = DAMAGE_NO; if (self->chain) { G_FreeEdict (self->chain); self->chain = NULL; } self->think = TripBomb_Explode; self->nextthink = level.time + FRAMETIME; } } #endif // TBOMB_KILLABLE // end Knightmare void tripbomb_laser_think (edict_t *self) { vec3_t start; vec3_t end; vec3_t delta; trace_t tr; int count = 8; self->nextthink = level.time + FRAMETIME; if (level.time > self->timeout) { // play a sound //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0); // blow up self->chain->think = TripBomb_Explode; self->chain->nextthink = level.time + FRAMETIME; G_FreeEdict(self); return; } // randomly phase out or EMPNuke is in effect if (EMPNukeCheck(self, self->s.origin) || random() < 0.1) { self->svflags |= SVF_NOCLIENT; return; } self->svflags &= ~SVF_NOCLIENT; VectorCopy (self->s.origin, start); VectorMA (start, 2048, self->movedir, end); tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); if (!tr.ent) return; VectorSubtract(tr.endpos, self->move_origin, delta); if (VectorCompare(self->s.origin, self->move_origin)) { // we haven't done anything yet VectorCopy(tr.endpos, self->move_origin); if (self->spawnflags & 0x80000000) { self->spawnflags &= ~0x80000000; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_LASER_SPARKS); gi.WriteByte (count); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (self->s.skinnum); gi.multicast (tr.endpos, MULTICAST_PVS); } } else if (VectorLength(delta) > 1.0) { // play a sound //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0); // blow up self->chain->think = TripBomb_Explode; self->chain->nextthink = level.time + FRAMETIME; G_FreeEdict(self); return; } VectorCopy(self->move_origin, self->s.old_origin); } void tripbomb_laser_on (edict_t *self) { self->svflags &= ~SVF_NOCLIENT; self->think = tripbomb_laser_think; // play a sound gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_arm.wav"), 1, ATTN_NORM, 0); tripbomb_laser_think(self); //gi.positioned_sound(self->s.old_origin, self, CHAN_AUTO, gi.soundindex("weapons/ired/las_tink.wav"), 1, ATTN_NORM, 0); } void create_tripbomb_laser (edict_t *bomb) { // create the laser edict_t *laser = G_Spawn(); bomb->chain = laser; laser->classname = "laser trip bomb laser"; VectorCopy(bomb->s.origin, laser->s.origin); VectorCopy(bomb->s.origin, laser->move_origin); VectorCopy(bomb->s.angles, laser->s.angles); G_SetMovedir (laser->s.angles, laser->movedir); laser->owner = bomb; laser->s.skinnum = 0xb0b1b2b3; // <- faint purple 0xf3f3f1f1 <-blue red-> 0xf2f2f0f0; laser->s.frame = 2; laser->movetype = MOVETYPE_NONE; laser->solid = SOLID_NOT; laser->s.renderfx |= RF_BEAM|RF_TRANSLUCENT; laser->s.modelindex = 1; laser->chain = bomb; laser->spawnflags |= 0x80000001; laser->think = tripbomb_laser_on; laser->nextthink = level.time + FRAMETIME; laser->svflags |= SVF_NOCLIENT; laser->timeout = level.time + sk_tbomb_life->value; // was TBOMB_TIMEOUT; gi.linkentity (laser); } void use_tripbomb (edict_t *self, edict_t *other, edict_t *activator) { if (self->chain) { // we already have a laser, remove it G_FreeEdict (self->chain); self->chain = NULL; } else // create the laser create_tripbomb_laser (self); } void turnOffGlow (edict_t *self) { self->s.effects &= ~EF_COLOR_SHELL; self->s.renderfx &= ~RF_SHELL_GREEN; self->think = NULL; self->nextthink = 0; } // Knightmare- removed this for shootable tripbombs #ifdef TBOMB_KILLABLE void tripbomb_pain (edict_t *self, edict_t *other, float kick, int damage) { } #else void tripbomb_pain (edict_t *self, edict_t *other, float kick, int damage) { // play the green glow sound //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_glow.wav"), 1, ATTN_NORM, 0); // turn on the glow self->damage_debounce_time = level.time + 0.2; // if we don't have a think function, then turn this thing on if (self->think == NULL) { self->s.effects |= EF_COLOR_SHELL; self->s.renderfx |= RF_SHELL_GREEN; self->nextthink = self->damage_debounce_time; self->think = turnOffGlow; } } #endif // TBOMB_KILLABLE void tripbomb_think (edict_t *self) { if (self->chain == NULL) { // check whether we need to create the laser if (self->timeout < level.time) { create_tripbomb_laser(self); } } // do we need to show damage? if (self->damage_debounce_time > level.time) { self->s.effects |= EF_COLOR_SHELL; self->s.renderfx |= RF_SHELL_GREEN; } else { self->s.effects &= ~EF_COLOR_SHELL; self->s.renderfx &= ~RF_SHELL_GREEN; } self->nextthink = level.time + FRAMETIME; } void setupBomb (edict_t *bomb, char *classname, float damage, float damage_radius) { bomb->classname = classname; VectorSet(bomb->mins, -8, -8, -8); VectorSet(bomb->maxs, 8, 8, 8); bomb->solid = SOLID_BBOX; bomb->movetype = MOVETYPE_NONE; bomb->s.modelindex = gi.modelindex("models/objects/ired/tris.md2"); bomb->radius_dmg = damage; bomb->dmg = damage; bomb->dmg_radius = damage_radius; // Knightmare- made the tripbomb destroyable #ifdef TBOMB_KILLABLE bomb->health = (int)sk_tbomb_health->value; // was 1 bomb->takedamage = DAMAGE_AIM; bomb->pain = NULL; bomb->die = tripbomb_die; #else bomb->health = 1; bomb->takedamage = DAMAGE_IMMORTAL; // health will not be deducted bomb->pain = tripbomb_pain; #endif // TBOMB_KILLABLE } void removeOldest (void) { edict_t *oldestEnt = NULL; edict_t *e = NULL; int count = 0; while (1) { e = G_Find(e, FOFS(classname), "ired"); if (e == NULL) // no more break; count++; if (oldestEnt == NULL || e->timestamp < oldestEnt->timestamp) { oldestEnt = e; } } // do we have too many? if (count > TBOMB_MAX_EXIST && oldestEnt != NULL) { // get this tbomb to explode oldestEnt->think = TripBomb_Explode; oldestEnt->nextthink = level.time + FRAMETIME; G_FreeEdict(oldestEnt->chain); } } qboolean fire_lasertripbomb (edict_t *self, vec3_t start, vec3_t dir, float timer, float damage, float damage_radius, qboolean quad) { // trace a line trace_t tr; vec3_t endPos; vec3_t _dir; edict_t *bomb = NULL; edict_t *laser = NULL; VectorScale(dir, 64, _dir); VectorAdd(start, _dir, endPos); // trace ahead, looking for a wall tr = gi.trace(start, NULL, NULL, endPos, self, MASK_SHOT); if (tr.fraction == 1.0) { // not close enough // gi.cprintf(self, PRINT_HIGH, "Not close enough to a wall"); return false; } if (Q_stricmp(tr.ent->classname, "worldspawn") != 0) { // gi.cprintf(self, PRINT_HIGH, "Hit something other than a wall"); return false; } // create the bomb bomb = G_Spawn(); // VectorCopy(tr.endpos, bomb->s.origin); VectorMA(tr.endpos, 3, tr.plane.normal, bomb->s.origin); vectoangles(tr.plane.normal, bomb->s.angles); bomb->owner = self; setupBomb (bomb, "ired", damage, damage_radius); gi.linkentity(bomb); bomb->timestamp = level.time; bomb->timeout = level.time + timer; bomb->nextthink = level.time + FRAMETIME; bomb->think = tripbomb_think; // remove the oldest trip bomb removeOldest(); // play a sound gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_set.wav"), 1, ATTN_NORM, 0); return true; } void weapon_lasertripbomb_fire (edict_t *ent) { if (ent->client->ps.gunframe == 10) { vec3_t offset; vec3_t forward; vec3_t start; int damage = sk_tbomb_damage->value; float radius = sk_tbomb_radius->value; if (is_quad) damage *= 4; if (is_double) damage *= 2; // place the trip bomb VectorSet(offset, 0, 0, ent->viewheight * 0.75); AngleVectors (ent->client->v_angle, forward, NULL, NULL); VectorAdd(ent->s.origin, offset, start); if (fire_lasertripbomb(ent, start, forward, TBOMB_DELAY, damage, radius, is_quad)) { ent->client->pers.inventory[ent->client->ammo_index] -= 1; // switch models ent->client->ps.gunindex = gi.modelindex("models/weapons/v_ired/hand.md2"); // play quad sound // playQuadSound (ent); } } else if (ent->client->ps.gunframe == 15) { // switch models back int mi = gi.modelindex("models/weapons/v_ired/tris.md2"); if (ent->client->ps.gunindex != mi) { ent->client->ps.gunindex = mi; // go back to get another trip bomb ent->client->ps.gunframe = 0; return; } } else if (ent->client->ps.gunframe == 6) { ent->client->ps.gunframe = 16; return; } ent->client->ps.gunframe++; } void Weapon_LaserTripBomb (edict_t *ent) { static int pause_frames[] = {24, 33, 43, 0}; static int fire_frames[] = {6, 10, 15, 0}; const int deactivateFirst = 44; const int deactivateLast = 48; const int idleFirst = 16; const int idleLast = 43; const int fireFirst = 7; const int fireLast = 15; const int activateFirst = 0; const int activateLast = 6; if (ent->client->weaponstate == WEAPON_DROPPING) { if (ent->client->ps.gunframe == deactivateLast) { ChangeWeapon (ent); return; } ent->client->ps.gunframe++; return; } if (ent->client->weaponstate == WEAPON_ACTIVATING) { if (ent->client->ps.gunframe == activateLast) { ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunframe = idleFirst; return; } ent->client->ps.gunframe++; return; } if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) { ent->client->weaponstate = WEAPON_DROPPING; ent->client->ps.gunframe = deactivateFirst; return; } if (ent->client->weaponstate == WEAPON_READY) { if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) ) { ent->client->latched_buttons &= ~BUTTON_ATTACK; if(ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = fireFirst; ent->client->weaponstate = WEAPON_FIRING; // start the animation ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1-1; ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1-1; ent->client->anim_end = FRAME_attack8; } } else { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange (ent); } } else { if (ent->client->ps.gunframe == idleLast) { ent->client->ps.gunframe = idleFirst; return; } if (pause_frames) { int n = 0; for (n = 0; pause_frames[n]; n++) { if (ent->client->ps.gunframe == pause_frames[n]) { if (rand()&15) return; } } } ent->client->ps.gunframe++; return; } } if (ent->client->weaponstate == WEAPON_FIRING) { int n = 0; for (n = 0; fire_frames[n]; n++) { if (ent->client->ps.gunframe == fire_frames[n]) { weapon_lasertripbomb_fire(ent); break; } } if (!fire_frames[n]) ent->client->ps.gunframe++; if (ent->client->ps.gunframe == idleFirst+1) ent->client->weaponstate = WEAPON_READY; } } void SP_misc_lasertripbomb (edict_t *bomb) { // precache gi.soundindex("weapons/ired/las_set.wav"); gi.soundindex("weapons/ired/las_arm.wav"); // gi.soundindex("weapons/ired/las_tink.wav"); // gi.soundindex("weapons/ired/las_trig.wav"); // gi.soundindex("weapons/ired/las_glow.wav"); gi.modelindex("models/objects/shrapnel/tris.md2"); gi.modelindex("models/objects/ired/tris.md2"); if (bomb->spawnflags & CHECK_BACK_WALL) { vec3_t forward, endPos; trace_t tr; // look backwards toward a wall AngleVectors (bomb->s.angles, forward, NULL, NULL); VectorMA(bomb->s.origin, -64.0, forward, endPos); tr = gi.trace(bomb->s.origin, NULL, NULL, endPos, bomb, MASK_SOLID); VectorCopy(tr.endpos, bomb->s.origin); vectoangles(tr.plane.normal, bomb->s.angles); } // set up ourself setupBomb(bomb, "misc_ired", sk_tbomb_damage->value, sk_tbomb_radius->value); if (bomb->targetname) { bomb->use = use_tripbomb; } else { bomb->think = create_tripbomb_laser; bomb->nextthink = level.time + TBOMB_DELAY; } gi.linkentity(bomb); } //#endif // USE_ZAERO_ITEMS_WEAPONS /* ====================================================================== Sonic Cannon ====================================================================== */ #ifdef USE_ZAERO_WEAPONS void weapon_sc_fire (edict_t *ent) { int maxfiretime = sk_soniccannon_maxfiretime->value; if (!(ent->client->buttons & BUTTON_ATTACK)) { ent->client->ps.gunframe++; if(ent->client->weapon_sound && ent->client->ps.gunframe < 18) { ent->client->ps.gunframe = 18; } } else { if(EMPNukeCheck(ent, ent->s.origin)) { gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0); ent->client->ps.gunframe = 18; ent->client->weapon_sound = 0; ent->weaponsound_time = 0; ent->dmg_radius = 0; ent->client->startFireTime = 0; return; } if(!ent->client->startFireTime) { ent->client->startFireTime = level.time; } else if(level.time - ent->client->startFireTime >= maxfiretime) { ent->client->ps.gunframe = 17; } else { int old_cells = (int)ent->dmg_radius; ent->dmg_radius = ((level.time - ent->client->startFireTime) / maxfiretime) * sk_soniccannon_maxcells->value; if(old_cells < (int)ent->dmg_radius) { old_cells = (int)ent->dmg_radius - old_cells; if(ent->client->pers.inventory[ent->client->ammo_index] < old_cells) { ent->dmg_radius -= (old_cells - ent->client->pers.inventory[ent->client->ammo_index]); ent->client->pers.inventory[ent->client->ammo_index] = 0; } else { ent->client->pers.inventory[ent->client->ammo_index] -= old_cells; } } } if(!ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = 17; if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange (ent); } else { if(ent->weaponsound_time < level.time) { ent->client->weapon_sound = gi.soundindex("weapons/sonic/sc_fire.wav"); } } fire_sconnanEffects (ent); ent->client->ps.gunframe++; if (ent->client->ps.gunframe == 18 && (level.time - ent->client->startFireTime) < maxfiretime && ent->client->pers.inventory[ent->client->ammo_index]) ent->client->ps.gunframe = 12; } if (ent->client->ps.gunframe == 18) { ent->client->weapon_sound = 0; ent->weaponsound_time = 0; if(EMPNukeCheck(ent, ent->s.origin)) { gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0); } else { if (!is_silenced) gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 0.4, ATTN_NORM, 0); if(ent->dmg_radius) { fire_sconnan (ent); } } ent->dmg_radius = 0; ent->client->startFireTime = 0; } } void Weapon_SonicCannon (edict_t *ent) { static int pause_frames[] = {32, 42, 52, 0}; static int fire_frames[] = {12, 13, 14, 15, 16, 17, 0}; if (ent->client->ps.gunframe == 0) { #ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows... if (deathmatch->value) #endif { if (!is_silenced) gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 0.4, ATTN_NORM, 0); } ent->weaponsound_time = 0; ent->client->startFireTime = 0; ent->dmg_radius = 0; } else if (ent->client->ps.gunframe == 53) { #ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows... if (deathmatch->value) #endif { if (!is_silenced) gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 0.4, ATTN_NORM, 0); } } else if((ent->client->buttons & BUTTON_ATTACK) && ent->weaponsound_time == 0) { ent->weaponsound_time = level.time + 0.4; if (!is_silenced) gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 0.4, ATTN_NORM, 0); } Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire); if (is_quadfire) Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire); } void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage); void fire_sconnanEffects (edict_t *self) { vec3_t start, end; vec3_t forward, right; vec3_t offset, v; trace_t tr; AngleVectors (self->client->v_angle, forward, right, NULL); VectorScale (forward, -3, self->client->kick_origin); self->client->kick_angles[0] = -3; VectorSet(offset, 0, 7, self->viewheight-8); P_ProjectSource (self->client, self->s.origin, offset, forward, right, start); VectorMA (start, WORLD_SIZE, forward, end); // was 8192 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); VectorMA (tr.endpos, -5, forward, end); VectorSet(v, crandom() * 10 - 20, crandom() * 10 - 20, crandom() * 10 - 20); SpawnDamage(TE_SHIELD_SPARKS, end, v, 0); } void scexplode_think (edict_t *self) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_ROCKET_EXPLOSION); gi.WritePosition (self->s.origin); gi.multicast (self->s.origin, MULTICAST_PHS); G_FreeEdict (self); } void fire_sconnan (edict_t *self) { vec3_t start, end, explodepos; vec3_t forward, right, up; vec3_t offset; trace_t tr; float damage, radius; damage = self->dmg_radius / sk_soniccannon_maxcells->value; radius = damage * sk_soniccannon_radius->value; damage = sk_soniccannon_damage->value + (damage * sk_soniccannon_damage2->value); AngleVectors (self->client->v_angle, forward, right, up); VectorScale (forward, -3, self->client->kick_origin); self->client->kick_angles[0] = -3; VectorSet(offset, 0, 7, self->viewheight-8); P_ProjectSource (self->client, self->s.origin, offset, forward, right, start); VectorMA (start, WORLD_SIZE, forward, end); // was 8192 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); if ((tr.ent != self) && (tr.ent->takedamage)) { T_Damage (tr.ent, self, self, forward, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_SONICCANNON); } T_RadiusDamagePosition (tr.endpos, self, self, damage, tr.ent, radius, MOD_SONICCANNON); VectorMA (tr.endpos, -5, forward, end); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_ROCKET_EXPLOSION); gi.WritePosition (end); gi.multicast (self->s.origin, MULTICAST_PHS); damage -= 100; radius = 0.1; while (damage > 0) { edict_t *explode; VectorMA (end, (50 * crandom()) - 5, forward, explodepos); VectorMA (explodepos, (50 * crandom()) - 5, right, explodepos); VectorMA (explodepos, (50 * crandom()) - 5, up, explodepos); explode = G_Spawn(); VectorCopy (explodepos, explode->s.origin); explode->classname = "sconnanExplode"; explode->nextthink = level.time + radius; explode->think = scexplode_think; radius += 0.1; damage -= 100; } // play quad damage sound // playQuadSound (self); } #endif // USE_ZAERO_WEAPONS /* ====================================================================== Flares ====================================================================== */ #define FLASH_RANGE 256.0 void FoundTarget (edict_t *self); void flare_flash(edict_t *ent) { edict_t *target = NULL; // flash while (1) { float dist; float ratio; float dot; vec3_t delta; vec3_t forward; // get the next entity near us target = findradius (target, ent->s.origin, FLASH_RANGE); if (target == NULL) break; if (!target->client && !(target->svflags & SVF_MONSTER)) continue; if (target->deadflag) continue; if (!visible(ent, target)) continue; // if (!infront(target, ent)) // continue; // what's the distance, so that closer get's more VectorSubtract (ent->s.origin, target->s.origin, delta); dist = VectorLength (delta); ratio = 1 - (dist/FLASH_RANGE); if (ratio < 0) ratio = 0; // looking to the side get's less AngleVectors (target->s.angles, forward, NULL, NULL); VectorNormalize (delta); dot = Z_MAX(0.0, DotProduct(delta, forward)); ratio *= dot;// * 1.25; // set the flash counter if (target->client) { target->client->flashTime += ratio*25; if (target->client->flashTime > 25) target->client->flashTime = 25; target->client->flashBase = 30; if (deathmatch->value && // !target->client->pers.gl_polyblend && !(((int)zdmflags->value) & ZDM_NO_GL_POLYBLEND_DAMAGE)) T_Damage(target, ent, ent->owner, vec3_origin, target->s.origin, vec3_origin, (int)(10.0*ratio), 0, 0, MOD_GL_POLYBLEND); } else if ((target->svflags & SVF_MONSTER) && strcmp(target->classname, "monster_zboss") != 0) { target->monsterinfo.flashTime = Z_MAX(target->monsterinfo.flashTime, ratio*150); // a little bit more advantageous target->monsterinfo.flashBase = 50; if (target->enemy == NULL) { target->enemy = ent->owner; FoundTarget(target); } } } } void flare_think(edict_t *self) { edict_t *target = NULL; edict_t *closestEnt = NULL; float closestDist = 0.0; // on our last leg? if (level.time > self->timeout) { self->s.effects &= ~EF_ROCKET; self->think = G_FreeEdict; self->nextthink = level.time + 4.0; self->s.frame = 0; self->s.sound = 0; return; } self->s.frame++; if (self->s.frame > 14) self->s.frame = 5; // hissing sound self->s.sound = gi.soundindex ("weapons/flare/flarehis.wav"); // do the visual thing flare_flash(self); // next frame self->nextthink = level.time + FRAMETIME; } void fire_flare (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *flare; vec3_t adir; vec3_t up; vectoangles (dir, adir); AngleVectors (adir, NULL, NULL, up); flare = G_Spawn(); VectorCopy (start, flare->s.origin); VectorCopy (dir, flare->movedir); vectoangles (dir, flare->s.angles); VectorScale (dir, speed, flare->velocity); VectorMA (flare->velocity, 200 + crandom() * 10.0, up, flare->velocity); flare->movetype = MOVETYPE_BOUNCE; flare->clipmask = MASK_SHOT; flare->solid = SOLID_BBOX; flare->s.effects = EF_ROCKET; VectorSet(flare->mins, -4, -4, -4); VectorSet(flare->maxs, 4, 4, 4); flare->s.modelindex = gi.modelindex("models/objects/flare/tris.md2"); flare->owner = self; // flare->timeout = level.time + sk_flare_life->value; flare->timeout = level.time + 8000/speed; flare->nextthink = level.time + 1.0; flare->think = flare_think; flare->dmg = damage; flare->radius_dmg = radius_damage; flare->dmg_radius = damage_radius; flare->classname = "flare"; if (self->client) check_dodge (self, flare->s.origin, dir, speed); gi.linkentity (flare); } //#ifdef USE_ZAERO_ITEMS_WEAPONS void Weapon_FlareLauncher_Fire (edict_t *ent) { vec3_t offset, start; vec3_t forward, right; AngleVectors (ent->client->v_angle, forward, right, NULL); VectorSet(offset, 8, 8, ent->viewheight-8); P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); fire_flare(ent, start, forward, sk_flaregun_damage->value, sk_flaregun_speed->value, sk_flaregun_radius->value, sk_flaregun_rdamage->value); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); // play quad sound // playQuadSound (ent); if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) ent->client->pers.inventory[ent->client->ammo_index]--; // play shooting sound if (!is_silenced) gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 0.4, ATTN_NORM, 0); } void Weapon_FlareGun (edict_t *ent) { static int pause_frames[] = {15, 25, 35, 0}; static int fire_frames[] = {8, 0}; Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire); if (is_quadfire) Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire); } //#endif // USE_ZAERO_ITEMS_WEAPONS /* ====================================================================== Sniper Rifle ====================================================================== */ #ifdef USE_ZAERO_WEAPONS void fire_sniper_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) { trace_t tr; vec3_t end, s; edict_t *ignore = self; int i=0; VectorMA (start, WORLD_SIZE, aimdir, end); // was 8192 VectorCopy(start, s); while (i < 256) // Knightmare- prevent infinite loop, was 1 { tr = gi.trace (s, NULL, NULL, end, ignore, MASK_SHOT_NO_WINDOW); if (tr.fraction >= 1.0) return; // if we hit a plasmashield, then pass thru it if (Q_stricmp(tr.ent->classname, "PlasmaShield") == 0) { ignore = tr.ent; VectorCopy(tr.endpos, s); } else break; i++; } gi.WriteByte (svc_temp_entity); if (gi.pointcontents(tr.endpos) & MASK_WATER) { if (tr.plane.normal[2] > 0.7) gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); else gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); } else { if (tr.plane.normal[2] > 0.7) gi.WriteByte (TE_GRENADE_EXPLOSION); else gi.WriteByte (TE_ROCKET_EXPLOSION); } gi.WritePosition (tr.endpos); gi.multicast (tr.endpos, MULTICAST_PHS); if (tr.ent->takedamage) T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_NO_ARMOR, MOD_SNIPERRIFLE); } void weapon_sniperrifle_fire (edict_t *ent) { vec3_t forward, right; vec3_t offset, start; int damage; int kick; if (deathmatch->value) { // normal damage is too extreme in dm damage = sk_sniperrifle_damage_dm->value; // 150 kick = sk_sniperrifle_kick_dm->value; // 300 } else { damage = sk_sniperrifle_damage->value; // 250 kick = sk_sniperrifle_kick->value; // 400 } if (is_quad) { damage *= 4; kick *= 4; } if (is_double) { damage *= 2; kick *= 2; } AngleVectors (ent->client->v_angle, forward, right, NULL); // centre the shot VectorSet(offset, 0, 0, ent->viewheight); VectorAdd(ent->s.origin, offset, start); fire_sniper_bullet(ent, start, forward, damage, kick); if (!is_silenced) gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 1, ATTN_NORM, 0); else gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 0.4, ATTN_NORM, 0); PlayerNoise(ent, start, PNOISE_WEAPON); // play quad sound // playQuadSound (ent); VectorScale (forward, -20, ent->client->kick_origin); ent->client->kick_angles[0] = -2; ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity; } //#define SNIPER_CHARGE_TIME 30 void Weapon_SniperRifle(edict_t *ent) { /* Activate/Deactivate 0 - 8 : Activate 9 - 18 : Fire 19 - 27 : Idle 1 28 - 36 : Idle 2 37 - 41 : Deactivate Zoom 0 - 1 Zoom Hold 1 while zoomed */ const static int activateStart = 0; const static int activateEnd = 8; const static int deactivateStart = 37; const static int deactivateEnd = 41; const static int spFov = 15; const static int dmFov = 30; if (ent->client->weaponstate == WEAPON_DROPPING) { ent->client->sniperFramenum = 0; if (ent->client->ps.gunframe == deactivateStart) { // back to old fov ent->client->ps.fov = 90; #ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows... if (deathmatch->value) #endif gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/snip_bye.wav"), 1, ATTN_NORM, 0); } else if (ent->client->ps.gunframe == deactivateEnd) { ChangeWeapon(ent); return; } ent->client->ps.gunframe++; return; } if (ent->client->weaponstate == WEAPON_ACTIVATING) { if (ent->client->ps.gunframe == activateStart) { // play the activation sound #ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows... if (deathmatch->value) #endif gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/snip_act.wav"), 1, ATTN_NORM, 0); } else if (ent->client->ps.gunframe == activateEnd) { ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunindex = (deathmatch->value ? gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") : gi.modelindex("models/weapons/v_sniper/scope/tris.md2") ); ent->client->ps.gunframe = 0; ent->client->ps.fov = (deathmatch->value ? dmFov : spFov); ent->client->sniperFramenum = level.framenum + (sk_sniper_charge_time->value * 10); // SNIPER_CHARGE_TIME return; } ent->client->ps.gunframe++; return; } if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) { // back to other gun model ent->client->ps.gunindex = gi.modelindex("models/weapons/v_sniper/tris.md2"); ent->client->weaponstate = WEAPON_DROPPING; ent->client->ps.gunframe = deactivateStart; return; } if (ent->client->weaponstate == WEAPON_READY) { ent->client->ps.gunindex = (deathmatch->value ? gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") : gi.modelindex("models/weapons/v_sniper/scope/tris.md2") ); ent->client->ps.fov = (deathmatch->value ? dmFov : spFov); // beep if the sniper frame num is a multiple of 10 if (ent->client->sniperFramenum >= level.framenum) { if ((ent->client->sniperFramenum - level.framenum) % 10 == 1) gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/beep.wav"), 1, ATTN_NORM, 0); } if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) ) { if (level.framenum >= ent->client->sniperFramenum) { ent->client->latched_buttons &= ~BUTTON_ATTACK; if ((!ent->client->ammo_index) || ( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity)) { ent->client->weaponstate = WEAPON_FIRING; // start the animation ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1-1; ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1-1; ent->client->anim_end = FRAME_attack8; } } else { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange (ent); } } } } if (ent->client->weaponstate == WEAPON_FIRING) { ent->client->ps.gunindex = (deathmatch->value ? gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") : gi.modelindex("models/weapons/v_sniper/scope/tris.md2") ); ent->client->ps.fov = (deathmatch->value ? dmFov : spFov); //if (ent->client->quad_framenum > level.framenum) // gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); // fire weapon_sniperrifle_fire(ent); // start recharge ent->client->weaponstate = WEAPON_READY; ent->client->sniperFramenum = level.framenum + (sk_sniper_charge_time->value * 10); // SNIPER_CHARGE_TIME; } } #endif // USE_ZAERO_WEAPONS /* ====================================================================== Armageddon 2000 ====================================================================== */ #ifdef USE_ZAERO_WEAPONS void weapon_a2k_exp_think(edict_t *self) { self->s.frame++; self->s.skinnum++; if (self->s.frame == 6) { G_FreeEdict(self); return; } self->nextthink = level.time + FRAMETIME; } void Z_RadiusDamageVisible(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) { float points; edict_t *ent = NULL; vec3_t v; vec3_t dir; while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) { if (ent == ignore) continue; if (!ent->takedamage) continue; if (!visible(inflictor, ent)) continue; VectorAdd (ent->mins, ent->maxs, v); VectorMA (ent->s.origin, 0.5, v, v); VectorSubtract (inflictor->s.origin, v, v); points = damage - 0.5 * VectorLength (v); if (ent == attacker) points = points * 0.5; if (points > 0) { if (CanDamage (ent, inflictor)) { VectorSubtract (ent->s.origin, inflictor->s.origin, dir); T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } } } } //#define A2K_FRAMENUM 50 void weapon_a2k_fire (edict_t *ent) { if (ent->client->ps.gunframe == 14) { ent->client->a2kFramenum = level.framenum + (sk_a2k_detonate_time->value * 10); // A2K_FRAMENUM; ent->client->pers.inventory[ent->client->ammo_index]--; ent->client->ps.gunframe++; // start scream sound // ent->client->weapon_sound = gi.soundindex("weapons/a2k/countdn.wav"); gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/a2k/countdn.wav"), 1, ATTN_NORM, 0); // gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/a2k/ak_trig.wav"), 1, ATTN_NORM, 0); // play quad sound // playQuadSound (ent); } else if (ent->client->a2kFramenum == level.framenum) { // boom edict_t *exp = NULL; edict_t *e = NULL; float damage = sk_a2k_damage->value; // was 2500 float dmg_radius = sk_a2k_radius->value; // was 512 // play quad sound // playQuadSound (ent); if (is_quad) { damage *= 4; dmg_radius *= 4; } if (is_double) { damage *= 4; dmg_radius *= 4; } // do some damage T_RadiusDamage(ent, ent, damage, NULL, dmg_radius, MOD_A2K); // ok, now, do who ever's visible within 1024 units Z_RadiusDamageVisible(ent, ent, damage, NULL, dmg_radius * 2, MOD_A2K); exp = G_Spawn(); exp->classname = "A2K Explosion"; exp->solid = SOLID_NOT; exp->movetype = MOVETYPE_NONE; VectorClear(exp->mins); VectorClear(exp->maxs); VectorCopy(ent->s.origin, exp->s.origin); exp->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2"); exp->s.frame = 0; exp->s.skinnum = 6; exp->think = weapon_a2k_exp_think; exp->nextthink = level.time + FRAMETIME; gi.linkentity(exp); gi.positioned_sound(exp->s.origin, exp, CHAN_AUTO, gi.soundindex("weapons/a2k/ak_exp01.wav"), 1, ATTN_NORM, 0); ent->client->ps.gunframe++; ent->client->weapon_sound = 0; return; } else if (ent->client->ps.gunframe == 19) { // don't increase the gunframe return; } } void Weapon_A2k (edict_t *ent) { /* 00-09 Active 10-19 Boom (14 actual fire frame) 20-29 Idle1 30-39 Idle2 40-49 Idle3 50-55 Away */ static int pause_frames[] = {20, 30, 40, 0}; static int fire_frames[] = {14, 19, 0}; Weapon_Generic (ent, 9, 19, 49, 55, pause_frames, fire_frames, weapon_a2k_fire); } #endif // USE_ZAERO_WEAPONS /* ====================================================================== Push 0 - Start push 4 - Contact 8 - End Push ====================================================================== */ #if 0 qboolean push_hit (edict_t *self, vec3_t start, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t end; vec3_t v; edict_t *e = NULL; //see if enemy is in range VectorMA(start, 64, aim, end); tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT); if (tr.fraction >= 1) return false; // play sound gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/push/contact.wav"), 1, ATTN_NORM, 0); if (tr.ent->svflags & SVF_MONSTER || tr.ent->client) { // do our special form of knockback here VectorMA (tr.ent->absmin, 0.75, tr.ent->size, v); VectorSubtract (v, start, v); VectorNormalize (v); VectorMA (tr.ent->velocity, kick, v, tr.ent->velocity); //tr.ent->velocity[2] = 48; if (tr.ent->velocity[2] > 0) tr.ent->groundentity = NULL; } else if (tr.ent->movetype == MOVETYPE_FALLFLOAT) { if (tr.ent->touch) { float mass = tr.ent->mass; tr.ent->mass *= 0.25; tr.ent->touch(tr.ent, self, NULL, NULL); tr.ent->mass = mass; } } // ok, we hit something, damage it if (!tr.ent->takedamage) return false; // do the damage T_Damage (tr.ent, self, self, aim, tr.endpos, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); return true; } void Action_Push (edict_t *ent) { if (ent->client->ps.gunframe == 0) { // play grunt sound //gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/push/grunt.wav"), 1, ATTN_NORM, 0); ent->client->ps.gunframe++; } else if (ent->client->ps.gunframe == 4) { vec3_t forward; vec3_t offset; vec3_t start; // contact AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorSet(offset, 0, 0, ent->viewheight * 0.5); VectorAdd(ent->s.origin, offset, start); push_hit(ent, start, forward, 2, 512); ent->client->ps.gunframe++; } else if (ent->client->ps.gunframe == 8) { // go back to old weapon ent->client->newweapon = ent->client->pers.lastweapon; ChangeWeapon(ent); } else ent->client->ps.gunframe++; } #endif