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

666 lines
16 KiB
C++

//
// playcuts.cpp
// matthew rosenfeld 30 june 99
//
// plays back cutscenes made with cutscene.cpp in gedit
//
#include "playcuts.h"
#include "Game.h"
#include "person.h"
#include "psystem.h"
#include "ribbon.h"
#include "dirt.h"
#include "wmove.h"
#include "balloon.h"
#include "pow.h"
#include "puddle.h"
#include "drip.h"
#include "mfx.h"
#include "fc.h"
#include "poly.h"
#include "sound.h"
#include "grenade.h"
#ifndef PSX
#include "polypage.h"
#include "drawxtra.h"
#include "font2d.h"
#endif
//----------------------------------------------------------------------------
// DEFINES
//
// misc
#define SLOMO_RATE (10)
#define LERP_SHIFT (8)
// Channel Types
#define CT_CHAR (1)
#define CT_CAM (2)
#define CT_WAVE (3)
//#define CT_FX (4) // not used
#define CT_TEXT (5)
// Packet Types
#define PT_ANIM (1)
//#define PT_ACTION (2) // not used
#define PT_WAVE (3)
#define PT_CAM (4)
#define PT_TEXT (5)
// Packet Flags
#define PF_BACKWARDS (1) // for anims
#define PF_SECURICAM (1) // for cameras
#define PF_INTERPOLATE_MOVE (2) // for both
#define PF_SMOOTH_MOVE_IN (4)
#define PF_SMOOTH_MOVE_OUT (8)
#define PF_SMOOTH_MOVE_BOTH (PF_SMOOTH_MOVE_IN|PF_SMOOTH_MOVE_OUT)
#define PF_INTERPOLATE_ROT (16)
#define PF_SMOOTH_ROT_IN (32)
#define PF_SMOOTH_ROT_OUT (64)
#define PF_SMOOTH_ROT_BOTH (PF_SMOOTH_ROT_IN|PF_SMOOTH_ROT_OUT)
#define PF_SLOMO (128)
//----------------------------------------------------------------------------
// EXTERNAL REFERENCES
//
extern SLONG hardware_input_continue(void);
inline void screen_flip(void)
{
#ifdef PSX
//
// sCREENSHOT just before screen_flip
//
extern void DoFigureDraw(void);
DoFigureDraw();
#ifndef FS_ISO9660
extern void AENG_screen_shot(SLONG width);
if (Keys[KB_S])
{
AENG_screen_shot(320);
Keys[KB_S]=0;
}
#endif
#endif
//
// Always flip on the PSX.
//
AENG_flip();
}
extern void lock_frame_rate(SLONG fps);
extern SLONG person_normal_animate(Thing *p_person);
typedef Thing* ThingPtr;
//----------------------------------------------------------------------------
// GLOBALS
//
CBYTE *text_disp=0;
UBYTE PLAYCUTS_fade_level=255;
BOOL PLAYCUTS_slomo=0;
BOOL PLAYCUTS_playing=0;
UBYTE PLAYCUTS_slomo_ctr=0;
UBYTE no_more_packets=0;
//----------------------------------------------------------------------------
// MAJOR CONSTRUCTS
//
#ifndef PSX
CBYTE PLAYCUTS_text_data[MAX_CUTSCENE_TEXT];
CBYTE *PLAYCUTS_text_ptr=PLAYCUTS_text_data;
CPData PLAYCUTS_cutscenes[MAX_CUTSCENES];
CPPacket PLAYCUTS_packets[MAX_CUTSCENE_PACKETS];
CPChannel PLAYCUTS_tracks[MAX_CUTSCENE_TRACKS];
#else
CBYTE *PLAYCUTS_text_data;
CPData *PLAYCUTS_cutscenes;
CPPacket *PLAYCUTS_packets;
CPChannel *PLAYCUTS_tracks;
#endif
UWORD PLAYCUTS_cutscene_ctr=0;
UWORD PLAYCUTS_packet_ctr=0;
UWORD PLAYCUTS_track_ctr=0;
UWORD PLAYCUTS_text_ctr=0;
//----------------------------------------------------------------------------
// MISC USEFUL FUNCTIONS
//
inline SLONG LERPValue(SLONG a, SLONG b, SLONG m) {
return a+(((b-a)*m)>>LERP_SHIFT);
}
inline int LERPAngle(SLONG a, SLONG b, SLONG m) {
if ((a-b)>1024) b+=2048;
if ((b-a)>1024) a+=2048;
return LERPValue(a,b,m);
}
#ifndef PSX
//----------------------------------------------------------------------------
// CUTSCENE SUPPORT CODE
//
// Reading cutscenes and the bits that make them up from disk
void PLAYCUTS_Read_Packet(MFFileHandle handle, CPPacket *packet) {
FileRead(handle, packet, sizeof(CPPacket));
switch(packet->type) {
case PT_TEXT:
if (packet->pos.X) {
SLONG l;
FileRead(handle,&l,sizeof(l));
FileRead(handle,PLAYCUTS_text_ptr,l);
packet->pos.X=(SLONG)PLAYCUTS_text_ptr;
PLAYCUTS_text_ptr+=l;
*PLAYCUTS_text_ptr=0; PLAYCUTS_text_ptr++;
PLAYCUTS_text_ctr+=(l+1);
}
break;
}
}
void PLAYCUTS_Read_Channel(MFFileHandle handle, CPChannel *channel) {
SLONG packnum;
FileRead(handle, channel, sizeof(CPChannel));
// channel->packets = new CPPacket[channel->packetcount];
channel->packets = PLAYCUTS_packets+PLAYCUTS_packet_ctr;
PLAYCUTS_packet_ctr+=channel->packetcount;
for (packnum=0;packnum<channel->packetcount;packnum++)
PLAYCUTS_Read_Packet(handle, channel->packets+packnum);
}
CPData* PLAYCUTS_Read(MFFileHandle handle) {
UBYTE channum;
CPData* cutscene;
// cutscene = new CPData;
cutscene = PLAYCUTS_cutscenes+PLAYCUTS_cutscene_ctr;
PLAYCUTS_cutscene_ctr++;
cutscene->channels=0;
FileRead(handle,&cutscene->version,1);
FileRead(handle,&cutscene->channelcount,1);
// cutscene->channels = new CPChannel[cutscene->channelcount];
cutscene->channels = PLAYCUTS_tracks + PLAYCUTS_track_ctr;
PLAYCUTS_track_ctr+=cutscene->channelcount;
for (channum=0;channum<cutscene->channelcount;channum++)
PLAYCUTS_Read_Channel(handle,cutscene->channels+channum);
return cutscene;
}
// Freeing finished-with cutscenes
void PLAYCUTS_Free_Chan(CPChannel *chan) {
// we could free stuff on a packet by packet basis...
// ... but right now, there's nothing to free except text messages...
// ... and they get dealt with seperately
//we don't even do this now we have them pseudostatically allocated...
//delete [] chan->packets;
}
void PLAYCUTS_Free(CPData *cutscene) {
int chan;
// for (chan=0;chan<cutscene->channelcount;chan++) PLAYCUTS_Free_Chan(cutscene->channels+chan);
// delete [] cutscene->channels;
// delete cutscene;
}
void PLAYCUTS_Reset() {
PLAYCUTS_cutscene_ctr=0;
PLAYCUTS_track_ctr=0;
PLAYCUTS_packet_ctr=0;
}
#endif
// finds the packet that matches a cell
CPPacket *PLAYCUTS_Get_Packet(CPChannel* chan, SLONG cell) {
CPPacket* pkt=chan->packets;
SLONG ctr=1;
if ((cell<0)||(cell>2000)) return 0;
while (pkt&&(pkt->start!=cell)) {
pkt++;
ctr++;
if (ctr>chan->packetcount) pkt=0;
}
return pkt;
}
// finds the packets to the left and right of a cell
BOOL PLAYCUTS_find_surrounding_packets(CPChannel *chan, SLONG cell, SLONG* left, SLONG* right) {
SLONG leftmax=-1, rightmin=2001, packctr=0;
CPPacket *pack;
*left=-1; *right=2001;
pack=chan->packets;
for (packctr=0;packctr<chan->packetcount;packctr++,pack++) {
if ((pack->start<=cell)&&(pack->start>leftmax)) {
leftmax=pack->start; *left=packctr;
}
if ((pack->start>=cell)&&(pack->start<rightmin)) {
rightmin=pack->start; *right=packctr;
}
}
return ((leftmax>-1)&&(rightmin<2000));
}
//----------------------------------------------------------------------------
// PLAYING THE DAMN THINGS
//
void LERPAnim(CPChannel *chan, Thing *person, SLONG cell, SLONG sub_ctr) {
SLONG left,right,lastpos,pos,dist, a, mult, smoothmult, smoothin, smoothout;
int x,y,z;
CPPacket *pktA, *pktB;
GameCoord posn;
PLAYCUTS_find_surrounding_packets(chan,cell,&left,&right);
if (right==2001) no_more_packets++;
if (left<0) return;
pktA=chan->packets+left;
pos=cell-pktA->start;
// interpolation
if ((right<2001)&&(right!=left)) {
pktB=chan->packets+right;
dist=pktB->start-pktA->start;
mult=(pos<<LERP_SHIFT);
if (PLAYCUTS_slomo) {
// mult+=((SLOMO_RATE-CUTSCENE_slomo_ctr)<<LERP_SHIFT)/SLOMO_RATE;
mult+=(PLAYCUTS_slomo_ctr<<LERP_SHIFT)/SLOMO_RATE;
}
mult/=dist;
if (pktA->flags&(PF_SMOOTH_MOVE_BOTH|PF_SMOOTH_ROT_BOTH)) {
// this little fiddle should smooth in/out the edges
smoothmult=(mult-128)*4;
smoothmult=(SIN(smoothmult&2047)>>8)+256;
smoothmult>>=1;
smoothin=(mult-256)*2;
smoothin=256+(SIN(smoothin&2047)>>8);
smoothout=SIN(mult*2)>>8;
}
// motion:
if (pktA->flags&PF_INTERPOLATE_MOVE) {
if ((pktA->flags&PF_SMOOTH_MOVE_BOTH)==PF_SMOOTH_MOVE_BOTH) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothmult);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothmult);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothmult);
} else
if (pktA->flags&PF_SMOOTH_MOVE_IN) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothin);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothin);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothin);
} else
if (pktA->flags&PF_SMOOTH_MOVE_OUT) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothout);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothout);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothout);
} else { // linear
x=LERPValue(pktA->pos.X,pktB->pos.X,mult);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,mult);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,mult);
}
} else {
x=pktA->pos.X;
y=pktA->pos.Y;
z=pktA->pos.Z;
}
// er... later.
// rotation:
if (pktA->flags&PF_INTERPOLATE_ROT) {
if ((pktA->flags&PF_SMOOTH_ROT_BOTH)==PF_SMOOTH_ROT_BOTH) {
mult=smoothmult;
} else
if (pktA->flags&PF_SMOOTH_ROT_IN) {
mult=smoothin;
} else
if (pktA->flags&PF_SMOOTH_ROT_OUT) {
mult=smoothout;
} // else mult is already correct for linear
a=LERPAngle(pktA->angle,pktB->angle,mult);
} else {
a=pktA->angle;
}
person->Draw.Tweened->Angle=a;
posn.X=x; posn.Y=y; posn.Z=z;
move_thing_on_map(person,&posn);
}
set_anim(person,pktA->index);
if (pktA->flags&1) // reverse
pos=(pktA->length-pos)-1;
if (pos<=0) return;
TICK_RATIO=pos<<TICK_SHIFT;
if (PLAYCUTS_slomo) {
// TICK_RATIO+=((SLOMO_RATE-CUTSCENE_slomo_ctr)<<TICK_SHIFT)/SLOMO_RATE;
TICK_RATIO+=(PLAYCUTS_slomo_ctr<<TICK_SHIFT)/SLOMO_RATE;
}
person_normal_animate(person);
}
void LERPCamera(CPChannel *chan, SLONG cell, SLONG sub_ctr) {
SLONG x,y,z,p,a, left, right, dist, pos, mult, smoothin, smoothout, smoothmult,lens;
CPPacket *pktA,*pktB;
if (!PLAYCUTS_find_surrounding_packets(chan, cell,&left,&right)) {
if (right==2001) no_more_packets++;
return;
}
pktA=chan->packets+left;
pktB=chan->packets+right;
dist=pktB->start-pktA->start;
pos=cell-pktA->start;
mult=(pos*256);
if (PLAYCUTS_slomo) {
// mult+=((SLOMO_RATE-CUTSCENE_slomo_ctr)<<LERP_SHIFT)/SLOMO_RATE;
mult+=(PLAYCUTS_slomo_ctr<<LERP_SHIFT)/SLOMO_RATE;
}
mult/=dist;
if (pktA->flags&(PF_SMOOTH_MOVE_BOTH|PF_SMOOTH_ROT_BOTH)) {
// this little fiddle should smooth in/out the edges
smoothmult=(mult-128)*4;
smoothmult=(SIN(smoothmult&2047)>>8)+256;
smoothmult>>=1;
smoothin=(mult-256)*2;
smoothin=256+(SIN(smoothin&2047)>>8);
smoothout=SIN(mult*2)>>8;
}
if (pktA->flags&PF_INTERPOLATE_MOVE) {
if ((pktA->flags&PF_SMOOTH_MOVE_BOTH)==PF_SMOOTH_MOVE_BOTH) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothmult);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothmult);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothmult);
lens=LERPValue(pktA->length&0xff,pktB->length&0xff,smoothmult);
} else
if (pktA->flags&PF_SMOOTH_MOVE_IN) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothin);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothin);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothin);
lens=LERPValue(pktA->length&0xff,pktB->length&0xff,smoothin);
} else
if (pktA->flags&PF_SMOOTH_MOVE_OUT) {
x=LERPValue(pktA->pos.X,pktB->pos.X,smoothout);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,smoothout);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,smoothout);
lens=LERPValue(pktA->length&0xff,pktB->length&0xff,smoothout);
} else { // linear
x=LERPValue(pktA->pos.X,pktB->pos.X,mult);
y=LERPValue(pktA->pos.Y,pktB->pos.Y,mult);
z=LERPValue(pktA->pos.Z,pktB->pos.Z,mult);
lens=LERPValue(pktA->length&0xff,pktB->length&0xff,mult);
}
} else { // snap-to
x=pktA->pos.X;
y=pktA->pos.Y;
z=pktA->pos.Z;
lens=pktA->length&0xff;
}
lens=((lens-0x7f)*1.5f)+0xff;
FC_cam[0].lens=(lens*0x24000)>>8;
if (pktA->flags&PF_INTERPOLATE_ROT) {
if ((pktA->flags&PF_SMOOTH_ROT_BOTH)==PF_SMOOTH_ROT_BOTH) {
mult=smoothmult;
} else
if (pktA->flags&PF_SMOOTH_ROT_IN) {
mult=smoothin;
} else
if (pktA->flags&PF_SMOOTH_ROT_OUT) {
mult=smoothout;
} // else mult is already correct for linear
p=LERPAngle(pktA->pitch,pktB->pitch,mult);
a=LERPAngle(pktA->angle,pktB->angle,mult);
} else {
p=pktA->pitch;
a=pktA->angle;
}
// AENG_set_camera(x,y,z,(-p)&2047,(-a)&2047,0);
AENG_set_camera(x,y,z,(a)&2047,(p)&2047,0);
PLAYCUTS_fade_level=LERPValue(pktA->length>>8,pktB->length>>8,mult);
}
// update an individual actor
void PLAYCUTS_Update(CPChannel *chan, Thing *thing, SLONG read_head, SLONG sub_ctr) {
CPPacket *pkt;
int index,lens;
SLONG l,r;
pkt=PLAYCUTS_Get_Packet(chan,read_head);
if (!pkt) {
switch(chan->type) {
case CT_CHAR:
LERPAnim(chan,thing,read_head, sub_ctr);
break;
case CT_CAM:
LERPCamera(chan,read_head,sub_ctr);
break;
default:
// check for end-of-track
PLAYCUTS_find_surrounding_packets(chan, read_head, &l, &r);
if (r==2001) no_more_packets++;
break;
}
return;
}
switch(pkt->type) {
case PT_ANIM:
index=pkt->index;
while (index>1023) { index-=1024; }
if (thing) {
move_thing_on_map(thing,&pkt->pos);
thing->Draw.Tweened->Angle=pkt->angle;
set_anim(thing,index);
if (pkt->flags&1) LERPAnim(chan,thing,read_head,sub_ctr); // should handle the reversing
}
break;
case PT_CAM:
// AENG_set_camera(pkt->pos.X,pkt->pos.Y,pkt->pos.Z,pkt->pitch&2047,pkt->angle&2047,0);
AENG_set_camera(pkt->pos.X,pkt->pos.Y,pkt->pos.Z,pkt->angle&2047,pkt->pitch&2047,0);
lens=((pkt->length&0xff)-0x7f);
lens=(lens*1.5f)+0xff;
FC_cam[0].lens=(lens*0x24000)>>8;
#ifndef PSX
PolyPage::SetGreenScreen(pkt->flags&PF_SECURICAM);
#endif
PLAYCUTS_slomo=pkt->flags&PF_SLOMO;
PLAYCUTS_fade_level=pkt->length>>8;
break;
case PT_WAVE:
MFX_play_xyz(MUSIC_REF+10,pkt->index-2048,0,pkt->pos.X<<8,pkt->pos.Y<<8,pkt->pos.Z<<8);
break;
case PT_TEXT:
if (pkt->pos.X)
text_disp=(CBYTE*)pkt->pos.X;
else
text_disp=0;
break;
}
}
// main loop
void PLAYCUTS_Play(CPData *cutscene) {
UBYTE env_frame_rate = 20;
SLONG read_head=0, sub_ctr=0;
UBYTE channum;
ThingPtr *cs_things;
Thing* darci=NET_PERSON(0);
CPChannel *chan;
// sensible defaults
text_disp=0;
PLAYCUTS_fade_level=255;
PLAYCUTS_slomo=0;
PLAYCUTS_slomo_ctr=0;
PLAYCUTS_playing=1;
cs_things = new ThingPtr[cutscene->channelcount];
for (channum=0,chan=cutscene->channels;channum<cutscene->channelcount;channum++,chan++)
{
if (chan->type==CT_CHAR) {
cs_things[channum]=TO_THING(
create_person(
chan->index-1,
0,
0,
0,
0
)
);
} else {
cs_things[channum]=0;
}
}
// already_warned_about_leaving_map = GetTickCount();
Keys[KB_SPACE]=0;
LastKey=0;
no_more_packets=0;
remove_thing_from_map(darci);
while(SHELL_ACTIVE&&(!Keys[KB_SPACE])&&(!no_more_packets)&&!hardware_input_continue())
{
// process some generic stuff. not all of it (yet...)
PARTICLE_Run();
//OB_process();
//TRIP_process();
//DOOR_process();
//EWAY_process();
#ifndef PSX
RIBBON_process();
DIRT_process();
ProcessGrenades();
#ifndef TARGET_DC
WMOVE_draw();
BALLOON_process();
#endif
//MAP_process();
POW_process();
#ifndef TARGET_DC
PUDDLE_process();
#endif
DRIP_process();
#endif
//FC_process(); // camera is overriden by funky one
// Update the cutscene's 'actors'
for (channum=0;channum<cutscene->channelcount;channum++)
{
chan=cutscene->channels+channum;
PLAYCUTS_Update(chan,cs_things[channum],read_head,sub_ctr);
}
if (no_more_packets<channum) no_more_packets=0;
MFX_set_listener(FC_cam[0].x,FC_cam[0].y,FC_cam[0].z,-(FC_cam[0].yaw>>8),-(1024),-(FC_cam[0].pitch>>8));
AENG_draw(FALSE);
if (text_disp) {
#ifndef PSX
POLY_frame_init(FALSE, FALSE);
#endif
FONT2D_DrawStringCentred(text_disp,320,400,0x7fffffff,256,POLY_PAGE_FONT2D);
#ifndef PSX
POLY_frame_draw(FALSE, FALSE);
#endif
}
if (PLAYCUTS_fade_level<255) {
#ifndef PSX
POLY_frame_init(FALSE, FALSE);
DRAW2D_Box(0, 0, 640, 480, (255-PLAYCUTS_fade_level)<<24, 1, 255);
POLY_frame_draw(FALSE, FALSE);
#endif
}
//draw_screen();
//
// Draw panel and other exciting things
//
// OVERLAY_handle(); prolly don't wanna do that for cutscenes
MFX_render();
screen_flip();
//
// Lock frame-rate to 30 FPS
//
lock_frame_rate(env_frame_rate);
GAME_TURN++;
if (PLAYCUTS_slomo) {
PLAYCUTS_slomo_ctr++;
if (PLAYCUTS_slomo_ctr==SLOMO_RATE) {
read_head++;
PLAYCUTS_slomo_ctr=0;
}
} else {
read_head++;
}
}
add_thing_to_map(darci);
Keys[KB_SPACE]=0;
// clean up afterwards
for (channum=0,chan=cutscene->channels;channum<cutscene->channelcount;channum++,chan++) {
if (cs_things[channum]) THING_kill(cs_things[channum]);
}
delete [] cs_things;
PLAYCUTS_playing=0;
}
//