www.pudn.com > nucsrc > DISP.C


/*============================================================================*/ 
/*   PROJECT NUCLEUS.      (c) RocSoft, 1995.                                 */ 
/*   v1.0  18 Jan 95.                                                         */ 
/*============================================================================*/ 
 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include "\rocco\glib\games.h" 
 
#include  
#include  
#include  
#include  
 
#include "defines.h" 
#include "prototyp.h" 
#include "globals.h" 
 
/*==================[ External Variable Definitions ]=========================*/ 
 
extern   char               *Banner, Demo_Mode, Demo_Exited; 
extern   int                ParticleMass[]; 
extern   int                LogoIndex1[], LogoIndex2[], Logo_Control[14]; 
extern   unsigned int       Demo_Timer, Experiment_Timer; 
extern   int                *RootTable; 
 
extern   BYTE               Screen_Shot; 
 
extern   HIGH_SCORE_TABLE   High_Scores; 
 
/*=======================[ Local Variable Definitions ]=======================*/ 
 
int                         Last_Mins; 
unsigned char               Neutron_Fired; 
unsigned char               Update_Score; 
POWER_UP_TYPE               Power_Up_Icon; 
int                         Power_Up_Delay; 
char                        *VictoryMsgs[]={ "Congratulations! You've successfully", 
                                             "completed all of your tasks. I'm afraid", 
                                             "you need to get another day job. Why", 
                                             "don't be become your OWN boss and", 
                                             "register! Upon registration you get", 
                                             "your very own level editor to create", 
                                             "loads and loads of different levels!", 
                                             NULL }; 
char                        *DeathMsgs[NUMBER_OF_MELTDOWNS]={ 
                                             "", 
                                             "Time Meltdown!", 
                                             "Mass Meltdown!", 
                                             "Rogue Ray Meltdown!", 
                                             "Rogue Particle Meltdown!" 
                                            }; 
 
/*===================[ Functions For Display Control ]========================*/ 
 
void Add_Particle_To_Nucleus( int nuc, int mol, int d ) 
{ 
    int        dx, dy, ac; 
    PARTICLE   *p=Cfg.particles+mol; 
    NUCLEUS    *n=Cfg.nuclei+nuc; 
 
    n->mass   += ParticleMass[p->type]; 
    p->active  = TRUE; 
    p->r       = (d<6) ? 3 : d>>1; 
    p->nucleus = nuc; 
    dx = p->x - n->cx; 
    dy = p->y - n->cy; 
    if ( d<2 ) ac=TRIG_TAB_FACTOR; else 
    { 
        ac = (dx*TRIG_TAB_FACTOR)/(d>>1); 
        ac = ABS(ac); 
        if ( ac > TRIG_TAB_FACTOR ) ac=TRIG_TAB_FACTOR; 
    } 
    if ( dx>=0 ) 
      p->angle = (dy<0) ? 360-PseudoACos(ac) : PseudoACos(ac); 
    else 
      p->angle = (dy<0) ? 180+PseudoACos(ac) : 180-PseudoACos(ac); 
} 
 
int  Calculate_Rogue_Interval( int mintime, int maxtime ) 
{ 
    return( (mintime || maxtime) ? ((random()%(maxtime-mintime+1))+mintime) : 0 ); 
} 
 
void Check_Beam_Collisions( void ) 
{ 
    BEAM      *b; 
    NUCLEUS   *n; 
    SHIELD    *s; 
    int       i, j, dx, dy; 
 
    for ( i=0, b=Cfg.beams; iactive && object_active(BEAM_TOON_BASE_PRI+i) ) 
        { 
            for ( j=0, s=Cfg.shields; jactive && object_active(SHIELD_TOON_BASE_PRI+j) ) 
                { 
                    dx = s->x - b->x + ((s->x>b->x)?3:0); 
                    dx = ABS(dx); 
                    dy = s->y - b->y; 
                    dy = ABS(dy); 
                    if ( dx<36 && dy<36 && RootTable[dx*dx+dy*dy]<=((s->rx+s->ry)>>1) ) 
                    { 
                        Sfx( SFX_SHIELD_HIT ); 
                        delete_object( BEAM_TOON_BASE_PRI+i ); 
                        b->active = FALSE; 
                        break; 
                    } 
                } 
            } 
            if ( b->active ) 
            { 
                for ( j=0, n=Cfg.nuclei; jmass ) 
                    { 
                        dx = n->cx - b->x + ((n->cx>b->x)?3:0); 
                        dx = ABS(dx); 
                        dy = n->cy - b->y; 
                        dy = ABS(dy); 
                        if ( dx<=NUCLEUS_HIT_TOLERANCE && dy<=NUCLEUS_HIT_TOLERANCE ) 
                        { 
                            Split_Nucleus( n,j,SFX_NUCLEUS_HIT ); 
                            delete_object( BEAM_TOON_BASE_PRI+i ); 
                            b->active = FALSE; 
                            Meltdown  = ROGUE_RAY_MELTDOWN; 
                            break; 
                        } 
                    } 
                } 
            } 
        } 
    } 
} 
 
void Check_Bonus_Pickup( int x, int y ) 
{ 
    OBJECT         *o=ObjectList[object_index(POWER_UP_TOON_BASE_PRI)]; 
 
    if ( o->status==ACTIVE_OBJECT && o->x1<=x && x<=o->x2 && o->y1<=y && y<=o->y2 ) 
    { 
        delete_object( POWER_UP_TOON_BASE_PRI ); 
        Sfx( SFX_BONUS_PICKUP ); 
        Cfg.score += PTS_POWER_UP; 
        Update_Score = TRUE; 
        switch( Power_Up_Icon ) 
        { 
            case PUP_NEUTRON: 
                if ( (Cfg.rays+=Lev->rays) > 99 ) Cfg.rays=99; 
                Display_Neutron(); 
                break; 
            case PUP_SHIELD: 
                if ( (Cfg.shield_cnt+=Lev->shields) > 99 ) Cfg.shield_cnt=99; 
                Display_Shields(); 
                break; 
        } 
    } 
} 
 
