// // spl_bluering.c // // Heretic II // Copyright 1998 Raven Software // #include "g_local.h" #include "fx.h" #include "random.h" #include "vector.h" #include "g_playstats.h" #include "p_actions.h" #define RING_THINKS 4 // This is a .4 seconds extern void AlertMonsters (edict_t *self, edict_t *enemy, float lifetime, qboolean ignore_shadows); extern edict_t *PhoenixMissileReflect(edict_t *missile, edict_t *other, vec3_t vel); extern edict_t *RedRainMissileReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *MeteorBarrierReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *SphereReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *HellboltReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *MorphReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *MagicMissileReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *FlyingFistReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *AssassinArrowReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *GkrokonSpooReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *ImpFireballReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *MssithraAlphaArrowReflect(edict_t *self, edict_t *other, vec3_t vel); extern edict_t *SpearProjReflect(edict_t *self, edict_t *other, vec3_t vel); // Since findradius is not specific enough for our needs // This, for one, will seek out player maceballs, arrows, and meteors. edict_t *findringradius (edict_t *from, vec3_t org, float rad, edict_t *ringent) { static float max2; static vec3_t min; static vec3_t max; vec3_t eorg; int j; float elen; if (!from) { max2=rad*rad; VectorCopy(org,min); VectorCopy(org,max); for (j=0 ; j<3 ; j++) { min[j]-=rad; max[j]+=rad; } } while (1) { from=findinbounds(from,min,max); if (!from) return 0; if ((from->reflected_time > level.time) || (from == ringent->owner)) continue; if ((fabs(from->s.origin[2] - ringent->s.origin[2])) > 50.0) // This is a RING, not a sphere. Cap the vert at 40]. continue; // don't let these affect coop friends if (from->client && coop->value && !((int)dmflags->value & DF_HURT_FRIENDS)) continue; // don't target team members in team deathmatching, if they are on the same team, and friendly fire is not enabled. if ((from->client && (int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)) && !((int)dmflags->value & DF_HURT_FRIENDS) && deathmatch->value) { if (OnSameTeam(from, ringent->owner)) continue; } for (j=0 ; j<3 ; j++) eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5); elen = DotProduct(eorg,eorg); if (elen > max2) continue; // if we've already reflected this, don't do it again. // We DO have to wait for after the radius check, however, because the shot might get closer over the next half second. from->reflected_time = level.time + 0.6; return from; } } void RingThink(edict_t *self) { int hit; edict_t *ent = NULL, *newent; vec3_t vel, hitloc; vec_t scale; edict_t* (*reflect)(edict_t*, edict_t*, vec3_t); // kill the ring eventually self->nextthink = level.time + 0.1; if (self->count <= 0) { G_SetToFree(self); return; } self->count--; // Since find radius is not specific enough for our needs, here is while(ent = findringradius(ent, self->s.origin, RING_EFFECT_RADIUS*0.25*(RING_THINKS-self->count), self)) { hit = false; reflect = NULL; if (ent->mass) { VectorSubtract(ent->s.origin, self->s.origin, vel); scale = (RING_EFFECT_RADIUS - VectorLength(vel)) * (RING_KNOCKBACK_SCALE/RING_EFFECT_RADIUS) * sqrt(RING_MASS_FACTOR / ent->mass) + RING_KNOCKBACK_BASE; VectorNormalize(vel); if (ent->client) { // For players, force them up more and faster. vel[2] = 0.5; if (vel[2] < 0.5 && vel[2] > 0.0) { scale *= 2.0; VectorNormalize(vel); } } // Vel is just passing the direction of the knockback. QPostMessage(ent, MSG_REPULSE, PRI_DIRECTIVE, "fff", vel[0], vel[1], vel[2] + 30.0); if (ent->takedamage) { VectorMA(ent->s.origin, -ent->maxs[0], vel, hitloc); if (ent->movetype != PHYSICSTYPE_NONE) T_Damage (ent, ent, self, vel, hitloc, vec3_origin, 4, (int)scale, DAMAGE_RADIUS | DAMAGE_SPELL,MOD_ROR); else T_Damage (ent, ent, self, vel, hitloc, vec3_origin, 4, 0, DAMAGE_RADIUS | DAMAGE_SPELL,MOD_ROR); } } else if (strcmp(ent->classname, "Spell_RedRainArrow") == 0) { reflect = RedRainMissileReflect; } else if (strcmp(ent->classname, "Spell_PhoenixArrow") == 0) { reflect = PhoenixMissileReflect; } else if (strcmp(ent->classname, "Spell_MeteorBarrier") == 0) { reflect = MeteorBarrierReflect; } else if (strcmp(ent->classname, "Spell_SphereOfAnnihilation") == 0) { reflect = SphereReflect; } else if (strcmp(ent->classname, "Spell_Hellbolt") == 0) { reflect = HellboltReflect; } else if (strcmp(ent->classname, "Spell_MorphArrow") == 0) { reflect = MorphReflect; } else if (strcmp(ent->classname, "Spell_MagicMissile") == 0) { reflect = MagicMissileReflect; } else if (strcmp(ent->classname, "Spell_FlyingFist") == 0) { reflect = FlyingFistReflect; } else if (strcmp(ent->classname, "Assassin_Dagger") == 0) { reflect = AssassinArrowReflect; } else if (strcmp(ent->classname, "Gkrokon_Spoo") == 0) { reflect = GkrokonSpooReflect; } else if (strcmp(ent->classname, "imp fireball") == 0) { reflect = ImpFireballReflect; } else if (strcmp(ent->classname, "mssithra_Arrow") == 0) { reflect = MssithraAlphaArrowReflect; } else if (strcmp(ent->classname, "Spell_SpearProj") == 0) { reflect = SpearProjReflect; } else if (strcmp(ent->classname, "Spell_Maceball") == 0) { if (ent->owner != self->owner) { // Don't screw up your own projectiles. hit = true; // Give the self credit for stuff killed with it, or worse yet, set the originator as the enemy. ent->enemy = ent->owner; ent->owner = self->owner; // Do a nasty looking blast at the impact point gi.CreateEffect(&ent->s, FX_LIGHTNING_HIT, CEF_OWNERS_ORIGIN, NULL, "t", ent->velocity); } } if (reflect) { if (ent->owner != self && ent->reflect_debounce_time) { hit = true; } else { reflect = NULL; } } if (hit) { VectorSubtract(self->s.origin, ent->s.origin, vel); VectorNormalize(vel); // The dot product is the velocity towards the self (normally negative), let's reverse it. scale = DotProduct(vel, ent->velocity); if (scale > 0) // If heading towards the self, reverse that portion of the velocity VectorMA(ent->velocity, -2.0*scale, vel, vel); else // Jes' double the speed away VectorMA(ent->velocity, scale, vel, vel); if(reflect) { if (Vec3IsZero(vel)) // Reflect needs a non-zero vel. If zeroed, throw it straight up. VectorSet(vel, 0, 0, 200.0); newent = reflect(ent, self->owner, vel); vectoangles(newent->velocity, newent->s.angles); } } } } // Formula for knockback: 1 to 0 (center to outside) * KNOCKBACK_SCALE + KNOCKBACK_BASE // This total is multiplied by (MASS_FACTOR / mass). (If mass > 200, less, if < 200, more) void SpellCastBlueRing(edict_t *Caster, vec3_t StartPos, vec3_t AimAngles, vec3_t AimDir, float value) { edict_t *newent; // create the actual effect entity newent = G_Spawn(); newent->owner = Caster; newent->solid = SOLID_NOT; newent->svflags |= SVF_NOCLIENT; newent->movetype = PHYSICSTYPE_NONE; newent->classname = "Spell_Ring"; newent->nextthink = level.time + 0.1; newent->think = RingThink; newent->count = RING_THINKS; newent->timestamp = level.time; VectorCopy(Caster->s.origin, newent->s.origin); gi.linkentity(newent); // fire off a special effect. gi.CreateEffect(&Caster->s, FX_SPELL_BLUERING, CEF_OWNERS_ORIGIN, 0, ""); } // end