1288 lines
30 KiB
C++
1288 lines
30 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/specialfx.cpp $
|
|
// $Revision:: 40 $
|
|
// $Author:: Markd $
|
|
// $Date:: 11/15/98 11:33p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/specialfx.cpp $
|
|
//
|
|
// 40 11/15/98 11:33p Markd
|
|
// Added FATPROJECTILE flag
|
|
//
|
|
// 39 11/14/98 11:15p Markd
|
|
// bullet proofed fire blowing up
|
|
//
|
|
// 38 11/08/98 11:02p Markd
|
|
// increased damage of firebarrel
|
|
//
|
|
// 37 11/07/98 10:15p Markd
|
|
// put in forcemusic support
|
|
//
|
|
// 36 10/24/98 12:42a Markd
|
|
// changed origins to worldorigins where appropriate
|
|
//
|
|
// 35 10/23/98 5:37a Jimdose
|
|
// Added SpawnBlastDamage
|
|
//
|
|
// 34 10/12/98 4:30p Aldie
|
|
// Added some more functionality to sprites
|
|
//
|
|
// 33 10/11/98 8:50p Jimdose
|
|
// Added sound to fire sprite
|
|
//
|
|
// 32 10/10/98 5:58p Aldie
|
|
// More quantumdestab fixes
|
|
//
|
|
// 31 10/09/98 7:38p Aldie
|
|
// Fix dir when doing trace to surfaces
|
|
//
|
|
// 30 10/08/98 7:25p Aldie
|
|
// Fixed a comment
|
|
//
|
|
// 29 10/08/98 4:28p Aldie
|
|
// Do a trace to surface to apply a sprite
|
|
//
|
|
// 28 10/07/98 2:32p Aldie
|
|
// Changed "model" to "sprite" for fx_sprite
|
|
//
|
|
// 27 9/29/98 4:25p Markd
|
|
// put in parallel fire support
|
|
//
|
|
// 26 9/29/98 4:15p Aldie
|
|
// Removed the sined section
|
|
//
|
|
// 25 9/25/98 3:00p Aldie
|
|
// Added lightstyle and type to tessellation
|
|
//
|
|
// 24 9/21/98 4:36p Aldie
|
|
// Added fx_sprite
|
|
//
|
|
// 23 9/20/98 9:09p Aldie
|
|
// Removed a no longer needed function
|
|
//
|
|
// 22 9/20/98 7:11p Aldie
|
|
// Added flags to particles
|
|
//
|
|
// 21 9/19/98 4:47p Markd
|
|
// fixed music stuff and added actionincrement to weapons
|
|
//
|
|
// 20 9/18/98 8:41p Aldie
|
|
// Added type to tesselate message
|
|
//
|
|
// 19 9/18/98 5:47p Aldie
|
|
// Remove the EV_Remove on func_beams
|
|
//
|
|
// 18 9/18/98 5:30p Aldie
|
|
// Removed func_beam for demo version
|
|
//
|
|
// 17 9/17/98 9:08p Aldie
|
|
// Don't use edict->s.parent for stuff :-)
|
|
//
|
|
// 16 9/16/98 4:06p Aldie
|
|
// Added a comment
|
|
//
|
|
// 15 9/16/98 4:03p Aldie
|
|
// Fixed targeting of beams
|
|
//
|
|
// 14 9/15/98 7:34p Markd
|
|
// Added ChangeMusic and ChangeSoundtrack
|
|
//
|
|
// 13 9/13/98 5:39p Aldie
|
|
// Changed a command
|
|
//
|
|
// 12 9/13/98 4:34p Aldie
|
|
// Added lots of beam stuff
|
|
//
|
|
// 11 9/12/98 11:10p Aldie
|
|
// Made func beam generic
|
|
//
|
|
// 10 9/11/98 11:36p Aldie
|
|
// Added activate and deactivate
|
|
//
|
|
// 9 9/11/98 5:45p Aldie
|
|
// Updated some flags for beam
|
|
//
|
|
// 8 9/10/98 8:38p Aldie
|
|
// Electrical beam effects
|
|
//
|
|
// 7 9/09/98 3:56p Aldie
|
|
// New lightning effect
|
|
//
|
|
// 6 9/01/98 6:17p Markd
|
|
// Added additonal sprite types to fire
|
|
//
|
|
// 5 9/01/98 3:05p Markd
|
|
// Rewrote explosion code
|
|
//
|
|
// 4 8/29/98 7:42p Markd
|
|
// Fixed flicker code for fx_fire
|
|
//
|
|
// 3 8/29/98 7:24p Markd
|
|
// put in light ability on fx_fire
|
|
//
|
|
// 2 8/29/98 5:27p Markd
|
|
// added specialfx, replaced misc with specialfx where appropriate
|
|
//
|
|
// 1 8/29/98 4:39p Markd
|
|
//
|
|
// DESCRIPTION:
|
|
// Special fx
|
|
//
|
|
|
|
#include "g_local.h"
|
|
#include "entity.h"
|
|
#include "trigger.h"
|
|
#include "explosion.h"
|
|
#include "misc.h"
|
|
#include "light.h"
|
|
#include "specialfx.h"
|
|
#include "player.h"
|
|
#include "g_utils.h"
|
|
#include "surface.h"
|
|
|
|
/*
|
|
================
|
|
SpawnBlastDamage
|
|
================
|
|
*/
|
|
void SpawnBlastDamage
|
|
(
|
|
trace_t *trace,
|
|
int damage,
|
|
Entity *attacker
|
|
)
|
|
|
|
{
|
|
Entity *blastmark;
|
|
Vector norm;
|
|
|
|
assert( trace );
|
|
assert( attacker );
|
|
if ( !trace )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !attacker )
|
|
{
|
|
attacker = world;
|
|
}
|
|
|
|
surfaceManager.DamageSurface( trace, damage, attacker );
|
|
if ( trace->ent && trace->ent->entity && trace->ent->entity->flags & FL_BLASTMARK )
|
|
{
|
|
blastmark = new Entity;
|
|
blastmark->setMoveType( MOVETYPE_NONE );
|
|
blastmark->setSolidType( SOLID_NOT );
|
|
blastmark->setModel( "sprites/blastmark.spr" );
|
|
blastmark->setSize( "0 0 0", "0 0 0" );
|
|
blastmark->edict->s.scale = G_Random() + 0.7;
|
|
|
|
norm = trace->plane.normal;
|
|
norm.x = -norm.x;
|
|
norm.y = -norm.y;
|
|
blastmark->angles = norm.toAngles();
|
|
blastmark->angles.z = G_Random( 360 );
|
|
blastmark->setAngles( blastmark->angles );
|
|
blastmark->setOrigin( Vector( trace->endpos ) + ( Vector( trace->plane.normal ) * 0.2 ) );
|
|
|
|
blastmark->PostEvent( EV_FadeOut, 5 );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Particles
|
|
================
|
|
*/
|
|
void Particles
|
|
(
|
|
Vector org,
|
|
Vector norm,
|
|
int count,
|
|
int lightstyle,
|
|
int flags
|
|
)
|
|
|
|
{
|
|
if (count>255) count = 255;
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_PARTICLES );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.WriteDir( norm.vec3() );
|
|
gi.WriteByte( lightstyle );
|
|
gi.WriteByte( count );
|
|
gi.WriteByte( flags );
|
|
gi.multicast( org.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SpawnBlood
|
|
================
|
|
*/
|
|
void SpawnBlood
|
|
(
|
|
Vector org,
|
|
Vector norm,
|
|
int damage
|
|
)
|
|
|
|
{
|
|
if (damage > 150) damage = 150;
|
|
Particles( org, norm, damage, 121, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SpawnSparks
|
|
================
|
|
*/
|
|
void SpawnSparks
|
|
(
|
|
Vector org,
|
|
Vector norm,
|
|
int count
|
|
)
|
|
|
|
{
|
|
Particles( org, norm, count, 122, PARTICLE_OVERBRIGHT );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SpawnPulseBeam
|
|
==============
|
|
*/
|
|
void SpawnBeam
|
|
(
|
|
Vector start,
|
|
Vector end,
|
|
int parent_entnum,
|
|
int modelindex,
|
|
float alpha,
|
|
float life,
|
|
int flags
|
|
)
|
|
|
|
{
|
|
byte sendflags;
|
|
sendflags = 0;
|
|
|
|
if ( alpha != 1.0f )
|
|
sendflags |= BM_ALPHA;
|
|
if ( life != 30 )
|
|
sendflags |= BM_LIFE;
|
|
if ( flags )
|
|
sendflags |= BM_FLAGS;
|
|
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_BEAM );
|
|
gi.WriteByte( sendflags );
|
|
gi.WritePosition( start.vec3() );
|
|
gi.WritePosition( end.vec3() );
|
|
gi.WriteShort( modelindex );
|
|
|
|
if ( sendflags & BM_ALPHA )
|
|
gi.WriteByte( alpha*255 );
|
|
if ( sendflags & BM_FLAGS )
|
|
gi.WriteByte( flags );
|
|
if ( sendflags & BM_LIFE )
|
|
gi.WriteShort( life );
|
|
|
|
gi.multicast( start.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SpawnRocketExplosion
|
|
================
|
|
*/
|
|
void SpawnRocketExplosion
|
|
(
|
|
Vector org
|
|
)
|
|
|
|
{
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_ROCKET_EXPLOSION );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.multicast( org.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SpawnScaledExplosion
|
|
================
|
|
*/
|
|
void SpawnScaledExplosion
|
|
(
|
|
Vector org,
|
|
float scale
|
|
)
|
|
|
|
{
|
|
scale *= 64;
|
|
if ( scale > 255 )
|
|
scale = 255;
|
|
else if ( scale < 0 )
|
|
return;
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_SCALED_EXPLOSION );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.WriteByte( scale );
|
|
gi.multicast( org.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SpawnTempDlight
|
|
================
|
|
*/
|
|
void SpawnTempDlight
|
|
(
|
|
Vector org,
|
|
float r,
|
|
float g,
|
|
float b,
|
|
float radius,
|
|
float decay,
|
|
float life
|
|
)
|
|
{
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_DLIGHT );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.WriteByte( r*255.0f );
|
|
gi.WriteByte( g*255.0f );
|
|
gi.WriteByte( b*255.0f );
|
|
gi.WriteByte( radius/4 );
|
|
gi.WriteByte( decay*255.0f );
|
|
gi.WriteByte( life*25.5f );
|
|
gi.multicast( org.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
SpawnTeleportEffect
|
|
===================
|
|
*/
|
|
void SpawnTeleportEffect
|
|
(
|
|
Vector org,
|
|
int lightstyle
|
|
)
|
|
|
|
{
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_TELEPORT_EFFECT );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.WriteByte( lightstyle );
|
|
gi.multicast( org.vec3(), MULTICAST_PVS );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
BurnWall
|
|
================
|
|
*/
|
|
void BurnWall
|
|
(
|
|
Vector org,
|
|
Vector end,
|
|
int amount
|
|
)
|
|
|
|
{
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_BURNWALL );
|
|
gi.WritePosition( org.vec3() );
|
|
gi.WritePosition( end.vec3() );
|
|
gi.WriteByte( amount );
|
|
gi.multicast( org.vec3(), MULTICAST_ALL );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
TempModel
|
|
================
|
|
*/
|
|
void TempModel
|
|
(
|
|
Entity * parent,
|
|
Vector origin,
|
|
Vector angles,
|
|
const char * modelname,
|
|
int anim,
|
|
float scale,
|
|
float alpha,
|
|
int flags,
|
|
float life
|
|
)
|
|
{
|
|
byte sendflags;
|
|
int modelindex;
|
|
sendflags = 0;
|
|
|
|
// encode sendflags
|
|
if (origin[0] || origin[1] || origin[2])
|
|
sendflags |= TM_ORIGIN;
|
|
if (angles[0] || angles[1] || angles[2])
|
|
sendflags |= TM_ANGLES;
|
|
if (anim)
|
|
sendflags |= TM_ANIM;
|
|
if (scale != 1.0f)
|
|
sendflags |= TM_SCALE;
|
|
if (flags != 0)
|
|
sendflags |= TM_FLAGS;
|
|
if (life != 0.1f)
|
|
sendflags |= TM_LIFE;
|
|
if (parent)
|
|
sendflags |= TM_OWNER;
|
|
if (alpha != 1.0f)
|
|
sendflags |= TM_ALPHA;
|
|
|
|
modelindex = gi.modelindex( modelname );
|
|
//
|
|
// send the stuff out
|
|
//
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_TEMPMODEL );
|
|
gi.WriteByte( sendflags );
|
|
if (sendflags & TM_ORIGIN)
|
|
gi.WritePosition( origin.vec3() );
|
|
if (sendflags & TM_ANGLES)
|
|
gi.WritePosition( angles.vec3() );
|
|
if (sendflags & TM_ANIM)
|
|
gi.WriteByte( anim );
|
|
if (sendflags & TM_SCALE)
|
|
gi.WriteShort( scale*256 );
|
|
if (sendflags & TM_ALPHA)
|
|
gi.WriteByte( alpha*255 );
|
|
if (sendflags & TM_FLAGS)
|
|
gi.WriteByte( flags );
|
|
if (sendflags & TM_OWNER)
|
|
gi.WriteShort( parent->entnum );
|
|
if (sendflags & TM_LIFE)
|
|
gi.WriteShort( life*1000.0f );
|
|
gi.WriteShort( modelindex );
|
|
if (parent)
|
|
{
|
|
gi.multicast( parent->worldorigin.vec3(), MULTICAST_ALL );
|
|
}
|
|
else
|
|
{
|
|
gi.multicast( origin.vec3(), MULTICAST_ALL );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
TesselateModel
|
|
================
|
|
*/
|
|
void TesselateModel
|
|
(
|
|
Entity * ent,
|
|
int min_size,
|
|
int max_size,
|
|
Vector dir,
|
|
float power,
|
|
float percentage,
|
|
int thickness,
|
|
Vector origin,
|
|
int type,
|
|
int lightstyle
|
|
)
|
|
{
|
|
short sendflags;
|
|
|
|
sendflags = 0;
|
|
|
|
if (power > 255) power = 255;
|
|
if (power < 0) power = 0;
|
|
if (thickness > 255 ) thickness = 255;
|
|
if (thickness < 0 ) thickness = 0;
|
|
if (percentage < 0) percentage = 0;
|
|
if (percentage > 1) percentage = 1;
|
|
if (min_size > 255) min_size = 255;
|
|
if (min_size < 0) min_size = 0;
|
|
if (max_size > 255) max_size = 255;
|
|
if (max_size < 0) max_size = 0;
|
|
|
|
// encode sendflags
|
|
if ( origin[0] || origin[1] || origin[2] )
|
|
sendflags |= TESS_ORIGIN;
|
|
if ( dir[0] || dir[1] || dir[2] )
|
|
sendflags |= TESS_DIR;
|
|
if ( ent && ent->edict )
|
|
sendflags |= TESS_ENTNUM;
|
|
if ( min_size != TESS_DEFAULT_MIN_SIZE )
|
|
sendflags |= TESS_MINSIZE;
|
|
if ( max_size != TESS_DEFAULT_MAX_SIZE )
|
|
sendflags |= TESS_MAXSIZE;
|
|
if ( power != TESS_DEFAULT_POWER )
|
|
sendflags |= TESS_POWER;
|
|
if ( percentage != TESS_DEFAULT_PERCENT )
|
|
sendflags |= TESS_PERCENT;
|
|
if ( thickness != min_size )
|
|
sendflags |= TESS_THICK;
|
|
if (lightstyle != TESS_DEFAULT_LIGHTSTYLE)
|
|
sendflags |= TESS_LIGHTSTYLE;
|
|
if (type != TESS_DEFAULT_TYPE)
|
|
sendflags |= TESS_TYPE;
|
|
|
|
//
|
|
// send the stuff out
|
|
//
|
|
gi.WriteByte( svc_temp_entity );
|
|
gi.WriteByte( TE_TESSELATE );
|
|
gi.WriteShort( sendflags );
|
|
if (sendflags & TESS_ORIGIN)
|
|
gi.WritePosition( origin.vec3() );
|
|
if (sendflags & TESS_DIR)
|
|
gi.WriteDir( dir.vec3() );
|
|
if (sendflags & TESS_ENTNUM)
|
|
gi.WriteShort( ent->entnum );
|
|
if (sendflags & TESS_MINSIZE)
|
|
gi.WriteByte( min_size );
|
|
if (sendflags & TESS_MAXSIZE)
|
|
gi.WriteByte( max_size );
|
|
if (sendflags & TESS_POWER)
|
|
gi.WriteByte( power );
|
|
if (sendflags & TESS_PERCENT)
|
|
gi.WriteByte( percentage * 255.0f );
|
|
if (sendflags & TESS_THICK)
|
|
gi.WriteByte( thickness );
|
|
if (sendflags & TESS_LIGHTSTYLE)
|
|
gi.WriteByte( lightstyle );
|
|
if (sendflags & TESS_TYPE)
|
|
gi.WriteByte( type );
|
|
|
|
if (ent)
|
|
{
|
|
if (gi.IsModel( ent->edict->s.modelindex ))
|
|
gi.multicast( ((ent->absmax+ent->absmin)*0.5).vec3(), MULTICAST_PVS );
|
|
else
|
|
gi.multicast( ent->worldorigin.vec3(), MULTICAST_ALL );
|
|
}
|
|
else
|
|
{
|
|
gi.multicast( origin.vec3(), MULTICAST_ALL );
|
|
}
|
|
}
|
|
|
|
CLASS_DECLARATION( Entity, Bubble, NULL )
|
|
|
|
Event EV_Bubble_Think( "think" );
|
|
|
|
ResponseDef Bubble::Responses[] =
|
|
{
|
|
{ &EV_Bubble_Think, ( Response )Bubble::Think },
|
|
//{ &EV_Touch, ( Response )Bubble::Touch },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
EXPORT_FROM_DLL void Bubble::Touch
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
edict->s.scale *= 0.8f;
|
|
velocity[ 0 ] = G_CRandom( 30 );
|
|
velocity[ 1 ] = G_CRandom( 30 );
|
|
velocity[ 2 ] = -G_Random( 30 );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Bubble::Think
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
velocity[ 0 ] *= 0.8f;
|
|
velocity[ 1 ] *= 0.8f;
|
|
velocity[ 2 ] += 10;
|
|
|
|
if ( ( edict->s.scale > 0.2f ) || ( edict->s.scale < 0.02f ) || !waterlevel )
|
|
{
|
|
PostEvent( EV_Remove, 0 );
|
|
return;
|
|
}
|
|
|
|
PostEvent( EV_Bubble_Think, 0.1 );
|
|
}
|
|
|
|
void Bubble::Setup
|
|
(
|
|
Vector pos
|
|
)
|
|
|
|
{
|
|
setSolidType( SOLID_NOT );
|
|
setMoveType( MOVETYPE_FLY );
|
|
|
|
setModel( "sprites/bubble.spr" );
|
|
setOrigin( pos );
|
|
worldorigin.copyTo( edict->s.old_origin );
|
|
mass = 0;
|
|
|
|
watertype = gi.pointcontents( worldorigin.vec3() );
|
|
|
|
PostEvent( EV_Bubble_Think, 0.1 );
|
|
|
|
velocity[ 0 ] = G_CRandom( 30 );
|
|
velocity[ 1 ] = G_CRandom( 30 );
|
|
velocity[ 2 ] = -G_Random( 30 );
|
|
|
|
edict->s.scale = 0.05f + G_CRandom( 0.04f );
|
|
}
|
|
|
|
CLASS_DECLARATION( ScriptSlave, FuncBeam, "func_beam" );
|
|
|
|
Event EV_FuncBeam_Activate( "activate" );
|
|
Event EV_FuncBeam_Deactivate( "deactivate" );
|
|
Event EV_FuncBeam_Diameter( "diameter" );
|
|
Event EV_FuncBeam_Lightstyle( "beamstyle" );
|
|
Event EV_FuncBeam_Maxoffset( "maxoffset" );
|
|
Event EV_FuncBeam_Minoffset( "minoffset" );
|
|
Event EV_FuncBeam_Color( "color" );
|
|
Event EV_FuncBeam_SetTarget( "settarget" );
|
|
|
|
ResponseDef FuncBeam::Responses[] =
|
|
{
|
|
{ &EV_Activate, ( Response )FuncBeam::Activate },
|
|
{ &EV_FuncBeam_Activate, ( Response )FuncBeam::Activate },
|
|
{ &EV_FuncBeam_Deactivate, ( Response )FuncBeam::Deactivate },
|
|
{ &EV_FuncBeam_Diameter, ( Response )FuncBeam::SetDiameter },
|
|
{ &EV_FuncBeam_Lightstyle, ( Response )FuncBeam::SetLightstyle },
|
|
{ &EV_FuncBeam_Maxoffset, ( Response )FuncBeam::SetMaxoffset },
|
|
{ &EV_FuncBeam_Minoffset, ( Response )FuncBeam::SetMinoffset },
|
|
{ &EV_FuncBeam_Color, ( Response )FuncBeam::SetColor },
|
|
{ &EV_FuncBeam_SetTarget, ( Response )FuncBeam::SetTarget },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
void FuncBeam::SetColor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Vector color = ev->GetVector( 1 );
|
|
edict->s.color_r = color[0];
|
|
edict->s.color_g = color[1];
|
|
edict->s.color_b = color[2];
|
|
}
|
|
|
|
void FuncBeam::SetMaxoffset
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
edict->s.frame = ev->GetFloat( 1 );
|
|
}
|
|
|
|
void FuncBeam::SetMinoffset
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
edict->s.radius = ev->GetFloat( 1 );
|
|
}
|
|
|
|
void FuncBeam::SetDiameter
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
edict->s.frame = ev->GetFloat( 1 );
|
|
}
|
|
|
|
void FuncBeam::SetLightstyle
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
edict->s.skinnum = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void FuncBeam::Deactivate
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
hideModel();
|
|
|
|
// Cancel all of the events to activate
|
|
CancelEventsOfType( EV_FuncBeam_Activate );
|
|
CancelEventsOfType( EV_Activate );
|
|
}
|
|
|
|
void FuncBeam::Shoot
|
|
(
|
|
Vector start,
|
|
Vector end,
|
|
int radius
|
|
)
|
|
|
|
{
|
|
trace_t trace;
|
|
Vector dir;
|
|
Vector b1=vec_zero, b2=vec_zero;
|
|
|
|
dir = end - start;
|
|
|
|
if ( edict->s.effects & EF_BEAMEFFECT )
|
|
{
|
|
b1 = Vector( -radius, -radius, -radius );
|
|
b2 = Vector( radius, radius, radius );
|
|
}
|
|
|
|
trace = G_FullTrace( start, b1, b2, end, radius, this, MASK_SHOT, "FuncBeam::Activate" );
|
|
|
|
if ( trace.ent->entity && trace.ent->entity->takedamage )
|
|
{
|
|
if ( trace.intersect.valid )
|
|
{
|
|
// We hit a valid group so send in location based damage
|
|
trace.ent->entity->Damage( this,
|
|
this,
|
|
damage,
|
|
trace.endpos,
|
|
dir,
|
|
trace.plane.normal,
|
|
0,
|
|
0,
|
|
MOD_BEAM,
|
|
trace.intersect.parentgroup,
|
|
-1,
|
|
trace.intersect.damage_multiplier );
|
|
}
|
|
else
|
|
{
|
|
// We didn't hit any groups, so send in generic damage
|
|
trace.ent->entity->Damage( this,
|
|
this,
|
|
damage,
|
|
trace.endpos,
|
|
dir,
|
|
trace.plane.normal,
|
|
0,
|
|
0,
|
|
MOD_BEAM,
|
|
-1,
|
|
-1,
|
|
1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FuncBeam::Activate
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Vector forward,endpoint;
|
|
trace_t trace;
|
|
|
|
showModel();
|
|
|
|
// An entity is targeted
|
|
if ( end )
|
|
{
|
|
VectorCopy( end->origin, edict->s.old_origin );
|
|
}
|
|
else
|
|
{
|
|
worldangles.AngleVectors( &forward, NULL, NULL );
|
|
endpoint = forward * 8192;
|
|
trace = G_Trace( origin, vec_zero, vec_zero, endpoint, this, MASK_SOLID, "FuncBeam::Activate" );
|
|
VectorCopy( trace.endpos, edict->s.old_origin );
|
|
}
|
|
|
|
if ( damage )
|
|
Shoot( origin, edict->s.old_origin, edict->s.frame );
|
|
|
|
|
|
// If life is set, then post a deactivate message
|
|
if ( life > 0 && !EventPending( EV_FuncBeam_Deactivate ) )
|
|
{
|
|
PostEvent( EV_FuncBeam_Deactivate, life );
|
|
return;
|
|
}
|
|
|
|
// Shoot beam and cause damage
|
|
PostEvent( EV_Activate, 0.1 );
|
|
}
|
|
|
|
void FuncBeam::SetTarget
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const char *target;
|
|
int num;
|
|
|
|
// Set the end position if there is a target set.
|
|
target = Target();
|
|
if ( target && strlen( target ) )
|
|
{
|
|
if ( num = G_FindTarget( 0, target ) )
|
|
{
|
|
end = G_GetEntity( num );
|
|
assert( end );
|
|
VectorCopy( end->origin.vec3(), edict->s.old_origin );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*SINED func_beam (0 .8 .5) (-8 -8 -8) (8 8 8) START_ON ANIMATE FAST ROLL RANDSTART ELECTRIC RANDALPHA
|
|
|
|
This creates a beam effect from the origin to the target's origin. If no
|
|
target is specified, uses angles and projects beam out from there.
|
|
|
|
"model" Specifies the model to use as the beam
|
|
"overlap" Specifies the amount of overlap each beam link should have. Use this to fill
|
|
in the cracks when using electric on beams. (Default is 0)
|
|
"minoffset" Amount of electrical variation at the endpoints of beam (Default is 5)
|
|
"maxoffset" Amount of electrical variation in the middle of beam (Default is 25)
|
|
"color" Vector specifiying the red,green, and blue components. (Default is "1 1 1")
|
|
"alpha" Alpha of the beam (Default is 1.0)
|
|
"damage" Amount of damage the beam inflicts if beam hits someone (Default is 0)
|
|
"angles" Sets the angle of the beam if no target is specified.
|
|
"life" Deactivates the beam life seconds after the beam is activated. If not set, beam
|
|
will not be deactivated.
|
|
"beamstyle" Sets the style for this beam to cycle through. This applies only
|
|
to beams without models. sample styles (121 blood, 120 gunsmoke, 123 orangeglow, 124 blueyellow,
|
|
125 debris trail, 128 oil, 129 waterspray 130 blue-yellow-blue) See global0.scr for
|
|
more style numbers
|
|
|
|
START_ON - Starts the beam on
|
|
ANIMATE - Plays animation of the model
|
|
FAST - Plays the animation at 20 frames/sec
|
|
ROLL - Rolls the beam
|
|
RANDSTART - Starts each segment of the beam's animation on a different frame
|
|
ELECTRIC - Applies a random electric efffect to the beam
|
|
RANDALPHA - Randomly generate the alpha for the beam
|
|
|
|
If the model field is not set, then a straight beam will be created using the color
|
|
specified. color, minoffset, maxoffset ,ANIMATE, FAST, ROLL, RANDSTART,
|
|
ELECTRIC, and RANDALPHA are not applicable if the model is not set.
|
|
|
|
/*****************************************************************************/
|
|
|
|
FuncBeam::FuncBeam()
|
|
{
|
|
Vector color;
|
|
const char *target;
|
|
trace_t trace;
|
|
Vector endpoint, forward;
|
|
const char *model=0;
|
|
qboolean setangles;
|
|
|
|
setSolidType( SOLID_NOT );
|
|
setOrigin( origin );
|
|
|
|
end = NULL;
|
|
|
|
target = Target();
|
|
|
|
if ( target )
|
|
PostEvent( EV_FuncBeam_SetTarget, 0 );
|
|
else
|
|
// No target specified, so use angles to get direction and do a trace to get endpoint.
|
|
{
|
|
setangles = ( G_GetSpawnArg( "angle" ) || G_GetSpawnArg( "angles" ) );
|
|
if ( setangles )
|
|
{
|
|
float angle;
|
|
angle = G_GetFloatArg( "angle", 0 );
|
|
angles = G_GetVectorArg( "angles", Vector( 0, angle, 0 ) );
|
|
|
|
setAngles( angles );
|
|
angles.AngleVectors( &forward, NULL, NULL );
|
|
setAngles( angles );
|
|
endpoint = forward * 8192;
|
|
trace = G_Trace( origin, vec_zero, vec_zero, endpoint, this, MASK_SOLID, "FuncBeam" );
|
|
VectorCopy( trace.endpos, edict->s.old_origin );
|
|
}
|
|
else // Nothing specified. Remove this thing
|
|
{
|
|
gi.dprintf( "No target or angles set on FuncBeam.\n" );
|
|
PostEvent( EV_Remove, 0 );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for a model and use the beam models if it is set
|
|
model = G_GetSpawnArg( "model" );
|
|
if ( model && strlen( model ) )
|
|
{
|
|
setModel( model );
|
|
edict->s.effects |= EF_BEAMEFFECT;
|
|
edict->s.frame = G_GetFloatArg( "maxoffset", 25 );
|
|
edict->s.radius = G_GetFloatArg( "minoffset", 5 );
|
|
edict->s.lightofs = G_GetIntArg( "overlap", 0 );
|
|
// skinnum will hold the flags
|
|
edict->s.skinnum = 0;
|
|
if ( spawnflags & 0x0002 )
|
|
edict->s.skinnum |= BEAM_AUTO_ANIMATE;
|
|
if ( spawnflags & 0x0004 )
|
|
edict->s.skinnum |= BEAM_ANIMATE_FAST;
|
|
if ( spawnflags & 0x0008 )
|
|
edict->s.skinnum |= BEAM_ROLL;
|
|
if ( spawnflags & 0x0010 )
|
|
edict->s.skinnum |= BEAM_ANIMATE_RANDOM_START;
|
|
if ( spawnflags & 0x0020 )
|
|
edict->s.skinnum |= BEAM_LIGHTNING_EFFECT;
|
|
if ( spawnflags & 0x0040 )
|
|
edict->s.skinnum |= BEAM_RANDOM_ALPHA;
|
|
}
|
|
else // Otherwise do a Quake2 style beam
|
|
{
|
|
edict->s.renderfx |= RF_BEAM;
|
|
edict->s.frame = G_GetIntArg( "diameter", 4 );
|
|
edict->s.skinnum = G_GetIntArg( "beamstyle", 255 );;
|
|
color = G_GetVectorArg( "color", Vector( 1,1,1 ) );
|
|
edict->s.color_r = color.x;
|
|
edict->s.color_g = color.y;
|
|
edict->s.color_b = color.z;
|
|
edict->s.modelindex = 1; // must be non-zero
|
|
}
|
|
|
|
edict->s.alpha = G_GetFloatArg( "alpha", 1 );
|
|
damage = G_GetFloatArg( "damage", 0 );
|
|
life = G_GetFloatArg( "life", 0 );
|
|
|
|
if ( spawnflags & 0x0001 ) // Start On
|
|
{
|
|
PostEvent( EV_Activate, 0 );
|
|
}
|
|
else
|
|
{
|
|
hideModel();
|
|
}
|
|
}
|
|
|
|
CLASS_DECLARATION( Entity, Beam, NULL );
|
|
|
|
ResponseDef Beam::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Beam::Beam()
|
|
{
|
|
hideModel();
|
|
edict->s.frame = 0;
|
|
edict->s.skinnum = 0;
|
|
|
|
setMoveType( MOVETYPE_NONE );
|
|
setSolidType( SOLID_NOT );
|
|
|
|
edict->s.renderfx |= RF_BEAM;
|
|
edict->s.modelindex = 1; // must be non-zero
|
|
}
|
|
|
|
void Beam::setBeam
|
|
(
|
|
Vector startpos,
|
|
Vector endpos,
|
|
int diameter,
|
|
float r,
|
|
float g,
|
|
float b,
|
|
float alpha,
|
|
float lifespan
|
|
)
|
|
|
|
{
|
|
showModel();
|
|
|
|
if ( lifespan )
|
|
{
|
|
PostEvent( EV_Remove, lifespan );
|
|
}
|
|
|
|
start = startpos;
|
|
end = endpos;
|
|
|
|
setOrigin( start );
|
|
|
|
edict->s.old_origin[ 0 ] = end[ 0 ];
|
|
edict->s.old_origin[ 1 ] = end[ 1 ];
|
|
edict->s.old_origin[ 2 ] = end[ 2 ];
|
|
|
|
edict->s.frame = diameter;
|
|
edict->s.skinnum = 255;
|
|
|
|
edict->s.color_r = r;
|
|
edict->s.color_g = g;
|
|
edict->s.color_b = b;
|
|
edict->s.alpha = alpha;
|
|
|
|
link();
|
|
}
|
|
|
|
CLASS_DECLARATION( Entity, Projectile, NULL );
|
|
|
|
ResponseDef Projectile::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Projectile::Projectile()
|
|
{
|
|
flags |= FL_FATPROJECTILE;
|
|
}
|
|
|
|
void Projectile::Setup
|
|
(
|
|
Entity *owner,
|
|
Vector pos,
|
|
Vector dir
|
|
)
|
|
{
|
|
// This should never be called, but just in case.
|
|
assert( 0 );
|
|
PostEvent( EV_Remove, 0 );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*SINED fx_fire (0 .8 .5) (-8 -8 -8) (8 8 8) SINGLE STAR PARALLEL
|
|
|
|
This creates a fire sprite, it defaults to the cross shape
|
|
|
|
SINGLE - Only one sprite orientated
|
|
STAR - 4 sprites in a star shape
|
|
|
|
If you want flickering
|
|
|
|
Set style to the identifier contained in groupname in the surfaces to control.
|
|
|
|
"on_style" light style to set to when "on" (default is a flicker)
|
|
"off_style" light style to set to when "off" (default is "a")
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define SINGLE (1<<0)
|
|
#define STAR (1<<1)
|
|
#define PARALLEL (1<<2)
|
|
CLASS_DECLARATION( Light, FireSprite, "fx_fire" )
|
|
|
|
ResponseDef FireSprite::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
FireSprite::FireSprite()
|
|
{
|
|
if ( on_style == str( "m" ) )
|
|
on_style = str( "kkllmmnnmmllkkonnmmkkllonnmmkkllnnoonnmmllkkonnnnmmllkko" );
|
|
|
|
ProcessEvent( EV_Light_TurnOn );
|
|
setMoveType( MOVETYPE_NONE );
|
|
setSolidType( SOLID_NOT );
|
|
showModel();
|
|
edict->svflags &= ~SVF_NOCLIENT;
|
|
|
|
edict->s.effects |= EF_AUTO_ANIMATE;
|
|
if ( spawnflags & SINGLE )
|
|
{
|
|
setModel( "sprites/fire.spr" );
|
|
}
|
|
else if ( spawnflags & STAR )
|
|
{
|
|
setModel( "sprites/fire_star.spr" );
|
|
}
|
|
else if ( spawnflags & PARALLEL )
|
|
{
|
|
setModel( "sprites/fire_single.spr" );
|
|
}
|
|
else
|
|
{
|
|
setModel( "sprites/fire_cross.spr" );
|
|
}
|
|
|
|
RandomGlobalEntitySound( "snd_fire" );
|
|
}
|
|
|
|
FireSprite::~FireSprite()
|
|
{
|
|
ProcessEvent( EV_Light_TurnOff );
|
|
detach();
|
|
//
|
|
// spawn an explosion
|
|
//
|
|
if ( world )
|
|
CreateExplosion( worldorigin, 100, edict->s.scale * 0.5f, true, this, this, this );
|
|
}
|
|
|
|
CLASS_DECLARATION( Trigger, Sprite, "fx_sprite" )
|
|
|
|
Event EV_Sprite_Activate( "activate" );
|
|
Event EV_Sprite_Deactivate( "deactivate" );
|
|
|
|
ResponseDef Sprite::Responses[] =
|
|
{
|
|
{ &EV_Activate, ( Response )FuncBeam::Activate },
|
|
{ &EV_Sprite_Activate, ( Response )FuncBeam::Activate },
|
|
{ &EV_Sprite_Deactivate, ( Response )FuncBeam::Deactivate },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
void Sprite::Activate
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
showModel();
|
|
}
|
|
|
|
void Sprite::Deactivate
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
hideModel();
|
|
}
|
|
|
|
Sprite::Sprite()
|
|
{
|
|
str model;
|
|
qboolean setangles;
|
|
Vector dir;
|
|
Vector color;
|
|
|
|
model = G_GetStringArg( "sprite", "" );
|
|
edict->s.frame = G_GetIntArg( "frame", 0 );
|
|
edict->s.scale = G_GetFloatArg( "scale", 1.0 );
|
|
edict->s.alpha = G_GetFloatArg( "alpha", 1.0 );
|
|
color = G_GetVectorArg("color",Vector(1,1,1));
|
|
edict->s.color_r = color.x;
|
|
edict->s.color_g = color.y;
|
|
edict->s.color_b = color.z;
|
|
|
|
if ( !model.length() )
|
|
{
|
|
gi.dprintf( "fx_sprite: sprite not defined, removing.\n" );
|
|
PostEvent( EV_Remove, 0 );
|
|
}
|
|
|
|
setModel( model.c_str() );
|
|
setMoveType( MOVETYPE_NONE );
|
|
setSolidType( SOLID_NOT );
|
|
showModel();
|
|
edict->svflags &= ~SVF_NOCLIENT;
|
|
|
|
if ( spawnflags & 1 )
|
|
edict->s.effects |= EF_AUTO_ANIMATE;
|
|
|
|
setangles = ( G_GetSpawnArg( "angle" ) || G_GetSpawnArg( "angles" ) );
|
|
|
|
if ( setangles )
|
|
{
|
|
float angle;
|
|
|
|
angle = G_GetFloatArg( "angle", 0 );
|
|
setAngles( G_GetVectorArg( "angles", Vector( 0, angle, 0 ) ) );
|
|
}
|
|
|
|
// If trace to surface, apply sprite and remove this.
|
|
if ( spawnflags & 2 )
|
|
{
|
|
Vector dir,end,norm;
|
|
trace_t trace;
|
|
Entity *splat;
|
|
|
|
dir = G_GetMovedir();
|
|
end = worldorigin + dir * 8192;
|
|
|
|
trace = G_Trace( worldorigin, vec_zero, vec_zero, end, this, MASK_SOLIDNONFENCE, "Sprite::Sprite" );
|
|
|
|
if ( HitSky( &trace ) || ( trace.ent->solid != SOLID_BSP ) )
|
|
{
|
|
PostEvent( EV_Remove, 0 );
|
|
return;
|
|
}
|
|
|
|
splat = new Entity;
|
|
splat->setMoveType( MOVETYPE_NONE );
|
|
splat->setSolidType( SOLID_NOT );
|
|
splat->setModel( model.c_str() );
|
|
splat->edict->s.frame = edict->s.frame;
|
|
splat->edict->s.scale = edict->s.scale;
|
|
splat->edict->s.alpha = edict->s.alpha;
|
|
splat->edict->s.color_r = edict->s.color_r;
|
|
splat->edict->s.color_g = edict->s.color_g;
|
|
splat->edict->s.color_b = edict->s.color_b;
|
|
|
|
if ( spawnflags & 1 )
|
|
splat->edict->s.effects |= EF_AUTO_ANIMATE;
|
|
|
|
norm = trace.plane.normal;
|
|
norm.x = -norm.x;
|
|
norm.y = -norm.y;
|
|
|
|
splat->angles = norm.toAngles();
|
|
splat->setAngles( splat->angles );
|
|
splat->setOrigin( Vector( trace.endpos ) + ( Vector( trace.plane.normal ) * 0.2 ) );
|
|
|
|
PostEvent( EV_Remove, 0.1f );
|
|
}
|
|
}
|
|
|
|
void ChangeMusic
|
|
(
|
|
const char *current,
|
|
const char *fallback,
|
|
qboolean force
|
|
)
|
|
{
|
|
int j;
|
|
edict_t *other;
|
|
|
|
if ( current || fallback )
|
|
{
|
|
for( j = 1; j <= game.maxclients; j++ )
|
|
{
|
|
other = &g_edicts[ j ];
|
|
if ( other->inuse && other->client )
|
|
{
|
|
Player * client;
|
|
client = ( Player * )other->entity;
|
|
client->ChangeMusic( current, fallback, force );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChangeSoundtrack
|
|
(
|
|
const char * soundtrack
|
|
)
|
|
{
|
|
gi.configstring( CS_SOUNDTRACK, soundtrack );
|
|
}
|