int  Create_Protective_Shield( int x, int y ) 
{ 
    SHIELD   *s; 
    int      i; 
 
    for ( i=0, s=Cfg.shields; iactive && !object_active(SHIELD_TOON_BASE_PRI+i) ) 
        { 
            s->active = TRUE; 
            s->rx     = Shield_Icons[0].w>>1; 
            s->ry     = Shield_Icons[0].h>>1; 
            s->reps   = s->frame = 0; 
            s->time   = Lev->shield_duration; 
            add_object( SHIELD_TOON_BASE_PRI+i,Toon_Shield,0,s->x=x,s->y=y ); 
            return( FALSE ); 
        } 
    } 
 
    return( TRUE ); 
} 
 
int  Create_Rogue_Beam( void ) 
{ 
    BEAM   *b; 
    int    i; 
 
    for ( i=0, b=Cfg.beams; iactive && !object_active(BEAM_TOON_BASE_PRI+i) ) 
        { 
            b->active = TRUE; 
            b->frame  = (random()&0x01) ? 0 : (NUM_OF_BEAMS-1); 
            b->x      = (b->frame==0) ? VX_MIN : VX_MAX; 
            b->y      = (random()%(VY_MAX-VY_MIN-10)) + VY_MIN; 
            b->vx     = (b->frame==0) ? 3 : -3; 
            add_object( BEAM_TOON_BASE_PRI+i,Toon_Beam,b->frame,b->x,b->y ); 
            return( TRUE ); 
        } 
    } 
 
    return( FALSE ); 
} 
 
int  Create_Rogue_Particle( void ) 
{ 
    NUCLEUS    *n; 
    PARTICLE   *p; 
    int        i, j, ok; 
 
    for ( i=0, p=Cfg.rogues; irogue && !object_active(ROGUE_TOON_BASE_PRI+i) ) 
        { 
            p->rogue  = TRUE; 
            p->delay  = p->frame  = 0; 
            p->time   = Lev->rogue_duration; 
            do 
            { 
                p->x  = (random()%(VX_MAX-VX_MIN+1)) + VX_MIN; 
                p->y  = (random()%(VY_MAX-VY_MIN+1)) + VY_MIN; 
                for ( j=0, ok=TRUE, n=Cfg.nuclei; ok && jcx-p->x)<(NUCLEUS_HIT_TOLERANCE<<2) && ABS(n->cy-p->y)<(NUCLEUS_HIT_TOLERANCE<<2) ) ok=FALSE; 
            } 
            while ( !ok ); 
            p->vx     = Set_Particle_Velocity( SPEED_MASK_2,2 ); 
            p->vy     = Set_Particle_Velocity( SPEED_MASK_2,2 ); 
            add_object( ROGUE_TOON_BASE_PRI+i,Toon_Rogue_Particle,0,p->x,p->y ); 
            return( TRUE ); 
        } 
    } 
 
    return( FALSE ); 
} 
 
void Cycle_Fractal_Colours( void ) 
{ 
    static   char  delay=0; 
    unsigned char  *buff=Pal.pal; 
 
    if ( ++delay > 6 ) 
    { 
        delay = 0; 
        _asm 
        { 
            pushf 
            push   ds 
            push   si 
            push   di 
            lds    si, buff 
            add    si, FRACTAL_CYCLE_COLOUR_1 
            mov    ax, ds 
            mov    es, ax 
            mov    di, si 
            cld                      /*  Both es:[di] & ds:[si] point to palette start  */ 
            lodsw                    /*  AX holds   G:R colour components  */ 
            mov    bx, ax            /*  Stash in bx  */ 
            lodsb                    /*  AL hold B component  */ 
            mov    dl, al            /*  Stash in dl  */ 
            mov    cx, FRACTAL_CYCLE_COUNT 
            shr    cx, 1 
            rep    movsw             /*  Move even number of words  */ 
            mov    ax, bx            /*  es:[di] should point to last colour  */ 
            stosw 
            mov    al, dl 
            stosb 
            pop    di 
            pop    si 
            pop    ds 
            popf 
        } 
    } 
} 
 
