MuckyFoot-UrbanChaos/fallen/Source/pyro.cpp
2017-05-20 11:14:17 +10:00

1349 lines
30 KiB
C++

//
// Pyrotechnics
// Burning (and maybe Exploding) Things
//
#include "game.h"
#include "pyro.h"
#include "statedef.h"
#include "C:\fallen\DDEngine\Headers\Matrix.h"
#include "C:\fallen\DDEngine\Headers\Sprite.h"
#ifndef PSX
#include "C:\fallen\DDEngine\Headers\poly.h"
#else
#include "c:\fallen\psxeng\headers\poly.h"
#endif
#include "dirt.h"
#include "ribbon.h"
#include "combat.h"
#include "barrel.h"
#include "sound.h"
#include "animate.h"
#include "interact.h"
#include "night.h"
#include "mfx.h"
#include "psystem.h"
#include "pcom.h"
#include "gamemenu.h"
RadPoint PYRO_defaultpoints[16];
/************************************************************
*
* Generic pyro infrastructure stuff
*
*/
GenusFunctions PYRO_functions[PYRO_RANGE] =
{
{PYRO_NONE, NULL},
{PYRO_FIREWALL, PYRO_state_function},
{PYRO_FIREPOOL, PYRO_state_function},
{PYRO_BONFIRE, BONFIRE_state_function},
{PYRO_IMMOLATE, IMMOLATE_state_function},
{PYRO_EXPLODE, EXPLODE_state_function},
{PYRO_DUSTWAVE, DUSTWAVE_state_function},
{PYRO_EXPLODE2, EXPLODE2_state_function},
{PYRO_STREAMER, STREAMER_state_function},
{PYRO_TWANGER, TWANGER_state_function},
{PYRO_FLICKER, PYRO_state_function},
{PYRO_HITSPANG, PYRO_state_function},
{PYRO_FIREBOMB, FIREBOMB_state_function},
{PYRO_SPLATTERY, PYRO_state_function},
{PYRO_IRONICWATERFALL, IRONICWATERFALL_state_function},
{PYRO_NEWDOME, NEWDOME_state_function},
{PYRO_WHOOMPH, PYRO_state_function},
{PYRO_GAMEOVER, PYRO_state_function},
};
//
// Zeros out the pyros in the 'the_game' structure.
//
SBYTE global_spang_count=0;
void init_pyros(void)
{
memset((UBYTE*)PYROS, 0, sizeof(Pyro) * MAX_PYROS);
#ifndef PSX
extern RadPoint PYRO_defaultpoints2[32];
memset((UBYTE*)PYRO_defaultpoints2,0,sizeof(RadPoint) * 32);
#endif
global_spang_count=0;
PYRO_COUNT = 1;
}
//
// Creates a new pyro of the given type.
//
Thing *alloc_pyro(UBYTE type)
{
SLONG i;
Thing *p_thing;
Pyro *p_pyro;
THING_INDEX t_index;
SLONG a_index;
ASSERT(WITHIN(type, 1, PYRO_RANGE - 1));
//
// Look for an unused pyro structure.
//
for (i = 1; i < MAX_PYROS; i++)
{
if (PYROS[i].PyroType == PYRO_NONE)
{
a_index = i;
goto found_pyro;
}
}
//
// There are no spare pyro structures.
//
TRACE("Run out of pyro structures.");
return NULL;
found_pyro:
t_index = alloc_primary_thing(CLASS_PYRO);
if (t_index)
{
p_thing = TO_THING(t_index);
p_pyro = TO_PYRO(a_index);
// DebugText(" alloc pyro %d thing %d\n",a_index,THING_NUMBER(p_thing));
if(a_index>=PYRO_COUNT)
PYRO_COUNT=a_index+1;
//
// Link the pyro to the thing and back again.
//
p_thing->Class = CLASS_PYRO;
p_thing->Genus.Pyro = p_pyro;
p_pyro->PyroType = type;
p_pyro->thing = p_thing;
//
// Set the draw type
//
p_thing->DrawType = DT_PYRO;
// PYRO_COUNT++;
return p_thing;
}
else
{
//
// Free up the pyro structure
//
TO_PYRO(a_index)->PyroType = PYRO_NONE;
return NULL;
}
}
void free_pyro(Thing *p_thing)
{
Pyro *pyro = PYRO_get_pyro(p_thing);
remove_thing_from_map(p_thing);
// Free up type-specific info
switch(pyro->PyroType) {
case PYRO_IMMOLATE:
{
#ifndef PSX
UBYTE i,r;
if (pyro->Dummy==2) r=5; else r=2;
for (i=0;i<r;i++)
RIBBON_free(pyro->radii[i]);
#endif
if (pyro->victim&&(pyro->victim->Class==CLASS_PERSON)) pyro->victim->Genus.Person->BurnIndex=0;
}
break;
case PYRO_EXPLODE2:
case PYRO_BONFIRE:
case PYRO_WHOOMPH:
NIGHT_dlight_destroy(pyro->dlight);
break;
}
pyro->soundid=0;
//
// Free the pyro structure and the thing structure.
//
pyro->PyroType = PYRO_NONE;
// PYRO_COUNT--;
p_thing->DrawType=DT_NONE;
free_thing(p_thing);
}
Thing *PYRO_create(GameCoord pos, UBYTE type)
{
Thing *p_thing = alloc_pyro(type);
if (p_thing != NULL)
{
p_thing->WorldPos = pos;
add_thing_to_map(p_thing);
//
// Make it initialise itself.
//
set_state_function(p_thing, STATE_INIT);
}
return p_thing;
}
Pyro *PYRO_get_pyro(struct Thing *pyro_thing)
{
Pyro *pyro;
ASSERT(WITHIN(pyro_thing, TO_THING(1), TO_THING(MAX_THINGS)));
ASSERT(pyro_thing->Class == CLASS_PYRO);
pyro = pyro_thing->Genus.Pyro;
ASSERT(WITHIN(pyro, TO_PYRO(1), TO_PYRO(MAX_PYROS - 1)));
return pyro;
}
/*************************************************************
*
* Blast radius stuff
* (rocket jumping, anyone? :P )
*/
#define MAX_COL_WITH 16
void PYRO_blast_radius(SLONG x, SLONG y, SLONG z, SLONG radius, SLONG strength) {
SLONG collide_types,col_with_upto,i;
THING_INDEX col_with[MAX_COL_WITH];
Thing *col_thing;
collide_types = (1 << CLASS_PERSON);
col_with_upto = THING_find_sphere(
x >> 8,
y >> 8,
z >> 8,
radius,
col_with,
MAX_COL_WITH,
collide_types);
ASSERT(col_with_upto <= MAX_COL_WITH);
for (i = 0; i < col_with_upto; i++)
{
col_thing = TO_THING(col_with[i]);
// stuff to do to everything:
// stuff to do to specific classes (for when collide_types gets new flags):
switch(col_thing->Class) {
case CLASS_PERSON:
{
Thing *pyro=PYRO_create(col_thing->WorldPos,PYRO_IMMOLATE);
if(pyro)
{
pyro->Genus.Pyro->victim=col_thing;
pyro->Genus.Pyro->Flags=PYRO_FLAGS_FLICKER;
}
}
break;
}
}
}
/*************************************************************
*
* Pyro Utility Belt
*
*/
//
// MergeSoundFX creates a sound effect for the pyro, unless
// other pyros of the same time already did that and are close
// by, in which case they share.
//
#define MAX_COL_WITH 16
THING_INDEX col_with[MAX_COL_WITH];
SLONG MergeSoundFX(Thing *thing, Pyro *pyro) {
SLONG col_with_upto;
SLONG collide_types = (1 << CLASS_PYRO);
Thing *col_thing;
SLONG i, sample, mode;
col_with_upto = THING_find_sphere(
thing->WorldPos.X >> 8,
thing->WorldPos.Y >> 8,
thing->WorldPos.Z >> 8,
5*256,
col_with,
MAX_COL_WITH,
collide_types);
for (i = 0; i < col_with_upto; i++)
{
col_thing = TO_THING(col_with[i]);
// skip all the dull things
if ((col_thing->State == STATE_DEAD)||
(col_thing->State == STATE_DYING)|| // Dead or dying things are of no interest
(col_thing->Genus.Pyro->PyroType!=pyro->PyroType)|| // nor non-matching kinds of pyro
(!col_thing->Genus.Pyro->soundid)) // can't share if it has no sound to share
continue;
if ((pyro->PyroType==PYRO_EXPLODE2)&&(col_thing->Genus.Pyro->counter>50)) continue;
// it'd be nice to average out the position and modify the gain, but hey.
pyro->soundid=col_thing->Genus.Pyro->soundid;
return pyro->soundid;
}
switch(pyro->PyroType) {
case PYRO_EXPLODE2:
#ifndef PSX
sample=S_EXPLODE_START+(rand()%3); mode=0; break;
#else
sample=S_EXPLODE_START; mode=0; break;
#endif
case PYRO_BONFIRE:
case PYRO_FLICKER:
// sample=S_FIRE; mode=WAVE_LOOP; break;
sample=S_FIRE; mode=MFX_LOOPED; break;
default:
return 0; // since we don't know what sound to play, we won't.
}
MFX_play_thing(THING_NUMBER(thing),sample,MFX_OVERLAP|mode,thing);
pyro->soundid=THING_NUMBER(thing); // heh.
return pyro->soundid;
}
static void normalise_val256(SLONG *vx,SLONG *vy,SLONG *vz)
{
SLONG len;
//was if ((abs(*vx)>512)&&(abs(*vy)>512)&&(abs(*vz)>512))
if ((abs(*vx)>32768)&&(abs(*vy)>32768)&&(abs(*vz)>32768))
{
(*vx)>>=8;
(*vy)>>=8;
(*vz)>>=8;
}
len=((*vx) * (*vx)) + ((*vy) * (*vy)) + ((*vz) * (*vz));
len=Root(len);
if(len==0)
len=1;
*vx=(*vx<<8)/len;
*vy=(*vy<<8)/len;
*vz=(*vz<<8)/len;
}
/*************************************************************
*
* Specific pyro behaviour stuff
*
*/
//
// The functions called when a pyro is in each State.
// course, they all do the same right now
//
void PYRO_fn_init (Thing *);
void PYRO_fn_normal(Thing *);
void PYRO_fn_init_ex(Thing *);
void PYRO_fn_normal_ex(Thing *);
StateFunction BONFIRE_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal}
};
StateFunction IMMOLATE_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal}
};
StateFunction EXPLODE_state_function[] =
{
{STATE_INIT, PYRO_fn_init_ex },
{STATE_NORMAL, PYRO_fn_normal_ex}
};
StateFunction DUSTWAVE_state_function[] =
{
{STATE_INIT, PYRO_fn_init},
{STATE_NORMAL, PYRO_fn_normal}
};
StateFunction EXPLODE2_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction STREAMER_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction TWANGER_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction FIREBOMB_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction IRONICWATERFALL_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction NEWDOME_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
StateFunction PYRO_state_function[] =
{
{STATE_INIT, PYRO_fn_init },
{STATE_NORMAL, PYRO_fn_normal }
};
// ========================================================
//
// The state functions.
//
// ========================================================
void PYRO_fn_init(Thing *thing)
{
Pyro *pyro = PYRO_get_pyro(thing);
UBYTE i;
pyro->counter=0;
pyro->radius=0;
pyro->soundid=0;
for (i=0;i<8;i++) pyro->radii[i]=0;
switch (pyro->PyroType) {
case PYRO_BONFIRE:
pyro->dlight=NIGHT_dlight_create(
thing->WorldPos.X>>8, thing->WorldPos.Y>>8, thing->WorldPos.Z>>8,
100, 35, 30, 0);
pyro->Dummy=0;
pyro->soundid=MergeSoundFX(thing, pyro);
break;
case PYRO_WHOOMPH:
pyro->dlight=NIGHT_dlight_create(
thing->WorldPos.X>>8, thing->WorldPos.Y>>8, thing->WorldPos.Z>>8,
100, 1, 0, 0);
break;
case PYRO_IMMOLATE:
#ifndef PSX
pyro->radii[0]=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,8+(rand()&7),POLY_PAGE_FLAMES3,-1,8,5+(rand()&7));
if (!pyro->radii[0]) {
free_pyro(thing);
return;
}
pyro->radii[1]=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,8+(rand()&7),POLY_PAGE_FLAMES3,-1,8,5+(rand()&7));
if (!pyro->radii[1]) {
RIBBON_free(pyro->radii[0]);
free_pyro(thing);
return;
}
#endif
pyro->tints[0]=pyro->tints[1]=0;
pyro->Dummy=0;
break;
case PYRO_STREAMER:
for (i=0;i<4;i++) {
// set streamer start time offsets
pyro->radii[i]=rand()&0x3f;
// prepare streamer attitudes
pyro->radii[i+4]=rand();
}
pyro->counter=4; // each streamer decs this when done
break;
case PYRO_TWANGER:
for (i=0;i<8;i++) {
// set twanger attitudes;
pyro->radii[i]=rand();
}
pyro->tints[0]=pyro->tints[1]=0;
break;
case PYRO_FLICKER:
#ifndef PSX
pyro->Dummy=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,8+(rand()&0xf),POLY_PAGE_FLAMES3,-1,8,5+(rand()&7));
#endif
pyro->scale=140+(rand()&0x7f);
pyro->radii[0]=256; pyro->radii[1]=0;
// pyro->padding=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,8+(rand()&0xf),POLY_PAGE_SPARKLE,-1,8,1);
// pyro->scale=100;
pyro->tints[0]=pyro->tints[1]=0;
if (!pyro->Dummy) {
free_pyro(thing);
return;
}
pyro->soundid=MergeSoundFX(thing, pyro);
break;
case PYRO_EXPLODE2:
pyro->soundid=MergeSoundFX(thing, pyro);
MFX_play_ambient(0,S_EXPLODE_AMBIENT,0);
pyro->dlight=NIGHT_dlight_create(thing->WorldPos.X>>8,thing->WorldPos.Y>>8,thing->WorldPos.Z>>8,pyro->scale>>1,0,0,0);
break;
case PYRO_HITSPANG:
GameCoord vec,old;
if (pyro->Dummy) {
// we're shooting a thing, so set up a hit location
// hopefully the creator has set a 'victim' by now
if (pyro->victim->Class == CLASS_PERSON)
{
calc_sub_objects_position(
pyro->victim,
pyro->victim->Draw.Tweened->AnimTween,
SUB_OBJECT_HEAD,
&vec.X,
&vec.Y,
&vec.Z);
vec.X<<=8; vec.Y<<=8; vec.Z<<=8;
vec.X+=pyro->victim->WorldPos.X;
vec.Y+=pyro->victim->WorldPos.Y;
vec.Z+=pyro->victim->WorldPos.Z;
//#ifndef VERSION_GERMAN
if(VIOLENCE)
for (i=0;i<2;i++)
DIRT_new_water(
vec.X>>8,
vec.Y>>8,
vec.Z>>8,
(Random()&0xf)-7,
0,
(Random()&0xf)-7,
DIRT_TYPE_BLOOD);
//#endif
}
else
{
//
// Shooting non-people...
//
vec = pyro->victim->WorldPos;
DIRT_new_sparks(vec.X>>8,vec.Y>>8,vec.Z>>8,2);
}
} else {
// we're shooting a random location, target should be preset
vec=pyro->target;
}
// the WorldPos should be the source of the shot
pyro->target.X=pyro->thing->WorldPos.X-vec.X;
pyro->target.Y=pyro->thing->WorldPos.Y-vec.Y;
pyro->target.Z=pyro->thing->WorldPos.Z-vec.Z;
old=pyro->target; old.X>>=8; old.Y>>=8; old.Z>>=8;
normalise_val256(&pyro->target.X,&pyro->target.Y,&pyro->target.Z);
if (abs(old.X)<abs(pyro->target.X<<1)) {// too big
pyro->target=old;
pyro->target.X>>=1; pyro->target.Y>>=1; pyro->target.Z>>=1;
}
// pyro->target.X>>=2; pyro->target.Y>>=2; pyro->target.Z>>=2;
/* SLONG tst= (pyro->target.X*pyro->target.X)+
(pyro->target.Y*pyro->target.Y)+
(pyro->target.Z*pyro->target.Z);
// gnasty float version for testing purposes.
tst=sqrt(tst);
TRACE("shot: %d %d %d : %d\n",pyro->target.X,pyro->target.Y,pyro->target.Z,tst);
*/
move_thing_on_map(pyro->thing,&vec);
break;
}
//
// Start being a normal pyro.
//
set_state_function(thing, STATE_NORMAL);
}
void PYRO_fn_init_ex(Thing *thing)
{
Pyrex *pyro = (Pyrex*)PYRO_get_pyro(thing);
UBYTE i,j;
SLONG height,radius;
RadPoint *pt;
SLONG cx,cz;
// if this is the first time an explosion is created, then set up
// global precalc stuff for the hemisphere
if (!PYRO_defaultpoints[0].flags) {
PYRO_defaultpoints[0].flags=1;
pt=PYRO_defaultpoints;
for (i=0;i<2;i++) {
height=SIN(i*350);
radius=COS(i*350)>>8;
// generate ring x,y
for (j=0;j<8;j++) {
pt->x=(radius*((SLONG)SIN(j*256)))/256;
pt->z=(radius*((SLONG)COS(j*256)))/256;
pt->y=height;
pt++;
}
}
}
// set up the stuff specific to this hemisphere
for (i=0;i<8;i++) {
pyro->points[i].delta=40+(rand()&0x1f);
pyro->points[i].radius=10;
}
height=0;
for (i=8;i<16;i++) {
pyro->points[i].delta=30+(rand()&0x1f);
pyro->points[i].radius=10;
height+=pyro->points[i].delta;
}
pyro->points[16].delta=height*0.125f;
pyro->points[16].radius=100;
thing->Genus.Pyro->Timer1=0;
// Start being a normal pyro.
set_state_function(thing, STATE_NORMAL);
}
void PYRO_fn_normal(Thing *thing)
{
GameCoord new_pos;
SLONG mag, rpos, altitude;
UBYTE i;
CBYTE msg[300];
Pyro *pyro = PYRO_get_pyro(thing);
ASSERT(pyro->thing==thing);
switch(pyro->PyroType) {
case PYRO_FIREWALL:
if (pyro->counter<254) {
GameCoord posn;
Thing *new_pyro;
SLONG angle;
pyro->counter+=16;
posn=pyro->target;
posn.X-=thing->WorldPos.X;
posn.Y-=thing->WorldPos.Y;
posn.Z-=thing->WorldPos.Z;
posn.X/=256; posn.Y/=256; posn.Z/=256;
new_pos.X=posn.X*pyro->counter;
new_pos.Y=posn.Y*pyro->counter;
new_pos.Z=posn.Z*pyro->counter;
new_pos.X+=thing->WorldPos.X;
new_pos.Y+=thing->WorldPos.Y;
new_pos.Z+=thing->WorldPos.Z;
new_pyro=PYRO_create(new_pos,PYRO_FLICKER);
if(new_pyro)
{
PYRO_fn_init(new_pyro);
new_pyro->Genus.Pyro->radii[0]=255;
new_pyro->Genus.Pyro->radii[1]=512;
angle=-Arctan(posn.X,posn.Z);
new_pyro->Genus.Pyro->radii[2]=angle&2047;
}
}
break;
case PYRO_FIREPOOL:
if (pyro->counter<254) pyro->counter+=2;
break;
case PYRO_BONFIRE:
// do some dynamic light stuff
if((((THING_NUMBER(thing))+GAME_TURN)&7)==0)
MFX_play_thing(99,S_FIRE,MFX_LOOPED|MFX_QUEUED,thing); //mikeD sort of 3d sound
if (pyro->dlight) {
SWORD max=pyro->Dummy;
if (max>0x7f)
max+=(Random()&0x3f)-0x1f;
else
max+=Random()&0x3f;
SATURATE(max,0,0xff);
pyro->Dummy=max;
NIGHT_dlight_colour(pyro->dlight,max,max-(Random()&0xf),0);
}
break;
case PYRO_IMMOLATE:
if (pyro->counter<255) pyro->counter++;
if (pyro->victim) {
new_pos = pyro->victim->WorldPos;
move_thing_on_map(thing, &new_pos);
if (pyro->victim->Class==CLASS_BAT) {
UBYTE p,q,r;
SLONG px,py,pz;
#ifndef PSX
r=pyro->radius>>6;
r=(r<5)?5-r:1;
for (p=0;p<r;p++)
{
q=Random()%15;
calc_sub_objects_position(
pyro->victim,
pyro->victim->Draw.Tweened->AnimTween,
q,
&px,
&py,
&pz);
px<<=8; py<<=8; pz<<=8;
px += pyro->victim->WorldPos.X;
py += pyro->victim->WorldPos.Y;
pz += pyro->victim->WorldPos.Z;
PARTICLE_Add(px,py,pz,
256+(Random()&0xff),256+(Random()&0xff),256+(Random()&0xff),
POLY_PAGE_FLAMES2,2+((Random()&3)<<2),0x7FFFFFFF,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FIRE|PFLAG_FADE|PFLAG_RESIZE,
300,90,1,4,-1);
}
#endif
}
if (pyro->victim->Class==CLASS_PERSON) {
UBYTE p,q,r;
SLONG px,py,pz;
#ifndef PSX
r=pyro->radius>>6;
r=(r<5)?5-r:1;
for (p=0;p<r;p++)
{
q=Random()%15;
calc_sub_objects_position(
pyro->victim,
pyro->victim->Draw.Tweened->AnimTween,
q,
&px,
&py,
&pz);
px<<=8; py<<=8; pz<<=8;
px += pyro->victim->WorldPos.X;
py += pyro->victim->WorldPos.Y;
pz += pyro->victim->WorldPos.Z;
/*PARTICLE_Add(px,py,pz,0,512,0,POLY_PAGE_FLAMES2,2+((Random()&3)<<2),0x00FFFFFF,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE2|PFLAG_RESIZE,
300,50,1,1,1);*/
PARTICLE_Add(px,py,pz,
256+(Random()&0xff),256+(Random()&0xff),256+(Random()&0xff),
POLY_PAGE_FLAMES2,2+((Random()&3)<<2),0x7FFFFFFF,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FIRE|PFLAG_FADE|PFLAG_RESIZE,
300,40,1,3,-1);
}
#endif
if ((pyro->victim->State!=STATE_DEAD)&&(pyro->victim->State!=STATE_DYING)&&(pyro->radius<290))
{
if ((pyro->victim->Genus.Person->Flags2 & FLAG2_PERSON_INVULNERABLE) ||
(pyro->victim->Genus.Person->pcom_bent & PCOM_BENT_PLAYERKILL))
{
//
// Nothing hurts this person.
//
}
else
{
pyro->victim->Genus.Person->Health -= 10;
//TRACE("person's health going down due to immolation\n");
if(pyro->victim->Genus.Person->Health<=0)
{
//
// The person has died.
//
pyro->victim->Genus.Person->Health = 0;
set_person_dead(
pyro->victim,
NULL,
PERSON_DEATH_TYPE_OTHER,
FALSE,
0);
}
}
}
else
{
#ifndef PSX
if (!pyro->Dummy) {
RIBBON_life(pyro->radii[0],30);
RIBBON_life(pyro->radii[1],50);
pyro->Dummy=2;
pyro->radius=0;
} else {
pyro->radius++;
switch(pyro->radius) {
case 30:
pyro->radii[2]=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,7,POLY_PAGE_FLAMES3,100,7,5+(rand()&7));
break;
case 100:
pyro->radii[3]=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,5,POLY_PAGE_FLAMES3,90,5,5+(rand()&7));
break;
case 150:
pyro->radii[4]=RIBBON_alloc(RIBBON_FLAG_CONVECT|RIBBON_FLAG_FADE|RIBBON_FLAG_SLIDE,3,POLY_PAGE_FLAMES3,90,3,5+(rand()&7));
break;
//case 350:
// free_pyro(thing);
}
if ((pyro->radius>350)&&(!pyro->victim->Genus.Person->BurnIndex))
free_pyro(thing);
else
if (!(pyro->victim->Flags&FLAGS_ON_MAPWHO)) free_pyro(thing);
}
#endif
}
}
}
break;
case PYRO_DUSTWAVE:
//BARREL_hit_with_sphere(pyro->thing->WorldPos.X>>8,pyro->thing->WorldPos.Y>>8,pyro->thing->WorldPos.Z>>8,pyro->counter*3);
if (pyro->counter<120)
pyro->counter+=8;
else
free_pyro(thing);
break;
case PYRO_EXPLODE2:
if (pyro->counter<249) {
pyro->counter+=7;
pyro->radius+=9;
if (pyro->dlight) {
SLONG rgb;
rgb=pyro->counter<<2;
if (rgb>512) rgb=512;
rgb=COS(rgb)>>10;
NIGHT_dlight_colour(pyro->dlight,rgb,rgb,rgb>>1);
}
for (i=0;i<4;i++)
pyro->radii[i]++;
} else
free_pyro(thing);
break;
case PYRO_STREAMER:
for (i=0;i<4;i++) {
pyro->radii[i]+=8;
}
if (pyro->counter==0) free_pyro(thing);
break;
case PYRO_TWANGER:
if (pyro->counter<240)
pyro->counter+=16;
else
free_pyro(thing);
break;
case PYRO_NEWDOME:
if (pyro->counter<249) {
if ((!pyro->counter)&&!(Random()&3))
{
if (Random()&1)
MFX_play_thing(THING_NUMBER(pyro->thing),S_BIG_BANG_START,MFX_OVERLAP,pyro->thing);
else
MFX_play_thing(THING_NUMBER(pyro->thing),S_FIREBALL,MFX_OVERLAP,pyro->thing);
}
pyro->radius=SIN(pyro->counter<<1)>>8;
pyro->radius=(pyro->radius*pyro->scale)>>9;
pyro->counter+=7;
for (i=0;i<4;i++)
pyro->radii[i]++;
} else free_pyro(thing);
break;
case PYRO_IRONICWATERFALL: // it's extra ironic now it does chimney smoke... :P
if (pyro->thing->Flags&FLAGS_IN_VIEW)
{
SLONG px,py,pz;
px=pyro->thing->WorldPos.X>>8;
py=pyro->thing->WorldPos.Y>>8;
pz=pyro->thing->WorldPos.Z>>8;
switch(pyro->radius) {
case 0: // water fountain
#ifndef PSX
DIRT_new_water(px + 2, py, pz, -1, 28, 0);
DIRT_new_water(px , py, pz + 2, 0, 29, -1);
DIRT_new_water(px , py, pz - 2, 0, 28, +1);
DIRT_new_water(px - 2, py, pz, +1, 29, 0);
#else
switch(GAME_TURN&3)
{
case 0:
DIRT_new_water(px + 2, py, pz, -1, 28, 0);
break;
case 1:
DIRT_new_water(px , py, pz + 2, 0, 29, -1);
break;
case 2:
DIRT_new_water(px , py, pz - 2, 0, 28, +1);
break;
case 3:
DIRT_new_water(px - 2, py, pz, +1, 29, 0);
break;
}
#endif
break;
case 1:
if (!(rand() & 0xff)) DIRT_new_water(px,py,pz,0,0,0);
break;
case 2: // smokestack
if (!(Random()&3))
PARTICLE_Add(pyro->thing->WorldPos.X,pyro->thing->WorldPos.Y,pyro->thing->WorldPos.Z,
256+(Random()&0xff),256+(Random()&0xff),256+(Random()&0xff),
POLY_PAGE_SMOKECLOUD,2+((Random()&3)<<2),0x7FFFFFFF,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE2|PFLAG_RESIZE,
300,60,1,1,1);
break;
case 3: // chimney
if (!(Random()&3))
PARTICLE_Add(pyro->thing->WorldPos.X,pyro->thing->WorldPos.Y,pyro->thing->WorldPos.Z,
256+(Random()&0xff),256+(Random()&0xff),256+(Random()&0xff),
POLY_PAGE_SMOKECLOUD2,2+((Random()&3)<<2),0x7FFFFFFF,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE2|PFLAG_RESIZE,
300,60,1,1,1);
break;
}
}
break;
case PYRO_FLICKER:
{
GameCoord pos;
SLONG dx,dz,nx,nz,cr,cosa,sina;
pyro->counter++;
if (pyro->Dummy&1)
pyro->radius+=pyro->scale;
else
pyro->radius-=pyro->scale;
pyro->scale+=pyro->tints[0];
if (pyro->scale<100) pyro->tints[0]+=4;
if (pyro->scale>300) pyro->tints[0]-=4;
if (pyro->tints[0]>40) pyro->tints[0]=40;
if (pyro->tints[0]<-40) pyro->tints[0]=-40;
dx=SIN(pyro->radius&2047)/8;
dz=COS(pyro->radius&2047)/8;
// pinch+twist code
if (pyro->radii[0]<256) {
// temp
dx*=pyro->radii[0];
dx/=256;
dz*=pyro->radii[1];
dz/=256;
cosa=COS(pyro->radii[2]&2047)/256;
sina=SIN(pyro->radii[2]&2047)/256;
nx= ((dx*cosa)/256)+((dz*sina)/256);
nz=-((dx*sina)/256)+((dz*cosa)/256);
dx=nx; dz=nz;
}
#ifndef PSX
pos=pyro->thing->WorldPos;
pos.X+=dx;
pos.Z+=dz;
RIBBON_extend(pyro->Dummy,pos.X>>8,pos.Y/256,pos.Z>>8);
pos=pyro->thing->WorldPos;
pos.X-=dx;
pos.Z-=dz;
RIBBON_extend(pyro->Dummy,pos.X>>8,pos.Y/256,pos.Z>>8);
#endif
}
break;
case PYRO_HITSPANG:
#ifdef PSX
if (pyro->counter<3)
#else
if (pyro->counter<5)
#endif
{
pyro->counter++;
}
else
{
global_spang_count--;
free_pyro(thing);
}
break;
case PYRO_GAMEOVER:
//TRACE("armageddon: %d\n",pyro->counter);
#ifndef PSX
if (GAMEMENU_is_paused()) break;
#else
if (GAME_FLAGS & GF_PAUSED) break;
#endif
if (!pyro->counter)
{
SLONG x,y,z;
for (i=0;i<8;i++)
{
x=SIN(i<<8); z=COS(i<<8);
x<<=2; z<<=2;
x+=pyro->thing->WorldPos.X;
y=pyro->thing->WorldPos.Y;
z+=pyro->thing->WorldPos.Z;
pyro->radii[i]=NIGHT_dlight_create(x>>8,(y>>8)+10,z>>8,200,1,1,1);
}
pyro->Dummy=0;
}
else
{
if ((pyro->counter>100)&&!pyro->Dummy)
{
pyro->Dummy=1;
MFX_play_thing(THING_NUMBER(pyro->thing),S_BIG_BANG_END,MFX_OVERLAP,pyro->thing);
}
for (i=0;i<8;i++)
{
UBYTE col = pyro->counter>>3;
UBYTE col2= Random()%(1+(col>>1));
NIGHT_dlight_colour(pyro->radii[i],col,col2,0);
}
}
#ifndef PSX
if (pyro->counter > 242)
{
GAME_STATE = GS_LEVEL_LOST;
if (NET_PERSON(0)->State != STATE_DYING &&
NET_PERSON(0)->State != STATE_DEAD)
{
knock_person_down(
NET_PERSON(0),
500,
NET_PERSON(0)->WorldPos.X + 0x1000 >> 8,
NET_PERSON(0)->WorldPos.Z + 0x1000 >> 8,
NULL);
}
}
if (pyro->counter<253)
pyro->counter+=3;
#else
if (pyro->counter>200) GAME_STATE = GS_LEVEL_LOST;
if (pyro->counter<254)
pyro->counter+=2;
#endif
else
{
for (i=0;i<8;i++)
{
NIGHT_dlight_destroy(pyro->radii[i]);
}
free_pyro(thing);
}
break;
case PYRO_FIREBOMB:
if (pyro->radii[0]<0xfe00) {
//pyro->counter++;
if (pyro->radii[0]>2560) pyro->radii[0]+=9*TICK_RATIO;
pyro->radii[0]+=TICK_RATIO;
if (pyro->radii[0]>0xff00) pyro->radii[0]=0xff00;
pyro->counter=pyro->radii[0]>>8;
} else free_pyro(thing);
break;
case PYRO_SPLATTERY:
if (pyro->counter<254) {
/* Thing *np = PYRO_create(thing->WorldPos,PYRO_ONESMOKE);
if (np) np->Genus.Pyro->target=thing->WorldPos; // original position for poss. light fx?*/
for (SLONG i=0;i<4;i++)
{
UBYTE sz;
SLONG dx, dr, dz,x,y,z,b,ox,oz;
sz=(255-pyro->counter)>>3;
dr=rand()&2047;
ox=SIN(dr)>>4;
oz=COS(dr)>>4;
PARTICLE_Add(pyro->thing->WorldPos.X,pyro->thing->WorldPos.Y,pyro->thing->WorldPos.Z,
ox,((Random()&0x1f)+0x1f)<<5,oz,
POLY_PAGE_SMOKECLOUD2,2+((Random()&3)<<2),0x7FFF0000,
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE|PFLAG_GRAVITY|PFLAG_RESIZE,130,30,1,10,-2);
dr=rand()&2047;
dx=((SIN(dr)>>8)*sz)>>8;
dz=((COS(dr)>>8)*sz)>>8;
if ((dx==0)&&(dz==0)) dz=1;
x=pyro->thing->WorldPos.X;
z=pyro->thing->WorldPos.Z;
x-=(dx/2); z-=(dz/2);
y=(PAP_calc_map_height_at(x>>8,z>>8)<<8)+257;
for (b=0;b<5;b++) {
dr=((SLONG)pyro)+b;
dr*=31415965;
dr+=123456789;
dr>>=8;
dr&=0xff;
ox=((SIN((b*340)+dr)>>8)*pyro->counter);
oz=((COS((b*340)+dr)>>8)*pyro->counter);
TRACKS_AddQuad(x+ox, y, z+oz, dx, 0, dz, POLY_PAGE_BLOODSPLAT, 0x00ffffff, sz, 0, 0);
}
pyro->counter+=18;
if (pyro->counter>254) break;
}
} else free_pyro(thing);
break;
case PYRO_WHOOMPH:
if (pyro->counter<255-16)
{
if (pyro->dlight)
{
SWORD max=SIN(((UWORD)pyro->counter)<<1)>>11;
NIGHT_dlight_colour(pyro->dlight,max,max>>1,max>>3);
}
pyro->counter+=16;
} else free_pyro(thing);
break;
}
}
void PYRO_fn_normal_ex(Thing *thing) {
Pyrex *pyro = (Pyrex*)PYRO_get_pyro(thing);
UBYTE i,dead=0;
for (i=0;i<17;i++) {
pyro->points[i].radius+=pyro->points[i].delta;
if (pyro->points[i].delta>20) pyro->points[i].delta--;
if (pyro->points[i].delta>-20) pyro->points[i].delta--;
pyro->points[i].delta--;
if (pyro->points[i].radius<0) dead=1;
}
thing->Genus.Pyro->Timer1+=7;
if (dead||(thing->Genus.Pyro->Timer1>=255)) free_pyro(thing);
}
// --------- interface thingy ---------
void PYRO_construct(GameCoord posn, SLONG types, SLONG scale) {
Thing *thing;
if (types & 1) {
thing=PYRO_create(posn,PYRO_TWANGER);
if (thing) thing->Genus.Pyro->scale=scale;
}
if (types & 2) {
thing=PYRO_create(posn,PYRO_EXPLODE2);
if (thing) {
SLONG cx,cz;
thing->Genus.Pyro->scale=scale;
// do initial blast gust
cx=thing->WorldPos.X>>8;
cz=thing->WorldPos.Z>>8;
DIRT_gust(thing,cx,cz,cx+scale,cz);
DIRT_gust(thing,cx+scale,cz,cx,cz);
// and explode fx
// play_quick_wave(thing,S_EXPLODE_START+(rand()%3),0);
}
}
if (types & 4) {
thing=PYRO_create(posn,PYRO_DUSTWAVE);
if (thing) thing->Genus.Pyro->scale=scale;
// BARREL_hit_with_sphere(thing->WorldPos.X>>8,thing->WorldPos.Y>>8,thing->WorldPos.Z>>8,512);
}
if (types & 8) {
thing=PYRO_create(posn,PYRO_STREAMER);
if (thing) thing->Genus.Pyro->scale=scale; // no effect
}
if (types & 16) {
thing=PYRO_create(posn,PYRO_BONFIRE);
}
if (types & 32) {
thing=PYRO_create(posn,PYRO_NEWDOME);
if (thing) {
SLONG cx,cz;
thing->Genus.Pyro->scale=scale;
// do initial blast gust
cx=thing->WorldPos.X>>8;
cz=thing->WorldPos.Z>>8;
DIRT_gust(thing,cx,cz,cx+scale,cz);
DIRT_gust(thing,cx+scale,cz,cx,cz);
// and explode fx
// play_quick_wave(thing,S_EXPLODE_START+(rand()%3),0);
}
}
if (types & 64) {
thing=PYRO_create(posn,PYRO_FIREBOMB);
}
if (types & 128) {
thing=PYRO_create(posn,PYRO_FIREBOMB);
if (thing)
thing->Genus.Pyro->Flags|=PYRO_FLAGS_WAVE;
}
}
void PYRO_hitspang(Thing *p_person, Thing *victim) {
GameCoord vec;
Thing *thing;
ASSERT(global_spang_count>=0);
if(global_spang_count>5)
return;
calc_sub_objects_position(
p_person,
p_person->Draw.Tweened->AnimTween,
SUB_OBJECT_LEFT_HAND,
&vec.X,
&vec.Y,
&vec.Z);
vec.X<<=8; vec.Y<<=8; vec.Z<<=8;
vec.X+=p_person->WorldPos.X;
vec.Y+=p_person->WorldPos.Y;
vec.Z+=p_person->WorldPos.Z;
thing=PYRO_create(vec,PYRO_HITSPANG);
if (!thing)
return;
thing->Genus.Pyro->victim=victim;
thing->Genus.Pyro->Dummy=1; // arbitrarily using this to indicate 'person' target
PYRO_fn_init(thing); //do this now so it has definatly been done by the time we call the draw routine
global_spang_count++;
}
void PYRO_hitspang(Thing *p_person, SLONG x, SLONG y, SLONG z) {
GameCoord vec;
Thing *thing;
ASSERT(global_spang_count>=0);
if(global_spang_count>5)
return;
calc_sub_objects_position(
p_person,
p_person->Draw.Tweened->AnimTween,
SUB_OBJECT_LEFT_HAND,
&vec.X,
&vec.Y,
&vec.Z);
vec.X<<=8; vec.Y<<=8; vec.Z<<=8;
vec.X+=p_person->WorldPos.X;
vec.Y+=p_person->WorldPos.Y;
vec.Z+=p_person->WorldPos.Z;
thing=PYRO_create(vec,PYRO_HITSPANG);
if (!thing) return;
thing->Genus.Pyro->target.X=x;
thing->Genus.Pyro->target.Y=y;
thing->Genus.Pyro->target.Z=z;
thing->Genus.Pyro->Dummy=0; // arbitrarily using this to indicate 'vector' target
DIRT_new_sparks(x>>8,y>>8,z>>8,2);
PYRO_fn_init(thing); //do this now so it has definatly been done by the time we call the draw routine
global_spang_count++;
}