sin-2015/nuke.cpp
1999-04-22 00:00:00 +00:00

649 lines
14 KiB
C++

/*
================================================================
NUKE LAUNCHER aka IP36
================================================================
Copyright (C) 1998 by 2015, Inc.
All rights reserved.
This source is may not be distributed and/or modified without
expressly written permission by 2015, Inc.
*/
#include "g_local.h"
#include "explosion.h"
#include "nuke.h"
#include "worldspawn.h"
#include "misc.h"
#include "surface.h"
#include "jitter.h"
#define NUKE_SPEED 700
#define NUKE_RADIUS 800
//=================================================================
// nuke flash maker
CLASS_DECLARATION(Entity, NukeFlash, NULL);
Event EV_NukeFlash_Flash("nukeflash_flash");
ResponseDef NukeFlash::Responses[] =
{
{&EV_NukeFlash_Flash, (Response)NukeFlash::Flash},
{NULL, NULL}
};
EXPORT_FROM_DLL void NukeFlash::Flash(Event *ev)
{
Entity *ent;
ent = G_NextEntity(NULL);
while(ent)
{
// only apply view flash to players
if(ent->isSubclassOf(Player))
{
if(CanDamage(ent))
{
((Player *)ent)->StartNukeFlash(fblend, falpha);
}
}
ent = G_NextEntity(ent);
}
// do fade in
if(falpha > 1)
{
//fade in the flash
falpha += 0.4;
// switch to fade out when we reach full opacity
if(falpha >= 2)
falpha = 1;
}
else // do fade out
{
// decay color to red
fblend[1] -= 0.02;
fblend[2] = fblend[1];
// decay alpha
if(falpha > 0.6)
falpha -= 0.1;
else
{
falpha -= 0.035;
if(falpha < 0)
falpha = 0;
}
}
if(falpha > 0)
PostEvent(EV_NukeFlash_Flash, 0.1);
else
ProcessEvent(EV_Remove);
}
void NukeFlash::Setup(Vector pos, float delay)
{
setMoveType(MOVETYPE_NONE);
setSolidType(SOLID_NOT);
setOrigin(pos);
worldorigin.copyTo(edict->s.old_origin);
fblend[0] = fblend[1] = fblend[2] = 1;
falpha = 1.01;
if(delay)
PostEvent(EV_NukeFlash_Flash, delay);
else
ProcessEvent(EV_NukeFlash_Flash);
}
//=================================================================
// nuke fireball
CLASS_DECLARATION(Projectile, NukeFireball, NULL);
Event EV_NukeFireball_Animate("fireball_animate");
Event EV_NukeFireball_Damage("fireball_damage");
Event EV_NukeFireball_Fade("fireball_fade");
ResponseDef NukeFireball::Responses[] =
{
{&EV_NukeFireball_Animate, (Response)NukeFireball::Animate},
{&EV_NukeFireball_Damage, (Response)NukeFireball::DoDamage},
{&EV_NukeFireball_Fade, (Response)NukeFireball::Fade},
{NULL, NULL}
};
EXPORT_FROM_DLL void NukeFireball::DoDamage(Event *ev)
{
Entity *ownerent;
float points, dist;
Entity *ent;
Vector org;
Vector v;
float rad, minrad;
int damage;
if(deathmatch->value)
damage = 80;
else
damage = 120;
rad = edict->s.scale*64 + 128;
minrad = rad - 256;
ownerent = G_GetEntity(owner);
ent = findradius(NULL, origin.vec3(), rad);
while(ent)
{
if(ent->takedamage)
{
org = ent->origin + (ent->mins + ent->maxs)*0.5;
v = origin - org;
dist = v.length();
if(dist > minrad)
{
points = dist*(float)0.04;
if(points < 0)
{
points = 0;
}
points = damage - points;
if(ent == ownerent)
{
points *= 0.75;
}
if(points > 0)
{
if(CanDamage(ent))
{
points += 0.5;
ent->Damage(this, ownerent, (int)points, ent->origin, v*(-1), vec_zero, points*0.1, DAMAGE_RADIUS, MOD_NUKEEXPLOSION, -1, -1, 1.0f );
}
}
}
}
ent = findradius(ent, origin.vec3(), rad);
}
// continue doing damage every 0.1 seconds
PostEvent(EV_NukeFireball_Damage, 0.1);
}
EXPORT_FROM_DLL void NukeFireball::Fade(Event *ev)
{
PostEvent(EV_NukeFireball_Fade, 0.1);
edict->s.renderfx |= RF_TRANSLUCENT;
translucence += 0.15;
if(translucence >= 0.98)
{
ProcessEvent(EV_Remove);
return;
}
setAlpha(1.0 - translucence);
}
EXPORT_FROM_DLL void NukeFireball::Animate(Event *ev)
{
edict->s.scale += 0.4;
PostEvent(EV_NukeFireball_Animate, 0.1);
}
void NukeFireball::Setup(Entity *owner, Vector pos, float life, int type)
{
this->owner = owner->entnum;
edict->owner = owner->edict;
setMoveType(MOVETYPE_FLY);
setSolidType(SOLID_NOT);
edict->s.frame = 0;
angles[PITCH] = 0;
angles[YAW] = random()*360;
angles[ROLL] = 0;
setAngles(angles);
setModel("sprites/nukering.spr");
edict->s.scale = 0.1;
// setup it's transparency
edict->s.renderfx |= RF_TRANSLUCENT;
translucence = 0;
setAlpha(1.0f - translucence);
// the outward most fireball does the expanding damage
PostEvent(EV_NukeFireball_Damage, 0.1);
PostEvent(EV_NukeFireball_Animate, 0.2);
setOrigin(pos);
worldorigin.copyTo(edict->s.old_origin);
PostEvent(EV_NukeFireball_Fade, life);
}
//=================================================================
// nuke energyball
CLASS_DECLARATION( Projectile, NukeBall, NULL );
Event EV_NukeBall_Animate("nuke_animate");
Event EV_NukeBall_Collapse("nuke_collapse");
Event EV_NukeBall_Explode("nuke_explode");
Event EV_NukeBall_ThrowRing("nuke_throwring");
Event EV_NukeBall_ExplosionBall("nuke_explosionball");
ResponseDef NukeBall::Responses[] =
{
{&EV_Touch, (Response)NukeBall::Collapse},
{&EV_NukeBall_Animate, (Response)NukeBall::Animate},
{&EV_NukeBall_Collapse, (Response)NukeBall::Collapse},
{&EV_NukeBall_Explode, (Response)NukeBall::Explode},
{&EV_NukeBall_ThrowRing, (Response)NukeBall::ThrowRing},
{&EV_NukeBall_ExplosionBall, (Response)NukeBall::ExplosionBall},
{NULL, NULL}
};
EXPORT_FROM_DLL void NukeBall::Animate (Event *ev)
{
edict->s.frame++;
if(edict->s.frame >= 10)
edict->s.frame = 0;
PostEvent(EV_NukeBall_Animate, FRAMETIME);
}
EXPORT_FROM_DLL void NukeBall::Collapse (Event *ev)
{
Entity *other;
Entity *owner;
int damg;
other = ev->GetEntity(1);
owner = G_GetEntity(this->owner);
if(deathmatch->value)
damg = 250;
else
damg = 300;
// touched something that triggered exploding
if(ballstate < NBS_COLLAPSING)
{
NukeFlash *flash;
assert(other);
if(other->isSubclassOf(Teleporter))
return;
if(other->entnum == this->owner)
return;
// whak touched entity
if (other->takedamage)
other->Damage(this, owner, damg, origin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_NUKE, -1, -1, 1.0f );
surfaceManager.DamageSurface (&level.impact_trace, damg, owner );
// make it collapse
CancelEventsOfType(EV_NukeBall_Animate);
setMoveType(MOVETYPE_FLY);
setSolidType(SOLID_NOT);
velocity.normalize();
setOrigin(origin - velocity*24);
velocity = vec_zero;
edict->s.scale = 0.2;
TempModel( NULL, origin, "0 0 0", "sprites/implosion.spr", 0, 1, 1.0f, TEMPMODEL_ANIMATE_ONCE|TEMPMODEL_ANIMATE_FAST, 2);
// post explosion event for end of collapsing
PostEvent(EV_NukeBall_Explode, 0.4);
//create flash entity
flash = new NukeFlash;
flash->Setup(origin, 0.2);
// check for evilness...
if(ballstate == NBS_EVIL)
{
// evilness, here he come...
int evilcount;
Vector evildirection;
NukeBall *ball;
Vector pos, dir;
Vector right, up;
Vector tmpvec;
for(evilcount = 0; evilcount < 6; evilcount++)
{
switch(evilcount)
{
case 0:
evildirection = Vector(1, 0, 0);
break;
case 1:
evildirection = Vector(-1, 0, 0);
break;
case 2:
evildirection = Vector(0, 1, 0);
break;
case 3:
evildirection = Vector(0, -1, 0);
break;
case 4:
evildirection = Vector(0, 0, 1);
break;
case 5:
default:
evildirection = Vector(0, 0, -1);
break;
}
tmpvec = worldorigin + evildirection*24;
ball = new NukeBall;
ball->Setup(owner, tmpvec, evildirection);
ball->takedamage = DAMAGE_NO;
}
}
//setup imploding stuff
ballstate = NBS_COLLAPSING;
}
// always check if something was touched
else if(other)
{
if (other->takedamage)
other->Damage(this, owner, damg, origin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_NUKE, -1, -1, 1.0f );
surfaceManager.DamageSurface (&level.impact_trace, damg, owner);
}
// collapse & animate the model
if(edict->s.scale > 0.2)
{
edict->s.scale -= 0.2;
if(edict->s.scale <= 0.2)
{
edict->s.scale = 0.2;
edict->s.frame = 0;
stopsound(CHAN_VOICE);
hideModel();
}
else
{
edict->s.frame++;
if(edict->s.frame >= 10)
edict->s.frame = 0;
}
}
}
EXPORT_FROM_DLL void NukeBall::Explode (Event *ev)
{
NukeFireball *fball;
Event *event;
RadiusJitter *jitter;
Entity *owner;
CancelEventsOfType(EV_NukeBall_Animate);
CancelEventsOfType(EV_NukeBall_Collapse);
takedamage = DAMAGE_NO;
owner = G_GetEntity(this->owner);
// small amount of damage to everyone visible from initial flash
RadiusDamage(this, owner, 20, NULL, MOD_NUKEEXPLOSION, 0.002);
// alota damage to anything near the initial blast
if(deathmatch->value)
RadiusDamage(this, owner, 250, NULL, MOD_NUKEEXPLOSION, 0.4);
else
RadiusDamage(this, owner, 300, NULL, MOD_NUKEEXPLOSION, 0.45);
// post the particle explosion events
event = new Event(EV_NukeBall_ExplosionBall);
event->AddInteger(64);
ProcessEvent(event);
event = new Event(EV_NukeBall_ExplosionBall);
event->AddInteger(128);
PostEvent(event, 0.6);
event = new Event(EV_NukeBall_ExplosionBall);
event->AddInteger(240);
PostEvent(event, 1.1);
fball = new NukeFireball;
fball->Setup(owner, origin, 1.8, 1);
TempModel( NULL, origin, Vector(90,0,G_Random(360)), "sprites/nukering2.spr", 0, 10.0f, 1.0f, TEMPMODEL_ANIMATE_SCALE|TEMPMODEL_ALPHAFADE, 20.0f );
// nuke explosion sounds
RandomPositionedSound(origin, "impact_nukeexplosion", 1, CHAN_AUTO, 0.75);
// add in the ass whoopin' view jitter
jitter = new RadiusJitter;
jitter->Setup(origin, 1600, 0.03,
0.8, 15, 4,
1.5, 10, 5);
PostEvent(EV_Remove, 1.5);
//do a bit of dialog for the first time firing it in single player
if(owner->isClient() && !deathmatch->value)
{
ScriptVariable *var;
var = gameVars.GetVariable("firstnukefire");
if(!var) // hasn't been fired yet this game
{
var = gameVars.CreateVariable("firstnukefire", 1);
// call the dialog thread for it
ExecuteThread("global/nukeweapon.scr::nuke_first_fire", true);
}
}
}
EXPORT_FROM_DLL void NukeBall::ThrowRing(Event *ev)
{
TempModel( NULL, origin, Vector( 90, G_Random(360), 0), "sprites/nukering2.spr",
0, 8.0f, 1.0f, TEMPMODEL_ANIMATE_SCALE|TEMPMODEL_ALPHAFADE, 15.0f);
TempModel( NULL, origin, Vector( -90, G_Random(360), 0), "sprites/nukering2.spr",
0, 8.0f, 1.0f, TEMPMODEL_ANIMATE_SCALE|TEMPMODEL_ALPHAFADE, 15.0f);
}
EXPORT_FROM_DLL void NukeBall::ExplosionBall(Event *ev)
{
int size;
size = ev->GetInteger(1);
SpawnNukeExplosion(origin, size, 153);
}
EXPORT_FROM_DLL void NukeBall::Setup (Entity *owner, Vector pos, Vector dir)
{
Event *ev;
this->owner = owner->entnum;
edict->owner = owner->edict;
setMoveType(MOVETYPE_FLYMISSILE);
setSolidType(SOLID_BBOX);
edict->clipmask = MASK_PROJECTILE;
edict->s.frame = 0;
angles = dir.toAngles();
angles[PITCH] = -angles[PITCH];
setAngles(angles);
speed = NUKE_SPEED;
velocity = dir * NUKE_SPEED;
// set nuke duration
PostEvent(EV_NukeBall_Animate, FRAMETIME);
ev = new Event(EV_NukeBall_Collapse);
ev->AddEntity( world );
PostEvent(ev, 1.5);
ballstate = NBS_FLYING;
setModel("sprites/nukeball.spr");
edict->s.renderfx |= RF_DLIGHT;
edict->s.effects |= EF_NUKETRAIL;
gravity = 0;
edict->s.color_r = 0.8;
edict->s.color_g = 0.2;
edict->s.color_b = 0.3;
edict->s.radius = 350;
// setup ambient thrust
edict->s.sound = gi.soundindex("weapons/nuke/ballsound.wav");
edict->s.sound |= ATTN_IDLE<<14;
setSize("-8 -8 -8", "8 8 8");
setOrigin(pos);
worldorigin.copyTo(edict->s.old_origin);
}
// for devious purposes...
EXPORT_FROM_DLL void NukeBall::EvilSetup (Entity *owner, Vector pos, Vector dir)
{
Setup(owner, pos, dir);
ballstate = NBS_EVIL;
}
//=================================================================
// nuke launcher
CLASS_DECLARATION(Weapon, IP36, "weapon_ip36");
Event EV_Nuke_Launch("nuke_launch");
ResponseDef IP36::Responses[] =
{
{&EV_Weapon_Shoot, (Response)IP36::Shoot},
{&EV_Nuke_Launch, (Response)IP36::Launch},
{NULL, NULL}
};
IP36::IP36()
{
SetModels("nuke_w.def", "view_nuke.def");
modelIndex("sprites/nukeball.spr");
modelIndex("sprites/implosion.spr");
modelIndex("sprites/nukering.spr");
modelIndex("sprites/nukering2.spr");
SetAmmo("IlludiumModules", 1, 1);
SetRank(110, 0);
SetType(WEAPON_2HANDED_LO);
SetMinRange(NUKE_RADIUS);
SetProjectileSpeed(NUKE_SPEED);
// precache the first fire dialog if needed
if(!deathmatch->value)
{
ScriptVariable *var;
var = gameVars.GetVariable("firstnukefire");
// only bother to precache if hasn't been fired yet
if(!var)
{
G_LoadAndExecScript("global/nukeweapon.scr", "nuke_first_fire_precache");
}
}
}
void IP36::Shoot (Event *ev)
{
NukeBall *ball;
Vector pos, dir;
Vector right, up;
Vector tmpvec;
if((!owner) || (owner->IsOnBike()))
return;
GetMuzzlePosition(&pos, &dir, &right, &up);
tmpvec = pos - right*8;
ball = new NukeBall;
ball->Setup(owner, tmpvec, dir);
NextAttack(3);
}
void IP36::Launch (Event *ev)
{
NukeBall *ball;
Vector pos, dir;
Vector right, up;
Vector tmpvec;
if((!owner) || (owner->IsOnBike()))
return;
GetMuzzlePosition(&pos, &dir, &right, &up);
tmpvec = pos - right*8;
ball = new NukeBall;
ball->Setup(owner, tmpvec, dir);
}
//==================================================================
// And yae, from yonder, hither weapon doth seth fourth evil things...
class EXPORT_FROM_DLL EvilIP36 : public IP36
{
public:
CLASS_PROTOTYPE(EvilIP36);
EvilIP36();
virtual void Shoot(Event *ev);
};
CLASS_DECLARATION(IP36, EvilIP36, "weapon_evilip36");
ResponseDef EvilIP36::Responses[] =
{
{&EV_Weapon_Shoot, (Response)EvilIP36::Shoot},
{NULL, NULL}
};
EvilIP36::EvilIP36()
{
}
void EvilIP36::Shoot (Event *ev)
{
NukeBall *ball;
Vector pos, dir;
Vector right, up;
Vector tmpvec;
if((!owner) || (owner->IsOnBike()))
return;
GetMuzzlePosition(&pos, &dir, &right, &up);
tmpvec = pos - right*8;
ball = new NukeBall;
ball->EvilSetup(owner, tmpvec, dir);
ball->takedamage = DAMAGE_NO;
NextAttack(2);
}