void Detect_Particle_Collisions( void ) 
{ 
    int           i, j, k, dx, dy, d, min; 
    unsigned int  adx, ady; 
    PARTICLE      *p, *pp; 
    NUCLEUS       *n; 
    SPRITE        *s1, *s2; 
 
    for ( i=0, n=Cfg.nuclei; icx-NUCLEUS_HIT_TOLERANCE<=MouseX && MouseX<=n->cx+NUCLEUS_HIT_TOLERANCE && n->cy-NUCLEUS_HIT_TOLERANCE<=MouseY && MouseY<=n->cy+NUCLEUS_HIT_TOLERANCE ) 
            { 
                Split_Nucleus( n,i,SFX_NUCLEUS_HIT ); 
                Cfg.score += PTS_NUCLEUS_HIT; 
                Update_Score = TRUE; 
            } 
            else 
            { 
                if ( n->mass >= Lev->critical_mass || n->cframe<=0 ) 
                { 
                    if ( n->mass >= Lev->critical_mass && Lev->mass_meltdown ) Meltdown=MASS_MELTDOWN; 
                    Split_Nucleus( n,i,SFX_NUCLEUS_SPLIT ); 
                } 
                else 
                { 
                    if ( Lev->rogue_particles ) 
                    { 
                        for ( j=0, p=Cfg.rogues; jrogue && object_active(ROGUE_TOON_BASE_PRI+j) ) 
                            { 
                                if ( ABS(n->cx-p->x)cy-p->y)count; i++, p++ ) 
    { 
        if ( !p->active && ABS(p->vx)<=MIN_SPEED && ABS(p->vy)<=MIN_SPEED ) 
        { 
            s1 = SParticles+p->type; 
            for ( j=0, pp=Cfg.particles; jcount && !p->active; j++, pp++ ) 
            { 
                if ( j!=i && ABS(pp->vx)<=MIN_SPEED && ABS(pp->vy)<=MIN_SPEED ) 
                { 
                    s2 = SParticles+pp->type; 
                    if ( pp->active ) 
                    { 
                        min = (s1->w+s1->h)>>2; 
                        dx  = Cfg.nuclei[pp->nucleus].cx; 
                        dy  = Cfg.nuclei[pp->nucleus].cy; 
                    } 
                    else 
                    { 
                        min = (s2->w+s2->h+s1->w+s1->h)>>2; 
                        dx  = pp->x; 
                        dy  = pp->y; 
                    } 
                    dx  -= p->x; 
                    dy  -= p->y; 
                    if ( (adx=ABS(dx))<=min && (ady=ABS(dy))<=min ) 
                    { 
                        if ( (d=RootTable[adx*adx+ady*ady])<=min ) 
                        { 
                            if ( pp->active ) 
                              Add_Particle_To_Nucleus( pp->nucleus,i,d<<1 ); 
                            else 
                            { 
                                for ( k=0, n=Cfg.nuclei; (n->mass>0 || object_active(COUNTER_TOON_BASE_PRI+k)) && kcx   = p->x + dx/2; 
                                    n->cy   = p->y + dy/2; 
                                    n->speed= ANGLE_START; 
                                    n->cframe = Lev->critical_time; 
                                    n->n_fired= n->s_fired=FALSE; 
                                    if ( add_object(COUNTER_TOON_BASE_PRI+k,Toon_Counter,n->cframe,n->cx-3,n->cy-2)==0 ) 
                                    { 
                                        Add_Particle_To_Nucleus( k,j,d ); 
                                        Add_Particle_To_Nucleus( k,i,d ); 
                                    } 
                                } 
                            } 
                        } 
                    } 
                } 
            } 
        } 
    } 
} 
 
void Display_Neutron( void ) 
{ 
    rect( PANEL_XBASE1+63,PANEL_YBASE+12,PANEL_XBASE1+78,PANEL_YBASE+19,CONTROL_PANEL_COLOUR,FILL,SPLIT_PAGE ); 
    Spr_Printi( SFont,Cfg.rays,PANEL_XBASE1+63,PANEL_YBASE+12,2,8,HORIZ,SPLIT_PAGE ); 
} 
 
void Display_Panel( void ) 
{ 
    int       i, j; 
 
    Fade_Screen( MODE_320x200x256 ); 
    splitscr( VS.my-SPLIT_SCREEN_HEIGHT+1 ); 
    imgvwport( VX_MIN,VY_MIN,VX_MAX,VY_MAX ); 
    mouselim( VX_MIN,VY_MIN,VX_MAX,VY_MAX ); 
 
    /*  Draw play area layout  */ 
 
    Draw_Fractal_Background( 0 ); 
 
    putspr( Panel,0,0,OWRTE,0 ); 
    putspr( Panel+2,VS.mx-7,0,OWRTE,0 ); 
    putspr( Panel+4,0,VS.my-7,OWRTE,0 ); 
    putspr( Panel+5,VS.mx-7,VS.my-7,OWRTE,0 ); 
 
    rect( 0,0,VS.mx,SPLIT_SCREEN_HEIGHT-1,CONTROL_PANEL_COLOUR,FILL,SPLIT_PAGE ); 
    putspr( Panel+6,0,0,OWRTE,SPLIT_PAGE ); 
    putspr( Panel+8,VS.mx-7,0,OWRTE,SPLIT_PAGE ); 
    putspr( Panel+11,0,SPLIT_SCREEN_HEIGHT-8,OWRTE,SPLIT_PAGE ); 
    putspr( Panel+13,VS.mx-7,SPLIT_SCREEN_HEIGHT-8,OWRTE,SPLIT_PAGE ); 
    for ( i=8; i=8 ) break; 
            if ( ++i>6 ) 
            { 
                add_object( 0,Toon_Nucleus,0,((VS.mx-Nucleus_Logo.w)>>1)-6,(VS.my-Nucleus_Logo.h)>>1 ); 
                Timers[5] = 72; 
            } 
            else 
            { 
                Logo_Control[i]=Logo_Control[i+7]=1; 
                Timers[5] = (i<6)?LOGO_DELAY:LOGO_DELAY*3; 
            } 
        } 
        if ( Demo_Exited ) break; 
    } 
    while ( uppercase((char)key)=='P' || !key ); 
 
    CONSUME_KEYS; 
    remove_objects(); 
} 
 
void Draw_Edged_Box( int x1, int y1, int x2, int y2, int sun, int shdw, int fill, int page ) 
{ 
    line( x1,y1,x2,y1,sun,page ); 
    line( x1,y1,x1,y2,sun,page ); 
    line( x1+1,y2,x2,y2,shdw,page ); 
    line( x2,y2,x2,y1+1,shdw,page ); 
    rect( x1+1,y1+1,x2-1,y2-1,fill,FILL,page ); 
    line( x1+4,y1+3,x2-4,y1+3,shdw,page ); 
    line( x1+4,y1+3,x1+4,y2-3,shdw,page ); 
    line( x1+5,y2-3,x2-4,y2-3,sun,page ); 
    line( x2-4,y2-3,x2-4,y1+4,sun,page ); 
} 
 
void Draw_Fractal_Background( int page ) 
{ 
    int   x, y; 
 
    for ( y=0; y<=VS.my; y+=Fractal.h ) 
      for ( x=0; x<=VS.mx; x+=Fractal.w ) 
        putspr( &Fractal,x,y,OWRTE,page ); 
} 
 
void End_Of_Level_Bonus( void ) 
{ 
    char  *title="Bonus!", *equals="=", *msgs[2]={ "End Of Level!",NULL }; 
    int   ht=SFont[' '].h, wth=240, x1, y1, x2, y2, i, j, total, speedy=FALSE, cnt, pts; 
 
    CONSUME_KEYS; 
    Show_Error_Message( msgs,SFX_LEVEL_START,2,IPage ); 
 
    x2 = (x1=(VS.mx+1-wth-6)>>1) + wth + 5; 
    y2 = (y1=(VS.my+1-ht*11-6)>>1) + ht*11 + 5; 
    Draw_Edged_Box( x1,y1,x2,y2,WHITE,GREY,L_GREY,IPage ); 
    x2 = x1 + ((wth-Spr_Strlen(SFont,title))>>1); 
    Spr_Print( SFont,title,x2,y1+5,PROPORTIONAL,HORIZ,IPage ); 
    line( x2-1,y1+ht+5,x2+Spr_Strlen(SFont,title),y1+ht+5,WHITE,IPage ); 
    line( x2-2,y1+ht+6,x2+Spr_Strlen(SFont,title)+1,y1+ht+6,GREY,IPage ); 
 
    CONSUME_KEYS; 
    Sfx( SFX_BONUS_PICKUP ); 
    Spr_Print( SFont,"Level Completion",x1+11,y1+ht*3+5,PROPORTIONAL,HORIZ,IPage ); 
    Spr_Print( SFont,equals,x1+22*8+3,y1+ht*3+5,PROPORTIONAL,HORIZ,IPage ); 
    Spr_Printi( SFont,total=PTS_END_OF_LEVEL_BONUS,x1+24*8+3,y1+ht*3+5,5,8,HORIZ,IPage ); 
 
    speedy = (inkey(0)!=0); 
 
    for ( i=0; i<2; i++ ) 
    { 
        if ( !speedy ) 
        { 
            waitticks( 9 ); 
            speedy = (inkey(0)!=0); 
        } 
        CONSUME_KEYS; 
        if ( i==0 ) 
        { 
            y2  = y1+ht*5+5; 
            cnt = Cfg.rays; 
            pts = PTS_NEUTRON_LEFT; 
        } 
        else 
        { 
            y2  = y1+ht*7+5; 
            cnt = Cfg.shield_cnt; 
            pts = PTS_SHIELD_LEFT; 
        } 
        if ( !cnt ) pts=0; 
        Spr_Print( SFont,(i==0)?"Remaining Neutrons":"Remaining Shields",x1+11,y2,PROPORTIONAL,HORIZ,IPage ); 
        Spr_Printi( SFont,cnt,x1+19*8+3,y2,2,8,HORIZ,IPage ); 
        Spr_Print( SFont,equals,x1+22*8+3,y2,PROPORTIONAL,HORIZ,IPage ); 
        x2 = x1 + 24*8 + 3; 
        for ( j=0; j<=cnt; j++ ) 
        { 
            if ( cnt==0 || j>2; 
    ave_pr -= ave_pr>>3; 
 
    for ( j=MAX_SHIELDS, s=Cfg.shields; j; j--, s++ ) 
    { 
        if ( s->active ) 
        { 
            ave_sr = (s->rx+s->ry)>>1; 
            dx     = (s->x >= p->x) ? s->x-p->x : p->x-s->x; 
            dy     = (s->y >= p->y) ? s->y-p->y : p->y-s->y; 
            if ( dx<=(ave_sr+ave_pr) && dy<=(ave_sr+ave_pr) )    /*  Prevents overflow for pseudoroot  */ 
            { 
                d  = RootTable[dx*dx+dy*dy]; 
                m1 = p->x+p->vx-s->x; m1=ABS(m1); 
                m2 = p->y+p->vy-s->y; m2=ABS(m2); 
                m1 = RootTable[ m1*m1+m2*m2 ]; 
                if ( (ave_sr<=d && d<=ave_sr+ave_pr && m1=d && ave_sr-ave_pr<=d && m1>d) ) 
                { 
                    /*  Determine angle of incidence & therefore reflection  */ 
 
                    if ( d>=ave_sr ) 
                    { 
                        Cfg.score += PTS_SHIELD_HIT; 
                        Update_Score = TRUE; 
                    } 
                    vx = p->vx*5;          /*  Velocity vector  */ 
                    vy = p->vy*5; 
                    nx = (px=p->x)-s->x;   /*  Point of incidence & normal vector  */ 
                    ny = (py=p->y)-s->y; 
                    m1 = RootTable[ nx*nx+ny*ny ]; 
                    m2 = RootTable[ vx*vx+vy*vy ]; 
                    factor = (m2*100)/m1; 
                    ac = (int) (((long)(nx*(-vx)+ny*(-vy))*(long)TRIG_TAB_FACTOR)/((long)(m1*m2))); 
                    if ( (theta=PseudoACos(ac)%360)<5 ) 
                    { 
                        rx = -vx; 
                        ry = -vy; 
                    } 
                    else 
                    { 
                        rx = (nx*PseudoCos(theta) - ny*PseudoSin(theta))/TRIG_TAB_FACTOR; 
                        ry = (nx*PseudoSin(theta) + ny*PseudoCos(theta))/TRIG_TAB_FACTOR; 
                        rx = (rx*factor)/100; 
                        ry = (ry*factor)/100; 
                        x1 = px-vx; 
                        y1 = py-vy; 
                        x2 = px+rx; 
                        y2 = py+ry; 
                        if ( ABS(x1-x2)<=3 && ABS(y1-y2)<=3 ) 
                        { 
                            theta = 180-theta; 
                            rx    = ((-nx)*PseudoCos(theta) - (-ny)*PseudoSin(theta))/TRIG_TAB_FACTOR; 
                            ry    = ((-nx)*PseudoSin(theta) + (-ny)*PseudoCos(theta))/TRIG_TAB_FACTOR; 
                            rx    = (rx*factor)/100; 
                            ry    = (ry*factor)/100; 
                        } 
                    } 
                    if ( (p->vx=rx/5)==0 ) p->vx=(rx>0)?1:-1; 
                    if ( (p->vy=ry/5)==0 ) p->vy=(ry>0)?1:-1; 
 
                    /*  If at single speed, energize them to avoid being caught in rasp fashion  */ 
 
                    if ( ABS(p->vx)==1 && ABS(p->vy)==1 ) 
                    { 
                        p->vx += (p->vx>0)?1:-1; 
                        p->vy += (p->vy>0)?1:-1; 
                    } 
 
                    Sfx( SFX_SHIELD_HIT ); 
 
                    return( TRUE ); 
                } 
            } 
        } 
    } 
 
    return( FALSE ); 
} 
 
void Play_Demo( void ) 
{ 
    static int   ftype; 
    NUCLEUS      *n; 
    int          i, dx, dy; 
 
    if ( Target_Nuc==-1 || Cfg.nuclei[Target_Nuc].mass==0 ) 
    { 
        for ( Target_Nuc=-1, i=0, n=Cfg.nuclei; imass ) 
            { 
                if ( (Lev->rogue_particles || Lev->rogue_beams) && !(n->n_fired || n->s_fired) ) 
                { 
                    Target_Nuc = i; 
                    ftype      = (n->s_fired) ? L_BUTTON:R_BUTTON; 
                } 
                else 
                { 
                    if ( Lev->mass_meltdown && n->mass+4>=Lev->critical_mass && !n->s_fired ) 
                    { 
                        Target_Nuc = i; 
                        ftype      = R_BUTTON; 
                    } 
                    else 
                    { 
                        if ( Lev->time_meltdown && !n->n_fired ) 
                        { 
                            Target_Nuc = i; 
                            ftype      = L_BUTTON; 
                        } 
                    } 
                } 
            } 
        } 
    } 
 
    if ( Target_Nuc != -1 ) 
    { 
        n  = Cfg.nuclei+Target_Nuc; 
        dx = n->cx - MouseX; 
        dy = n->cy - MouseY; 
        if ( ABS(dx) > 3 ) dx=(dx<0)?-3:3; 
        if ( ABS(dy) > 3 ) dy=(dy<0)?-3:3; 
        mousepos( MouseX+=dx,MouseY+=dy ); 
        dx = MouseX - n->cx; 
        dy = MouseY - n->cy; 
        if ( ABS(dx)<4 && ABS(dy)<4 ) 
        { 
            if ( SyringeDir==0 ) 
            { 
                if ( ftype==L_BUTTON ) 
                { 
                    if ( Cfg.rays ) 
                    { 
                        n->n_fired=TRUE; 
                        MouseB    =ftype; 
                    } 
                } 
                else 
                { 
                    if ( Cfg.shield_cnt ) 
                    { 
                        n->s_fired=TRUE; 
                        MouseB    =ftype; 
                    } 
                } 
                Target_Nuc=-1; 
            } 
        } 
    } 
} 
 
int  Play_Level( void ) 
{ 
    int            i, key=0; 
    long           score; 
    unsigned int   *t; 
    PARTICLE       *p; 
    NUCLEUS        *n; 
    SHIELD         *s; 
    BEAM           *b; 
    char           *msgs[2]={ "Get Ready!",NULL }; 
 
    Meltdown = NO_MELTDOWN; 
 
    do 
    { 
        Lev = Levels + Cfg.level - 1; 
        if ( !Cfg.briefed ) 
        { 
            Show_Experiment_Guide(); 
            Cfg.briefed=TRUE; 
            if ( Demo_Mode && Demo_Exited ) break; 
        } 
        Fade_Screen( MODE_320x200x256 ); 
        Draw_Fractal_Background( 0 ); 
        fadein( &Pal,0 ); 
        CONSUME_KEYS; 
        Show_Error_Message( msgs,SFX_LEVEL_START,2,0 ); 
        if ( Demo_Mode && Demo_Exited ) break; 
        Last_Mins = Target_Nuc = -1; 
        Display_Panel(); 
        COPY_PAGE( 0,1 ); 
        COPY_PAGE( 0,2 ); 
        initialise_objects( MAX_OBJECTS,&Selective_Replace,&Colour_Cycle_Switcher ); 
        mousepos( I_MouseX=MouseX=VS.mx>>1,I_MouseY=MouseY=VS.my>>1 ); 
        SyringeOffset=SyringeDir=0; 
 
        add_object( SYRINGE_TOON_BASE_PRI,Toon_Syringe,SyringeBase=5,MouseX,MouseY ); 
 
        if ( Demo_Mode ) 
        { 
            add_object( DEMO_TOON_BASE_PRI,Toon_Demo,0,(VS.mx-Demo_Icons[0].w)>>1,(VS.my-Demo_Icons[0].h)>>1 ); 
            Demo_Timer=360; 
        } 
 
        for ( i=0; icount; i++ ) 
          add_object( PARTICLE_TOON_BASE_PRI+i,Toon_Particle[Cfg.particles[i].type],0,Cfg.particles[i].x,Cfg.particles[i].y ); 
 
        for ( i=0, n=Cfg.nuclei; imass ) add_object( COUNTER_TOON_BASE_PRI+i,Toon_Counter,n->cframe,n->cx-3,n->cy-2 ); 
 
        for ( i=0, s=Cfg.shields; iactive ) add_object( SHIELD_TOON_BASE_PRI+i,Toon_Shield,s->frame,s->x,s->y ); 
 
        for ( i=0, b=Cfg.beams; iactive ) add_object( BEAM_TOON_BASE_PRI+i,Toon_Beam,b->frame,b->x,b->y ); 
 
        for ( i=0, p=Cfg.rogues; irogue ) add_object( ROGUE_TOON_BASE_PRI+i,Toon_Rogue_Particle,p->frame,p->x,p->y ); 
 
        Experiment_Timer = 18; 
 
        Game_Paused = Neutron_Fired = Update_Score = FALSE; 
 
        CONSUME_KEYS; 
 
        do 
        { 
            if ( Demo_Mode ) Play_Demo(); else 
            { 
                _asm    cli 
                MouseB = I_MouseB; 
                MouseX = I_MouseX; 
                MouseY = I_MouseY; 
                _asm    sti 
            } 
 
            update_objects(0,0,2); 
            if ( (key=inkey(0)) ) Process_Keystroke( key ); 
            Cycle_Fractal_Colours(); 
 
            if ( MouseB&L_BUTTON )     /*  Left button to fire rays  */ 
            { 
                if ( SyringeDir!=0 || !Lev->gun || Cfg.rays==0 ) 
                { 
                    Sfx( SFX_NO_FIRE ); 
                } 
                else 
                { 
                    Sfx( SFX_NEUTRON_FIRED ); 
                    SyringeDir=1; 
                    Cfg.rays--; 
                    Display_Neutron(); 
                    Neutron_Fired=TRUE; 
                } 
                MouseB ^= L_BUTTON; 
                _asm    cli 
                I_MouseB = MouseB; 
                _asm    sti 
            } 
            if ( MouseB&R_BUTTON )     /*  Right button to fire shields  */ 
            { 
                if ( SyringeDir!=0 || !Lev->force_field || Cfg.shield_cnt==0 ) 
                { 
                    Sfx( SFX_NO_FIRE ); 
                } 
                else 
                { 
                    SyringeDir=1; 
                    if ( Create_Protective_Shield(MouseX,MouseY) ) Sfx( SFX_NO_FIRE ); else 
                    { 
                        Sfx( SFX_SHIELD_FIRED ); 
                        Cfg.shield_cnt--; 
                        Display_Shields(); 
                    } 
                } 
                MouseB ^= R_BUTTON; 
                _asm    cli 
                I_MouseB = MouseB; 
                _asm    sti 
            } 
 
            Update_Particle_Positions(); 
            Detect_Particle_Collisions(); 
            if ( Lev->rogue_beams ) Check_Beam_Collisions(); 
 
            if ( object_active(POWER_UP_TOON_BASE_PRI) ) Check_Bonus_Pickup(MouseX,MouseY); 
 
            for ( i=0, t=Cfg.timers, n=Cfg.nuclei; imass && !*t ) 
                { 
                    if ( Lev->orbit_speed_up && n->speed<90 ) n->speed+=ANGLE_INC; 
                    n->cframe--; 
                    *t = SPEED_UP_TIME; 
                } 
            } 
 
            if ( !Experiment_Timer ) 
            { 
                Cfg.time--; 
                Display_Time(); 
                Experiment_Timer = 18; 
                for ( i=0, s=Cfg.shields; itime ) (s->time--); 
                if ( (--Cfg.power_up_timer)==0 ) 
                { 
                    Sfx( SFX_BONUS_PICKUP ); 
                    add_object( POWER_UP_TOON_BASE_PRI,Toon_PowerUp,((Power_Up_Icon=(Lev->powerup!=PUP_RANDOM)?Lev->powerup:(random()%PUP_RANDOM))==PUP_SHIELD)?0:8,VX_MIN-15,(random()%(VY_MAX-VY_MIN-15))+VY_MIN ); 
                    Power_Up_Delay     = 0; 
                    Cfg.power_up_timer = Lev->power_up_delay; 
                } 
                if ( Lev->rogue_beams ) 
                { 
                    if ( (--Cfg.beam_timer)==0 ) 
                    { 
                        if ( Create_Rogue_Beam() ) Sfx( SFX_ROGUE_BEAM ); 
                        Cfg.beam_timer = Calculate_Rogue_Interval( Lev->min_beam_sep,Lev->max_beam_sep ); 
                    } 
                } 
                if ( Lev->rogue_particles ) 
                { 
                    for ( i=0, p=Cfg.rogues; irogue && (--(p->time))==0 ) 
                      { 
                          p->rogue = FALSE; 
                          delete_object( ROGUE_TOON_BASE_PRI+i ); 
                      } 
                    if ( (--Cfg.rogue_timer)==0 ) 
                    { 
                        if ( Create_Rogue_Particle() ) Sfx( SFX_ROGUE_PARTICLE ); 
                        Cfg.rogue_timer = Calculate_Rogue_Interval( Lev->min_rogue_sep,Lev->max_rogue_sep ); 
                    } 
                } 
            } 
 
            if ( Update_Score ) 
            { 
                Display_Score(); 
                Update_Score=FALSE; 
            } 
 
            Neutron_Fired=FALSE; 
 
            if ( Demo_Mode && (Demo_Exited || !Demo_Timer) ) break; 
        } 
        while ( key!=ESCAPEKEY && !Meltdown && Cfg.time ); 
        Game_Paused = TRUE; 
        remove_objects(); 
 
        if ( Sound_Source==SOUND_BLASTER ) SB_abort(0x01); else flush_sounds(); 
 
        score = Cfg.score; 
 
        if ( Meltdown ) 
        { 
            Sfx( (random()&0x01)?SFX_SCREAM1:SFX_SCREAM2 ); 
            rect( 0,0,VS.mx,VS.my,BLACK,FILL,2 ); 
            Spr_Print( SFont,DeathMsgs[Meltdown],(VS.mx-Spr_Strlen(SFont,DeathMsgs[Meltdown]))>>1,30,PROPORTIONAL,HORIZ,2 ); 
            melt_screen( IPage,2,6 ); 
            CONSUME_KEYS; 
            if ( Demo_Mode ) Meltdown=NO_MELTDOWN; else 
            { 
                Menu_New_Game( 0 ); 
                Sfx( SFX_GAME_OVER ); 
                sleep( 1 ); 
                CONSUME_KEYS; 
            } 
        } 
 
        if ( Demo_Mode && Demo_Exited ) break; 
 
        if ( !Meltdown && (Cfg.time==0 || Demo_Mode) ) 
        { 
            if ( !Demo_Mode ) End_Of_Level_Bonus(); 
            if ( (++Cfg.level) > Max_Levels ) 
            { 
                if ( !Demo_Mode ) Show_Error_Message( VictoryMsgs,SFX_FANFARE,20,IPage ); 
                break; 
            } 
            Initialise_Level(); 
        } 
    } 
    while ( key!=ESCAPEKEY && !Meltdown ); 
 
    if ( !Demo_Mode && key!=ESCAPEKEY ) 
    { 
        CONSUME_KEYS; 
        Hall_Of_Fame( &High_Scores,score,1 ); 
    } 
 
    return( 0 ); 
} 
 
void Print_Banner( int set_mode ) 
{ 
    char   attr=ATTR(BLUE,WHITE); 
 
    if ( set_mode ) setmode( 0x03 ); 
 
    setpos( 0,0,0 ); 
 
    _asm   mov  ax, 0920H 
    _asm   xor  bh, bh 
    _asm   mov  bl, attr 
    _asm   mov  cx, 80 
    _asm   int  10H 
 
    setpos( (80-strlen(Banner))>>1,0,0 ); 
    prints( Banner,attr,0 ); 
    setpos( 0,5,0 ); 
} 
 
void Process_Keystroke( int key ) 
{ 
    SPRITE   copy; 
    int      x, y; 
 
    switch( key ) 
    { 
        case 'p': 
        case 'P': 
          Game_Paused=TRUE; 
          if ( Sound_Source==SOUND_BLASTER ) 
          { 
              SB_pause(TRUE); 
              SB_abort(0x02 ); 
          } 
          else hold_sound( ON ); 
          if ( !Screen_Shot ) 
          { 
              getspr( ©,x=(VS.mx-Pause_Icon.w)>>1,y=(VS.my-Pause_Icon.h)>>1,Pause_Icon.w,Pause_Icon.h,IPage ); 
              putspr( &Pause_Icon,x,y,OWRTE,IPage ); 
          } 
          Wait_For_Event( 32000 ); 
          if ( !Screen_Shot ) 
          { 
              putspr( ©,x,y,OWRTE,IPage ); 
              free( copy.buff ); 
          } 
          Game_Paused=FALSE; 
          if ( Sound_Source==SOUND_BLASTER ) 
          { 
              SB_pause(FALSE); 
              GOptions_Music(0); 
          } 
          else hold_sound( OFF ); 
          break; 
        case ESCAPEKEY: 
          break; 
    } 
} 
 
int  Set_Particle_Velocity( int speed_mask, int min ) 
{ 
    register int   v; 
 
    do v=(random()%speed_mask)-(speed_mask>>1); while ( v==0 ); 
 
    if ( v > 0 ) 
    { 
        if ( v < min ) v=min; 
    } 
    else if ( v > -min ) v=-min; 
 
    return( v ); 
} 
 
int  Show_Error_Message( char **msgs, SFX_TYPE sfx, int wait, int page ) 
{ 
    SPRITE    save; 
    int       x1, y1, x2, y2, l, w, i, ht=SFont[' '].h; 
 
    for ( l=w=0; msgs[l]; l++ ) 
      if ( (i=Spr_Strlen(SFont,msgs[l]))>w ) w=i; 
 
    w += 16; 
    x1 = (VS.mx + 1 - w - 10)>>1; 
    y1 = (VS.my + 1 - (l+2)*ht - 8)>>1; 
    x2 = x1 + w + 9; 
    y2 = y1 + (l+2)*ht + 7; 
 
    if ( getspr(&save,x1,y1,(x2-x1+1),(y2-y1+1),page) ) Terminate( ERR_MALLOC ); 
    Draw_Edged_Box( x1,y1,x2,y2,WHITE,GREY,L_GREY,page ); 
    Sfx( sfx ); 
    for ( i=0; i0 && !Demo_Exited; w-- ) waitticks( 1 ); 
        i=(Demo_Exited)?ESCAPEKEY:0; 
    } 
    else 
    { 
        if ( !wait ) i=getkey(0); else 
        { 
            Wait_For_Event( wait ); 
            i=0; 
        } 
    } 
    putspr( &save,x1,y1,OWRTE,page ); 
    free( save.buff ); 
 
    return( i ); 
} 
 
int  Show_Experiment_Guide( void ) 
{ 
#define   MAX_MSGS              20 
    char    buff[8], *msgs[MAX_MSGS]; 
    int     num=0, i, w, c; 
    int     x1, y1, x2, y2, ht; 
    FONT    f; 
 
    rom_font( &f,ROM_8x8 ); 
    ht = f.height; 
 
    for ( i=0; itext[i]; i++ ) strcpy(msgs[num++],Lev->text[i]); 
 
    *msgs[num++] = '\0'; 
    strcpy( msgs[num++],"                         Summary" ); 
    strsetx( msgs[num++],"Experiment Duration ",itoa(Lev->time,buff,10)," seconds.",NULL ); 
    strsetx( msgs[num++],"Number Of Particles ",itoa(Lev->count,buff,10),".",NULL ); 
    strsetx( msgs[num++],"Critical Mass       ",itoa(Lev->critical_mass,buff,10)," units ",(Lev->mass_meltdown)?"(Meltdown!).":"(Split Only).",NULL ); 
    strsetx( msgs[num++],"Critical Time       ",itoa(Lev->critical_time,buff,10)," seconds ",(Lev->time_meltdown)?"(Meltdown!).":"(Split Only).",NULL ); 
    strsetx( msgs[num++],"Atomic Injector     ",(Lev->gun)?"ACTIVE (":"INACTIVE",(Lev->gun)?itoa(Lev->rays,buff,10):"",(Lev->gun)?" neutrons).":".",NULL ); 
    strsetx( msgs[num++],"Nuclear Shields     ",(Lev->force_field)?"ACTIVE (":"INACTIVE",(Lev->force_field)?itoa(Lev->shields,buff,10):"",(Lev->force_field)?").":".",NULL ); 
    if ( Lev->rogue_beams ) 
      strsetx( msgs[num++],"Rogue Beams         ACTIVE (Meltdown!).",NULL ); 
    if ( Lev->rogue_particles ) 
      strsetx( msgs[num++],"Rogue Particles     ACTIVE (Meltdown!).",NULL ); 
 
    Fade_Screen( MODE_640x200x16 ); 
    rect( 0,0,VS.mx,VS.my,BLUE,FILL,0 ); 
 
    w  = (sizeof(Levels[0].text[0])-1)*8 + 16; 
    x1 = (VS.mx + 1 - w - 10)>>1; 
    y1 = (VS.my + 1 - (num+2)*ht - 8)>>1; 
    x2 = x1 + w + 9; 
    y2 = y1 + (num+2)*ht + 7; 
 
    Draw_Edged_Box( x1,y1,x2,y2,WHITE,GREY,L_GREY,0 ); 
    Sfx( SFX_LEVEL_START ); 
    for ( i=0; imass = 0; 
    n->s_fired = n->n_fired = FALSE; 
    delete_object( COUNTER_TOON_BASE_PRI+nidx ); 
    Sfx( sfx ); 
    for ( i=Lev->count, p=Cfg.particles; i>0; i--, p++ ) 
    { 
        if ( p->active && p->nucleus==nidx ) 
        { 
            p->active = FALSE; 
            p->vx     = Set_Particle_Velocity( SPEED_MASK_2,2 ); 
            p->vy     = Set_Particle_Velocity( SPEED_MASK_2,2 ); 
        } 
    } 
} 
 
void Update_Particle_Positions( void ) 
{ 
    int            i; 
    PARTICLE       *p; 
    NUCLEUS        *n; 
 
    if ( Lev->rogue_particles ) 
    { 
        for ( i=0, p=Cfg.rogues; irogue ) 
            { 
                Particle_Hit_Shield( p,RogueParticles[p->frame].w,RogueParticles[p->frame].h ); 
                if ( p->x+p->vx < VX_MIN || p->x+p->vx > VX_MAX ) p->vx=-p->vx; 
                if ( p->y+p->vy < VY_MIN || p->y+p->vy > VY_MAX ) p->vy=-p->vy; 
                p->x += p->vx; 
                p->y += p->vy; 
            } 
        } 
    } 
 
    for ( i=Lev->count, p=Cfg.particles; i>0; i--, p++ ) 
    { 
        if ( p->active ) 
        { 
            n = Cfg.nuclei + p->nucleus; 
            if ( (p->angle+=n->speed) >= MAX_DEGREES ) p->angle-=MAX_DEGREES; 
            p->x = n->cx + (p->r*PseudoCos(p->angle))/TRIG_TAB_FACTOR; 
            p->y = n->cy + (p->r*PseudoSin(p->angle))/TRIG_TAB_FACTOR; 
        } 
        else 
        { 
            /*  Look for particle hitting a shield  */ 
 
            Particle_Hit_Shield( p,SParticles[p->type].w,SParticles[p->type].h ); 
 
            if ( p->x+p->vx < VX_MIN || p->x+p->vx > VX_MAX ) 
            { 
                if ( ABS(p->vx) > MIN_SPEED ) p->vx += (p->vx<0)?1:-1; 
                p->vx=-p->vx; 
                if ( p->x < VX_MIN ) p->x=VX_MIN; else 
                  if ( p->x > VX_MAX ) p->x=VX_MAX; 
            } 
            if ( p->y+p->vy < VY_MIN || p->y+p->vy > VY_MAX ) 
            { 
                if ( ABS(p->vy) > MIN_SPEED ) p->vy += (p->vy<0)?1:-1; 
                p->vy=-p->vy; 
                if ( p->y < VY_MIN ) p->y=VY_MIN; else 
                  if ( p->y > VY_MAX ) p->y=VY_MAX; 
            } 
            p->x += p->vx; 
            p->y += p->vy; 
        } 
    } 
} 
 
/*==========================[ End Of Source File ]===========================*/