#include "platform.h" #include "fastfile.h" #include "ogldrv.h" #include "aldrv.h" #include "tgaLoader.h" #include "vmath.h" #include "game.h" #include "gold.h" #include "input.h" #include "texfont.h" #include "menu.h" #include "spline.h" #include "wavstream.h" /* Enemy types */ #define ENEMY_PARTICLE 0 #define ENEMY_REDDRAGON 1 #define ENEMY_GREENDRAGON 2 #define ENEMY_BLUEDRAGON 3 #define ENEMY_BLACKDRAGON 4 #define ENEMY_WATERDRAGON 5 #define ENEMY_FAIRY 6 #define ENEMY_PHOENIX 7 #define ENEMY_SQUID 8 #define ENEMY_SEASERPENT 9 #define ENEMY_TANK 10 #define ENEMY_ZEPPELIN 11 #define ENEMY_DRONE 12 #define ENEMY_SUPERDRAGON 13 /* Boss types */ #define BOSS_MASK 0 #define BOSS_SQUID 1 #define BOSS_BLUEDEATH 2 #define BOSS_AIRSHIP 3 #define BOSS_REAPER 4 /* Power up types */ #define POWER_CRYSTAL1 1 #define POWER_CRYSTAL2 2 #define POWER_CRYSTAL3 3 #define POWER_HEART1 4 #define POWER_HEART2 5 int game_state = GAME_STATE_INTRO1; int menu_state = MENU_STATE_TITLE; int score = 0; int level = 1; int boss_mode = No; int ingame_fade = -1; int bgm_fade = No; char bgm_next_track[16] = ""; int bgm_volume = 10; /* 10=loudest,0=silent */ int sfx_volume = 10; float scroll_speed = 0.5f; /* Ground scroll speed */ int background = 0; /* Background image number */ int fps_limit = 60; /* FPS cap (varies depending on game speed */ /* and halves when too many sprites are ingame */ int game_speed = 100; /* Game movement speed */ int active_shoots = 0; /* Number of active shoots in the game */ BYTE keys[256]; struct vec2d_t ch; struct mouse_t mouse; struct dragon_t dragon; struct lock_t locks[MAX_LOCKS]; struct enemy_t enemies[MAX_ENEMIES]; struct boss_t boss; struct enemyshoot_t enemyshoots[MAX_ENEMYSHOOTS]; struct guard_t guard; int overdrive_timer = 0; struct powerup_t powerups[4]; struct smoke_t smoke[MAX_SMOKE]; unsigned int textures[MAX_TEXTURES]; struct sound_t sfx[MAX_SOUNDFX]; struct font_t fonts[7]; struct homingblast_t homingblasts[MAX_HOMINGBLASTS]; struct fullautoblast_t fullautoblasts[MAX_FULLAUTOBLASTS]; struct explosion_t explosions[MAX_EXPLOSIONS]; struct vec2d_t trees[8], rocks[8], clouds[3]; struct wave_stream bgm; unsigned int render_target = 0; #if 1 struct spline_t spline; #endif char* tex_files[] = { "aircrft1.tga", "aircrft2.tga", "bkg1.tga", "bkg2.tga", "bkg3.tga", "bkg4.tga", "bkg5.tga", "bkg6.tga", "bkg7.tga", "blade1.tga", "blade2.tga", "bomb.tga", "boss1.tga", "boss2.tga", "boss3.tga", "boss4.tga", "boss5.tga", "blade1.tga", "blade2.tga", "cloud1.tga", "cloud2.tga", "cloud3.tga", "cross1.tga", "cross2.tga", "desc1.tga", "desc2.tga", "desc3.tga", "diamond1.tga", "diamond2.tga", "diamond3.tga", "dirt1a.tga", "dirt1b.tga", "dragon1.tga", "dragon2.tga", "dragon3.tga", "dragon4.tga", "drone.tga", "expl1.tga", "expl2.tga", "fairy.tga", "fbgreen.tga", "fbred.tga", "firebal1.tga", "firebal2.tga", "firebal3.tga", "firebird.tga", "flare0.tga", "flare.tga", "gold.tga", "grass1a.tga", "grass1b.tga", "guard.tga", "hblast2.tga", "hblast.tga", "heart1.tga", "heart2.tga", "hero.tga", "intile1.tga", "intile2.tga", "intile2d.tga", "intile3.tga", "intile3d.tga", "palmtree.tga", "particle.tga", "phoenix.tga", "pinetree.tga", "plasma1.tga", "plasma2.tga", "rock.tga", "sand1a.tga", "sand1b.tga", "sdragon.tga", "shogun3d.tga", "shot1.tga", "shot2.tga", "shot.tga", "smoke.tga", "sparkle.tga", "squid.tga", "sserpent.tga", "tank.tga", "title1.tga", "title2.tga", "water1a.tga", "water1b.tga", "zeppelin.tga", }; int tex_file_sizes[MAX_TEXTURES]; char* sfx_files[] = { "crystal3.wav", "firefa.wav", "hblast.wav", "lock8.wav", "mmove.wav", "msel.wav" "woosh.wav", }; #if 0 int sfx_file_sizes[MAX_SOUNDFX] = { 22134, /* crystal.wav */ 6376, /* firefa.wav */ 91674, /* hblast.wav */ 43280, /* mmove.wav */ 38780, /* msel.wav */ }; #else int sfx_file_sizes[MAX_SOUNDFX]; #endif unsigned int get_texture( char* texname ); int active_homing_blasts(); void activate_explosion( int expl_id, float x, float y, int type ); void give_possible_powerup( float x, float y ); void reset_smoke_puffs(); void add_smoke_puff( float x, float y, int type ); void update_crosshair_position() { get_crosshair_position( &ch.x, &ch.y ); } void reset_dragon() { /* Reset dragon values */ dragon.width = 106.0f/2.0f; dragon.height = 149.0f/2.0f; dragon.x = ( 640.0f / 2.0f ) - ( dragon.width / 2.0f ); dragon.y = ( 480.0f / 2.0f ) - ( dragon.height / 2.0f ); dragon.vx = 5.0f; dragon.vy = 5.0f; dragon.energy = 100; dragon.max_energy = 100; dragon.attack_mode = 1; dragon.powers[0] = Yes; dragon.powers[1] = Yes; dragon.powers[2] = Yes; dragon.inv_t = 0; } void reset_enemies() { int i = 0; /* Reset all enemies to dead and with no locks */ while( i < MAX_ENEMIES ) { enemies[i].dead = 1; enemies[i].dying = No; enemies[i].lock = -1; enemies[i].type = 0; enemies[i].movement = EMOVEMENT_STATIONARY; enemies[i].line = 0; enemies[i].energy = 1; i++; } #if 0 /* Test: Create one enemy (particle) */ /*enemies[0].dead = 0; enemies[0].frame = 0; enemies[0].type = ENEMY_PARTICLE; enemies[0].x = 320.0f; enemies[0].y = 150.0f; enemies[0].sx = 32.0f; enemies[0].sy = 32.0f; enemies[0].max_frame = 29; enemies[0].timer = 0; enemies[0].max_timer = 0; enemies[0].energy = 1; enemies[1].dead = 0; enemies[1].frame = 0; enemies[1].type = ENEMY_REDDRAGON; enemies[1].x = 375.0f; enemies[1].y = 120.0f; enemies[1].sx = 96.0f; enemies[1].sy = 64.0f; enemies[1].max_frame = 3; enemies[1].timer = 0; enemies[1].max_timer = 15; enemies[1].energy = 5; enemies[2].dead = 0; enemies[2].frame = 0; enemies[2].type = ENEMY_GREENDRAGON; enemies[2].x = 275.0f; enemies[2].y = 120.0f; enemies[2].sx = 96.0f; enemies[2].sy = 64.0f; enemies[2].max_frame = 3; enemies[2].timer = 0; enemies[2].max_timer = 15; enemies[2].energy = 5; enemies[3].dead = 0; enemies[3].frame = 0; enemies[3].type = ENEMY_BLACKDRAGON; enemies[3].x = 325.0f; enemies[3].y = 90.0f; enemies[3].sx = 96.0f; enemies[3].sy = 64.0f; enemies[3].max_frame = 3; enemies[3].timer = 0; enemies[3].max_timer = 15; enemies[3].energy = 5; enemies[4].dead = 0; enemies[4].frame = 0; enemies[4].type = ENEMY_FAIRY; enemies[4].x = 405.0f; enemies[4].y = 40.0f; enemies[4].sx = 56.0f; enemies[4].sy = 62.0f; enemies[4].max_frame = 1; enemies[4].timer = 0; enemies[4].max_timer = 0; enemies[0].energy = 1;*/ enemies[5].dead = 0; enemies[5].frame = 0; enemies[5].type = ENEMY_FAIRY; enemies[5].x = spline.points[spline.start_point].x; enemies[5].y = spline.points[spline.start_point].y; enemies[5].sx = 56.0f; enemies[5].sy = 62.0f; enemies[5].max_frame = 1; enemies[5].timer = 0; enemies[5].max_timer = 0; enemies[5].movement = EMOVEMENT_SPLINE; enemies[6].dead = 0; enemies[6].dying = 0; enemies[6].frame = 0; enemies[6].type = ENEMY_ZEPPELIN; enemies[6].x = 320.0f; enemies[6].y = 150.0f; enemies[6].sx = 76.0f; enemies[6].sy = 100.0f; enemies[6].max_frame = 1; enemies[6].timer = 0; enemies[6].max_timer = 0; enemies[6].energy = EMAX_ZEPPELIN; /* enemies[7].dead = 0; enemies[7].dying = 0; enemies[7].frame = 0; enemies[7].type = ENEMY_TANK; enemies[7].x = 320.0f; enemies[7].y = 0.0f; enemies[7].sx = 84.0f; enemies[7].sy = 91.0f; enemies[7].max_frame = 1; enemies[7].timer = 0; enemies[7].max_timer = 0; enemies[7].energy = EMAX_TANK; enemies[7].rot = 180.0f; // /* Turn completely around */ /*enemies[7].movement = EMOVEMENT_STILL;*/ enemies[8].dead = 0; enemies[8].dying = 0; enemies[8].frame = 0; enemies[8].type = ENEMY_DRONE; enemies[8].x = 280.0f; enemies[8].y = 100.0f; enemies[8].sx = 16.0f; enemies[8].sy = 16.0f; enemies[8].max_frame = 1; enemies[8].timer = 0; enemies[8].max_timer = 0; enemies[8].energy = 1; enemies[9].dead = 0; enemies[9].dying = 0; enemies[9].frame = 0; enemies[9].type = ENEMY_DRONE; enemies[9].x = 360.0f; enemies[9].y = 100.0f; enemies[9].sx = 16.0f; enemies[9].sy = 16.0f; enemies[9].max_frame = 1; enemies[9].timer = 0; enemies[9].max_timer = 0; enemies[9].energy = 1; enemies[10].dead = 0; enemies[10].dying = 0; enemies[10].frame = 0; enemies[10].type = ENEMY_DRONE; enemies[10].x = 320.0f; enemies[10].y = 80.0f; enemies[10].sx = 16.0f; enemies[10].sy = 16.0f; enemies[10].max_frame = 1; enemies[10].timer = 0; enemies[10].max_timer = 0; enemies[10].energy = 1; /*i = find_inactive_lock(); if( i != -1 ) { enemies[0].lock = i; activate_lock( i, enemies[0].x, enemies[0].y ); }*/ #endif } int get_enemy_point_value( int type ) { switch( type ) { case ENEMY_REDDRAGON: case ENEMY_GREENDRAGON: case ENEMY_BLUEDRAGON: case ENEMY_BLACKDRAGON: case ENEMY_WATERDRAGON: return 100; case ENEMY_FAIRY: return 25; case ENEMY_PHOENIX: case ENEMY_SQUID: case ENEMY_SEASERPENT: return 50; case ENEMY_ZEPPELIN: case ENEMY_TANK: return 150; case ENEMY_DRONE: return 10; case ENEMY_SUPERDRAGON: return 300; } return 0; } void kill_enemy( struct enemy_t* e, int expl_sfx ) { int expl = 0; /* Okay, it looks like we have a hit here. Now kill this targetable entity and deactivate this homing blast */ e->dead = Yes; /* Increase the user's score */ score += get_enemy_point_value( e->type ); /* Do explosion and sound effects */ play_sound_effect_static( &sfx[expl_sfx], 0 ); expl = find_inactive_explosion(); if( expl != -1 ) { activate_explosion( expl, e->x, e->y, 0 ); } /* Award the user with a gold peice */ add_gold_peice( e->x, e->y ); /* Possible powerup time */ give_possible_powerup( e->x, e->y ); } void add_new_enemy_shoot( float x, float y, float speed, int type ); void update_drone( struct enemy_t* e ) { static int delay = 0; /* Every 10 frames, make the drones shoot */ if( ++delay == 4 ) { add_new_enemy_shoot( e->x, e->y, 2.0f, 0 ); delay = 0; } } void reset_all_locks( int reset_enemy_locks ) { int i = 0; /* Reset all lock values */ memset( locks, 0, sizeof( struct lock_t ) ); while( i < MAX_LOCKS ) { locks[i].active = 0; locks[i].size = 500.0f; i++; } /* Remove locks from all enemies if necessary */ if( reset_enemy_locks ) { i = 0; while( i < MAX_ENEMIES ) { enemies[i].lock = -1; i++; } } } void reset_lock_to_inactive( int id ) { locks[id].active = 0; locks[id].size = 500.0f; locks[id].flash = 0; } int find_inactive_lock() { /* Search for an inactive lock. If one is free, return the id number of that lock and let the caller activate it and track it's position. If not, return -1 for no inactive lock. */ int i = 0; while( i < MAX_LOCKS ) { if( !locks[i].active ) return i; i++; } return -1; } int active_locks_available() { /* Search for any active locks and return the number of active locks. */ int i = 0; int lock_count = 0; while( i < MAX_LOCKS ) { if( locks[i].active ) lock_count++; i++; } return lock_count; } void activate_lock( int id, float x, float y ) { locks[id].active = 1; locks[id].x = x; locks[id].y = y; } void draw_active_locks() { int i; enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); wireframe( Yes ); for( i = 0; i < MAX_LOCKS; i++ ) { if( locks[i].active ) { float size = locks[i].size; push_pos(); translate( locks[i].x, locks[i].y ); rotate( locks[i].rotz ); if( locks[i].flash == 0 ) draw_quad( 0, -size/2.0f, -size/2.0f, size, size );// size += 5.0f; pop_pos(); } } wireframe( No ); disable_2d(); } void update_active_locks() { int i = 0; while( i < MAX_LOCKS ) { if( locks[i].active ) { locks[i].rotz += 4.0f; locks[i].size -= 15.0f; if( locks[i].size < 30.0f ) locks[i].size = 30.0f; } if( locks[i].size == 30.0f ) { locks[i].rotz = 0.0f; locks[i].flash++; if( locks[i].flash > 5 ) locks[i].flash = 0; } i++; } /* Update the position of each active lock with the position of that particular targetable entity */ i = 0; while( i < MAX_LOCKS ) { if( locks[i].active ) { locks[i].x = enemies[locks[i].owner].x; locks[i].y = enemies[locks[i].owner].y; } i++; } } void draw_explosion( struct explosion_t* e ) { div_t result; float tex[8];// = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; float s, t; float w = 320.0f, h = 320.0f; result = div( e->frame, 5 ); s = (float) result.rem * ( e->sx / w ); t = (float) 1.0f - ( result.quot * ( e->sy / h ) ); tex[0] = s; tex[1] = t - e->sy / h; tex[2] = s + e->sx / w; tex[3] = t - e->sy / h; tex[4] = s + e->sx / w; tex[5] = t; tex[6] = s; tex[7] = t; enable_2d(); push_pos(); transparent_blend( TRUE ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); translate( e->x, e->y ); draw_quad2( get_texture( "expl2" ), tex, -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); transparent_blend( FALSE ); pop_pos(); disable_2d(); } void draw_particle( const struct enemy_t* e ) { div_t result; float tex[8];// = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; float s, t; result = div( e->frame, 5 ); s = (float) result.rem * ( e->sx / 160.0f ); t = (float) 1.0f - ( result.quot * ( e->sy / 192.0f ) ); tex[0] = s; tex[1] = t + e->sy / 192.0f; tex[2] = s + e->sx / 160.0f; tex[3] = t + e->sy / 192.0f; tex[4] = s + e->sx / 160.0f; tex[5] = t; tex[6] = s; tex[7] = t; enable_2d(); push_pos(); transparent_blend( TRUE ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); translate( e->x, e->y ); draw_quad2( get_texture( "particle" ), tex, -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); transparent_blend( FALSE ); pop_pos(); disable_2d(); } void draw_dragon( const struct enemy_t* e, int frame_offset ) { div_t result; float tex[8];// = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; float s, t; float w = 288.0f; float h = 256.0f; result = div( e->frame + frame_offset, 5 ); s = (float) result.rem * ( e->sx / w ); t = (float) 1.0f - ( result.quot * ( e->sy / h ) ); tex[0] = s; tex[1] = t + e->sy / h; tex[2] = s + e->sx / w; tex[3] = t + e->sy / h; tex[4] = s + e->sx / w; tex[5] = t; tex[6] = s; tex[7] = t; enable_2d(); push_pos(); transparent_blend( TRUE ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); translate( e->x, e->y ); draw_quad2( get_texture( "dragon2" ), tex, -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); transparent_blend( FALSE ); pop_pos(); disable_2d(); } void draw_fairy( const struct enemy_t* e ) { /* Draw a simple fairy sprite */ /* Enable 2D rendering */ enable_2d(); /* Translate to the fairy's position */ push_pos(); translate( e->x, e->y ); /* Alpha blending for colour key */ transparent_blend( TRUE ); /* Set base colour to white */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Draw the fairy sprite */ draw_quad( get_texture( "fairy" ), -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); /* Disable alpha blending */ transparent_blend( FALSE ); /* Restore previous position */ pop_pos(); /* Disable 2D rendering */ disable_2d(); } void draw_zeppelin( const struct enemy_t* e ) { float f = 0.0f; /* Draw a zeppelin sprite */ /* Enable 2D rendering */ enable_2d(); /* Translate to the fairy's position */ push_pos(); translate( e->x, e->y ); /* Alpha blending for colour key */ transparent_blend( TRUE ); /* Set base colour to white */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Draw the fairy sprite */ draw_quad( get_texture( "zeppelin" ), -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); /* Disable alpha blending */ transparent_blend( FALSE ); /* Let's draw an energy bar! */ if( !e->dying ) { set_colour( 1.0f, 0.0f, 0.0f, 1.0f ); draw_line( -25.0f, -(e->sy/2.0f) - 10.0f, 25.0f, -(e->sy/2.0f) - 10.0f ); f = (float) (((float)e->energy) / ((float)EMAX_ZEPPELIN) ); set_colour( 0.0f, 1.0f, 0.0f, 1.0f ); draw_line( -25.0f, -(e->sy/2.0f) - 10.0f, (f*50.0f)-25.0f, -(e->sy/2.0f) - 10.0f ); } /* Restore previous position */ pop_pos(); /* Disable 2D rendering */ disable_2d(); } void draw_tank( const struct enemy_t* e ) { float f = 0.0f; /* Draw a tank sprite */ /* Enable 2D rendering */ enable_2d(); /* Translate to the tank's position */ push_pos(); translate( e->x, e->y ); /* Rotate the tank as necessary */ push_pos(); rotate( e->rot ); /* Alpha blending for colour key */ transparent_blend( TRUE ); /* Set base colour to white */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Draw the fairy sprite */ draw_quad( get_texture( "tank" ), -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); /* Disable alpha blending */ transparent_blend( FALSE ); /* Undo rotation */ pop_pos(); /* Let's draw an energy bar! */ if( !e->dying ) { set_colour( 1.0f, 0.0f, 0.0f, 1.0f ); draw_line( -25.0f, -(e->sy/2.0f) - 10.0f, 25.0f, -(e->sy/2.0f) - 10.0f ); f = (float) (((float)e->energy) / ((float)EMAX_TANK) ); set_colour( 0.0f, 1.0f, 0.0f, 1.0f ); draw_line( -25.0f, -(e->sy/2.0f) - 10.0f, (f*50.0f)-25.0f, -(e->sy/2.0f) - 10.0f ); } /* Restore previous position */ pop_pos(); /* Disable 2D rendering */ disable_2d(); } void draw_drone( const struct enemy_t* e ) { /* Draw a simple drone sprite */ /* Enable 2D rendering */ enable_2d(); /* Translate to the drone's position */ push_pos(); translate( e->x, e->y ); /* Alpha blending for colour key */ transparent_blend( TRUE ); /* Set base colour to white */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Draw the fairy sprite */ draw_quad( get_texture( "drone" ), -(e->sx/2.0f), -(e->sy/2.0f), e->sx, e->sy ); /* Disable alpha blending */ transparent_blend( FALSE ); /* Restore previous position */ pop_pos(); /* Disable 2D rendering */ disable_2d(); } void move_dragon() { if( move_up() ) { dragon.y -= 4.0f; } if( move_down() ) { dragon.y += 4.0f; } if( move_left() ) { dragon.x -= 4.0f; } if( move_right() ) { dragon.x += 4.0f; } /* Boundary detection */ if( dragon.x < 160.0f ) dragon.x = 160.0f; if( dragon.x > 480.0f - dragon.width ) dragon.x = 480.0f - dragon.width; if( dragon.y < 0.0f ) dragon.y = 0.0f; if( dragon.y > 480.0f - dragon.height ) dragon.y = 480.0f - dragon.height; } void reset_and_activate_guard(); int activate_overdrive(); void turn_all_enemy_shoots_into_gold(); void check_for_special_input() { if( special1_button() && dragon.powers[0] ) { turn_all_enemy_shoots_into_gold(); dragon.powers[0] = No; } if( special2_button() && dragon.powers[1] ) { if( activate_overdrive() ) dragon.powers[1] = No; } if( special3_button() && dragon.powers[2] ) { if( !guard.activated ) { dragon.powers[2] = No; reset_and_activate_guard(); } } } void ready_homing_blasts(); void activate_fullautoblast( int id ); void handle_mouse_input() { static int lb_down = 0; static int rb_down = 0; static int delay = 0; /* Process left mouse clicks */ if( shoot_button_down() ) { /* Signify that the left mouse button is down */ lb_down = 1; /* What firing mode are we using? */ if( dragon.attack_mode == 0 ) { /* Full auto plays the rapid fire sound effect */ if( ++delay == 3 ) { int fa_id = 0; play_sound_effect_static( &sfx[10], 0 ); fa_id = find_inactive_fullautoblast(); if( fa_id != -1 ) { activate_fullautoblast( fa_id ); } delay = 0; } } else { /* Lock mode plays the lock sound effect when new locks are aquired (not in this function) and the homing blast sound effect when the button is released */ } } else { /* Was the left mouse button just released? */ if( lb_down == 1 ) { lb_down = 0; /* Play the homing blast sound effect if there are any locks */ /* Also reset any locks */ if( active_locks_available() && active_homing_blasts() == 0 ) { play_sound_effect( &sfx[2], 0 ); ready_homing_blasts(); reset_all_locks( TRUE ); } } } } void draw_enemies() { #if 0 struct enemy_t e; static frame = 0; e.dead = 0; e.frame = frame; e.lock = 0; e.sx = 32.0f; e.sy = 32.0f; e.type = ENEMY_PARTICLE; e.x = 160.0f; e.y = 50.0f; draw_particle( &e ); if( ++frame > 29 ) frame = 0; #else int i = 0; /* Go through list of enemies */ while( i < MAX_ENEMIES ) { /* Is this enemy active/alive? */ if( !enemies[i].dead ) { /* If so, render it */ /* Flash if flashing */ if( enemies[i].flash ) flash_white( Yes ); switch( enemies[i].type ) { case ENEMY_PARTICLE: draw_particle( &enemies[i] ); break; case ENEMY_REDDRAGON: draw_dragon( &enemies[i], 10 ); break; case ENEMY_GREENDRAGON: draw_dragon( &enemies[i], 0 ); break; case ENEMY_BLUEDRAGON: draw_dragon( &enemies[i], 15 ); break; case ENEMY_BLACKDRAGON: draw_dragon( &enemies[i], 5 ); break; case ENEMY_FAIRY: draw_fairy( &enemies[i] ); break; case ENEMY_ZEPPELIN: draw_zeppelin( &enemies[i] ); break; case ENEMY_TANK: draw_tank( &enemies[i] ); break; case ENEMY_DRONE: draw_drone( &enemies[i] ); break; default: break; } /* Stop flashing */ if( enemies[i].flash ) flash_white( No ); /* Stop flashing */ enemies[i].flash = No; /* Update the sprite's key frame animation */ if( enemies[i].timer++ >= enemies[i].max_timer ) { enemies[i].timer = 0; if( ++enemies[i].frame >= enemies[i].max_frame ) enemies[i].frame = 0; } /* Move this enemy according to it's movement pattern */ if( enemies[i].movement == EMOVEMENT_SPLINE ) { /* TODO: Move enemy along it's spline. */ /* TODO: When enemy moves offscreen to it's final destination. mark it as dead, but obviously give no score for it either */ if( move_position_on_spline( &spline ) ) enemies[i].dead = Yes; else { struct Vector2 v; get_current_position_on_spline( &spline, &v ); enemies[i].x = v.x; enemies[i].y = v.y; } } else if( enemies[i].movement == EMOVEMENT_STILL ) { /* Move the enemy with the screen */ enemies[i].y += scroll_speed; /* When the enemy has gone below the screen, mark it as inactive */ if( enemies[i].y > 480.0f ) enemies[i].dead = Yes; } /* Check for dying enemies */ if( enemies[i].dying && !enemies[i].dead ) { /* Dying enemies (such as zeppelins) need special attention */ if( enemies[i].type == ENEMY_ZEPPELIN ) { static int interval = 0; /* Decrease ship size to make it look like it's falling */ enemies[i].sx -= 1.0f; enemies[i].sy -= 1.0f; enemies[i].y += 0.5f; /* Add a new smoke puff every 10 frames */ if( interval <= 0 ) { add_smoke_puff( enemies[i].x, enemies[i].y, SMOKEANIM_RISE ); interval = 10; } else interval--; /* Is the zeppelin small enough now? */ if( enemies[i].sx < ( 76.0f/3.0f ) && enemies[i].sy < ( 100.0f/3.0f ) ) { enemies[i].dead = Yes; kill_enemy( &enemies[i], 9 ); } } } /* Enemy specific updates */ if( enemies[i].type == ENEMY_DRONE ) update_drone( &enemies[i] ); } i++; } #endif } void reset_and_activate_guard() { /* Reset and activate the guard ability */ guard.activated = Yes; guard.angle = 0.0f; guard.dist = 1.0f; guard.expired = No; guard.timer = 1000; } void draw_guard_if_activated() { int i = 0; float size = 24.0f; if( !guard.activated ) return 0; /* Draw guard crystals */ enable_2d(); transparent_blend( Yes ); while( i < 3 ) { push_pos(); translate( guard.x[i], guard.y[i] ); draw_quad( get_texture( "guard" ), -(size/2.0f), -(size/2.0f), size, size ); pop_pos(); i++; } transparent_blend( No ); disable_2d(); } void update_guard_if_activated() { int i = 0; if( !guard.activated ) return 0; /* Update the angle that the guard crystal is rotating */ guard.angle += 0.1f; /* Move further outward if the maximum distance was not reached */ if( guard.dist < 70.0f && !guard.expired ) guard.dist++; /* Move inward if the guard is expiring */ if( guard.expired && guard.dist > 0.0f ) guard.dist--; /* Deactivate the guard if finished */ if( guard.expired && guard.dist <= 0.0f ) guard.activated = No; /* Set the position of all 3 guard crystals */ while( i < 3 ) { guard.x[i] = guard.dist * cos( ( i * 2.0f * 3.14f / 3.0f ) + guard.angle ) + ( dragon.x + ( dragon.width / 2.0f ) ); guard.y[i] = guard.dist * sin( ( i * 2.0f * 3.14f / 3.0f ) + guard.angle ) + ( dragon.y + ( dragon.height / 2.0f ) ); i++; } /* Update expiration timer */ if( --guard.timer <= 0 ) guard.expired = Yes; i = 0; /* Check each guard crystal for a collision */ while( i < 3 ) { int j = 0; /* Check each enemy for a collision test */ while( j < MAX_ENEMIES ) { /* Is this enemy alive so we can kill it? */ if( !enemies[j].dead ) { /* It is, let's check to see if the guard crystal did it's job */ if( guard.x[i] >= enemies[j].x && guard.x[i] <= enemies[j].x+enemies[j].sx && guard.y[i] >= enemies[j].y && guard.y[i] <= enemies[j].y+enemies[j].sy ) { /* Do 5 damage */ enemies[j].energy -= 25; /* Is this enemy dead? */ if( enemies[j].energy <= 0 ) { /* Enemy zeppelin's don't die automatically */ if( enemies[j].type == ENEMY_ZEPPELIN ) enemies[j].dying = Yes; else { /* Katsu! */ kill_enemy( &enemies[j], 7 ); } } else { /* If not, just flash white for a frame */ enemies[j].flash = Yes; } /* Let homing blast go */ // homingblasts[i].target = -1; // homingblasts[i].fade_timer = 200; } } j++; } i++; } } int activate_overdrive() { /* Activates overdrive! */ /* Don't activate overdrive if the timer from the previous session has not expired. If it hasn't return 0 so that the power up is not marked as used until this session is over! */ if( overdrive_timer > 0 ) return 0; overdrive_timer = 500; } void update_overdrive_if_active() { /* If the overdrive power up is active, decrease the timer */ if( overdrive_timer > 0 ) overdrive_timer--; } void reset_powerups() { memset( powerups, 0, sizeof( struct powerup_t ) * 4 ); } void give_possible_powerup( float x, float y ) { /* This function should be called every time an applicable targetable enemy is destroyed. It randomly decides wither the player gets a power up to recieve. There is a 1 in 20 chance of getting a power up, but the odds for getting certain ones can vary. The odds table contains a list of powerups. The less frequent the powerup is on the table, the less likely you are to get that one. Heart #2 is designed to the the rarest. */ int i = 0; int odds[10] = { 1, 1, 1, 2, 2, 3, 3, 4, 4, 5 }; /* Check odds of actually getting anything.... */ if( (rand()%5) != 1 ) return; /* *BUZZER!* Not this time. */ /* *DING DING DING!* We have a winner! */ while( i < 4 ) { /* Search the list for an inactive powerup. If we find one, activate it */ if( !powerups[i].active ) { powerups[i].active = Yes; powerups[i].x = x; powerups[i].y = y; powerups[i].type = odds[(rand()%10)]; powerups[i].vx = 3.0f; powerups[i].vy = 3.0f; powerups[i].sx = powerups[i].sy = 32.0f; return; } i++; } } void draw_powerups() { int i = 0; char string[16]; enable_2d(); transparent_blend( Yes ); /* Render each power up */ while( i < 4 ) { /* Is it active? */ if( powerups[i].active ) { /* get the actual texture name */ switch( powerups[i].type ) { case POWER_CRYSTAL1: sprintf( string, "diamond1.tga" ); break; case POWER_CRYSTAL2: sprintf( string, "diamond2.tga" ); break; case POWER_CRYSTAL3: sprintf( string, "diamond3.tga" ); break; case POWER_HEART1: sprintf( string, "heart1.tga" ); break; case POWER_HEART2: sprintf( string, "heart2.tga" ); break; } /* Now actually render the powerup sprite */ push_pos(); translate( powerups[i].x, powerups[i].y ); draw_quad( get_texture( string ), -16.0f, -16.0f, 32.0f, 32.0f ); pop_pos(); } i++; } transparent_blend( Yes ); disable_2d(); } void update_powerups() { int i = 0; /* Update each active powerup */ while( i < 4 ) { /* Only if it's active... */ if( powerups[i].active ) { /* Move the powerup icon around */ powerups[i].x += powerups[i].vx; powerups[i].y += powerups[i].vy; /* Check boundaries */ if( powerups[i].x <= 160.0f ) powerups[i].vx *= -1.0f; if( powerups[i].x >= 480.0f ) powerups[i].vx *= -1.0f; if( powerups[i].y <= 0.0f || powerups[i].y >= 480.0f ) powerups[i].vy *= -1.0f; /* Check for collision with player */ if( powerups[i].x >= dragon.x && powerups[i].x <= dragon.x + dragon.width && powerups[i].y >= dragon.y && powerups[i].y <= dragon.y + dragon.height ) { /* Deactivate powerup */ powerups[i].active = No; /* Play the confirmation sound */ play_sound_effect_static( &sfx[0], 0 ); /* Get the actual texture name */ switch( powerups[i].type ) { case POWER_CRYSTAL1: dragon.powers[0] = Yes; break; case POWER_CRYSTAL2: dragon.powers[1] = Yes; break; case POWER_CRYSTAL3: dragon.powers[2] = Yes; break; case POWER_HEART1: dragon.energy += 25; if( dragon.energy > dragon.max_energy ) dragon.energy = dragon.max_energy; break; case POWER_HEART2: dragon.max_energy += 25; dragon.energy += 25; break; } } } i++; } } void reset_enemy_shoots() { memset( enemyshoots, 0, sizeof( struct enemyshoot_t ) * MAX_ENEMYSHOOTS ); } int find_inactive_enemy_shoots() { int i = 0; /* Traverse through the list of enemy shoots and find the first inactive shoot */ while( i < MAX_ENEMYSHOOTS ) { if( !enemyshoots[i].active ) return i; i++; } return -1; } void add_new_enemy_shoot( float x, float y, float speed, int type ) { /* Activate a new enemy shoot. If none are free, abort */ /* TODO: Respond to above issue if we run out of shoots to make more space later */ int shoot = find_inactive_enemy_shoots(); /* Verify we have any inactive shoots */ if( shoot == -1 ) return; /* Update the number of active shoots */ active_shoots++; /* Activate this shoot */ enemyshoots[shoot].active = Yes; enemyshoots[shoot].x = x; enemyshoots[shoot].y = y; enemyshoots[shoot].angle = angle( x, y, dragon.x+(dragon.width/2.0f), dragon.y+(dragon.height/2.0f) ); enemyshoots[shoot].dx = cos( enemyshoots[shoot].angle ); enemyshoots[shoot].dy = sin( enemyshoots[shoot].angle ); enemyshoots[shoot].speed = speed; enemyshoots[shoot].type = type; /*fullautoblasts[id].active = Yes; fullautoblasts[id].x = dragon.x + (dragon.width/2.0f); fullautoblasts[id].y = dragon.y + (dragon.height/2.0f); fullautoblasts[id].size = ( overdrive_timer > 0 ) ? 64.0f : 32.0f; fullautoblasts[id].angle = angle( dragon.x+(dragon.width/2.0f), dragon.y+(dragon.height/2.0f), ch.x, ch.y ); fullautoblasts[id].dirx = cos( fullautoblasts[id].angle ); fullautoblasts[id].diry = sin( fullautoblasts[id].angle ); fullautoblasts[id].speed = 15.0f;*/ /* TODO: Handle animations, etc */ /* TODO: Different shot types */ switch( type ) { case 0: /* Standard */ enemyshoots[shoot].damage = 15; enemyshoots[shoot].frame = 0; enemyshoots[shoot].max_frame = 0; enemyshoots[shoot].sx = 12.0f; enemyshoots[shoot].sy = 22.0f; break; case 1: /* Cannon ball */ enemyshoots[shoot].damage = 30; break; } } void draw_enemy_shoots() { int i = 0; /* Draw each enemy shoot */ enable_2d(); transparent_blend( Yes ); while( i < MAX_ENEMYSHOOTS ) { /* Is this shoot active? */ if( enemyshoots[i].active ) { push_pos(); translate( enemyshoots[i].x, enemyshoots[i].y ); rotate( enemyshoots[i].angle*(180.0f/3.14f)+90.0f ); draw_quad( get_texture( "shot1" ), -(enemyshoots[i].sx/2.0f), -(enemyshoots[i].sy/2.0f), enemyshoots[i].sx, enemyshoots[i].sy ); pop_pos(); } i++; } transparent_blend( No ); disable_2d(); } void update_enemy_shoots() { int i = 0; /* Update each enemy shoot */ while( i < MAX_ENEMYSHOOTS ) { if( enemyshoots[i].active ) { float x1 = (dragon.x+(dragon.width/2.0f)-4.0f); float x2 = (dragon.x+(dragon.width/2.0f)+4.0f); float y1 = (dragon.y+(dragon.height/2.0f)-4.0f); float y2 = (dragon.y+(dragon.height/2.0f)+4.0f); /* Move each shoot in it's designated direction */ enemyshoots[i].x += enemyshoots[i].dx * enemyshoots[i].speed; enemyshoots[i].y += enemyshoots[i].dy * enemyshoots[i].speed; /* Deactivate any shoots that go out of the boundaries */ if( enemyshoots[i].x < 160.0f || enemyshoots[i].x > 480.0f || enemyshoots[i].y < 0.0f || enemyshoots[i].y > 480.0f ) { enemyshoots[i].active = No; active_shoots--; } /* Check for collisions with player dragon */ if( enemyshoots[i].x > x1 && enemyshoots[i].x < x2 && enemyshoots[i].y > y1 && enemyshoots[i].y < y2 ) { /* Oh no, we've been hit! */ /* Don't do damage if we are flashing */ if( dragon.inv_t == 0 ) { dragon.energy -= enemyshoots[i].damage; dragon.inv_t = 100; } if( dragon.energy < 0 ) dragon.energy = 0; enemyshoots[i].active = No; active_shoots--; } /* Check for collisions with guard crystals if active */ if( guard.activated && enemyshoots[i].active ) { /* TODO: FIXME!!! */ int j = 0; while( j < 3 ) { float gx1 = (guard.x[i]-12.0f); float gy1 = (guard.y[i]-12.0f); float gx2 = (guard.x[i]+12.0f); float gy2 = (guard.y[i]+12.0f); /*enable_2d(); wireframe(Yes); draw_quad( 0, gx1, gy1, 24, 24 ); wireframe(No); disable_2d();*/ /* Did it collide? */ if( enemyshoots[i].x > gx1 && enemyshoots[i].x < gx2 && enemyshoots[i].y > gy1 && enemyshoots[i].y < gy2 ) { /* If so, deactivate shot */ enemyshoots[i].active = No; active_shoots--; break; } j++; } } } i++; } } void check_enemy_shoots_for_collisions() { /* TODO */ } void turn_all_enemy_shoots_into_gold() { /* Turn all active enemy shoots into gold peices */ int i = 0; while( i < MAX_ENEMYSHOOTS ) { if( enemyshoots[i].active ) { enemyshoots[i].active = No; active_shoots--; add_gold_peice( enemyshoots[i].x, enemyshoots[i].y ); } i++; } } void init_boss( int level ) { /* Setup the boss structure depending on what boss we are facing */ memset( &boss, 0, sizeof( struct boss_t ) ); /* Mask */ if( level == 1 ) { boss.x = 320.0f /*- (365.0f/2.0f)*/; boss.y = 150.0f; boss.sx = 365.0f; boss.sy = 285.0f; boss.max_energy = 1000.0f; boss.energy = 900.0f; boss.type = BOSS_MASK; boss.lock_points = 8; } boss_mode = Yes; } void draw_boss() { float f = 0.0f; if( boss_mode ) { enable_2d(); transparent_blend(Yes); push_pos(); translate( boss.x, boss.y ); draw_quad( get_texture( "boss1" ), -boss.sx/2.0f, -boss.sy/2.0f, boss.sx, boss.sy ); pop_pos(); f = (boss.energy/boss.max_energy) * 300.0f; set_colour( 1.0f, 0.0f, 0.0f, 0.5f ); translate( 160.0f, 20.0f ); draw_quad( 0, 10.0f, 0, f, 8.0f ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_line( 10.0f, 0.0f, 310.0f, 0.0f ); draw_line( 10.0f, 8.0f, 310.0f, 8.0f ); draw_line( 10.0f, 0.0f, 10.0f, 8.0f ); draw_line( 310.0f, 0.0f, 310.0f, 8.0f ); transparent_blend(No); disable_2d(); } } void update_boss() { } int init_textures() { HFASTFILE ff; struct _iobuf* fp; struct _iobuf* out; void* buffer; int i = 0; int failed = 0; /* Try to open data00.dat */ out = fopen( "data00.dat", "r" ); /* If it's there, read the fastfile attributes from there. */ /* If not, then either create it ourselves or just fail (release builds) */ if( !out ) { /* Create a data file to contain the sizes of each texture */ out = fopen( "data00.dat", "w" ); fprintf( out, "files: %d\n\n", MAX_TEXTURES ); /* Retrieve the file size of each texture (TEMPORARY) */ while( i < MAX_TEXTURES ) { char filename[128]; sprintf( filename, "..\\Media\\textures\\%s", tex_files[i] ); fp = fopen( filename, "rb" ); if( !fp ) { MessageBox( NULL, filename, "Pale Dragon", MB_OK ); return 0; } fseek( fp, 0, SEEK_END ); tex_file_sizes[i] = ftell( fp ); if( out ) fprintf( out, "%d\n", tex_file_sizes[i] ); fclose(fp); i++; } } else { int files = 0; /* Get the number of files within this fastfile */ fscanf( out, "files: %d\n\n", &files ); /* Get the sizes of each file within this fastfile */ while( i < files ) { fscanf( out, "%d\n", &tex_file_sizes[i] ); i++; } } /* Close the data file */ if( out ) fclose( out ); /* Initialize the fastfile */ if( !FastFileInit( "data00.bin", MAX_TEXTURES ) ) return 0; /* Start loading files from the fast file */ i = 0; while( i < MAX_TEXTURES ) { /* Get a handle to the file */ ff = FastFileOpen( tex_files[i] ); if(ff) { /* If we find the file, allocate a buffer large enough so it can be read as a file in memory */ buffer = malloc( tex_file_sizes[i] ); /* Now actually read in the file's data */ FastFileRead( ff, buffer, tex_file_sizes[i] ); /* Now that we have a valid file pointer with data, create a texture from it */ textures[i] = create_texture_from_file( buffer, tex_files[i] ); /* Free the buffer */ free( buffer ); /* Close the fastfile handle */ FastFileClose( ff ); } else { failed++; } i++; } /* Uninitialize the fast file */ FastFileFini(); /* Show any failed texture loads */ if( failed ) { char string[64]; sprintf( string, "%d out of %d failed texture loads!", failed, MAX_TEXTURES ); MessageBox( NULL, string, "Pale Dragon", MB_OK ); } #if 0 /* TODO: Load textures from fastfile resource */ if( !( textures[TEX_CROSSHAIR1] = create_texture( "..\\Media\\textures\\crosshair1.tga" ) ) ) return 0; if( !( textures[TEX_CROSSHAIR2] = create_texture( "..\\Media\\textures\\crosshair2.tga" ) ) ) return 0; if( !( textures[TEX_DRAGON1] = create_texture( "..\\Media\\textures\\dragon1.tga" ) ) ) return 0; if( !( textures[TEX_DRAGON2] = create_texture( "..\\Media\\textures\\dragon2.tga" ) ) ) return 0; if( !( textures[TEX_HEALTH] = create_texture( "..\\Media\\textures\\heart.tga" ) ) ) return 0; if( !( textures[TEX_POWERUP1] = create_texture( "..\\Media\\textures\\diamond1.tga" ) ) ) return 0; if( !( textures[TEX_POWERUP2] = create_texture( "..\\Media\\textures\\diamond2.tga" ) ) ) return 0; if( !( textures[TEX_POWERUP3] = create_texture( "..\\Media\\textures\\diamond3.tga" ) ) ) return 0; if( !( textures[TEX_BACKGROUND] = create_texture( "..\\Media\\textures\\bkg1.tga" ) ) ) return 0; if( !( textures[TEX_HOMINGSHOT] = create_texture( "..\\Media\\textures\\hblast.tga" ) ) ) return 0; if( !( textures[TEX_SHOT] = create_texture( "..\\Media\\textures\\shot.tga" ) ) ) return 0; if( !( textures[TEX_REDSHOT] = create_texture( "..\\Media\\textures\\fbred.tga" ) ) ) return 0; if( !( textures[TEX_GREENSHOT] = create_texture( "..\\Media\\textures\\fbgreen.tga" ) ) ) return 0; if( !( textures[TEX_PARTICLE] = create_texture( "..\\Media\\textures\\particle.tga" ) ) ) return 0; if( !( textures[TEX_EXPLOSION] = create_texture( "..\\Media\\textures\\explosion.tga" ) ) ) return 0; if( !( textures[TEX_DIRT] = create_texture( "..\\Media\\textures\\dirt.tga" ) ) ) return 0; if( !( textures[TEX_SAND] = create_texture( "..\\Media\\textures\\sand.tga" ) ) ) return 0; if( !( textures[TEX_GRASS] = create_texture( "..\\Media\\textures\\grass.tga" ) ) ) return 0; if( !( textures[TEX_WATER] = create_texture( "..\\Media\\textures\\water.tga" ) ) ) return 0; if( !( textures[TEX_INDOOR1] = create_texture( "..\\Media\\textures\\intile1.tga" ) ) ) return 0; if( !( textures[TEX_INDOOR2] = create_texture( "..\\Media\\textures\\intile2.tga" ) ) ) return 0; if( !( textures[TEX_INDOOR2D] = create_texture( "..\\Media\\textures\\intile2d.tga" ) ) ) return 0; if( !( textures[TEX_INDOOR3] = create_texture( "..\\Media\\textures\\intile3.tga" ) ) ) return 0; if( !( textures[TEX_INDOOR3D] = create_texture( "..\\Media\\textures\\intile3d.tga" ) ) ) return 0; if( !( textures[TEX_PALMTREE] = create_texture( "..\\Media\\textures\\palmtree.tga" ) ) ) return 0; if( !( textures[TEX_PINETREE] = create_texture( "..\\Media\\textures\\pinetree.tga" ) ) ) return 0; #endif return 1; } void uninit_textures() { delete_textures( MAX_TEXTURES, textures ); } unsigned int get_texture( char* texname ) { int i = 0; /* Loop through each texture and find one with a matching name. */ /* When the '.' signifying the end of the texture's name is reached. */ while( i < MAX_TEXTURES ) { int c = 0; int match = 1; char str[16]; /* Test the file name against the current one on the list */ strcpy( str, tex_files[i] ); while( str[c] != '.' ) { if( str[c] != texname[c] ) { match = 0; break; } c++; } /* If it matches, return that texture */ if( match ) return textures[i]; i++; } return 0; } int init_soundfx() { HFASTFILE ff = NULL; struct _iobuf* fp = NULL; struct _iobuf* out = NULL; int i = 0; int failed = 0; #if 0 /* Try to open data01.dat */ out = fopen( "data01.dat", "r" ); /* If it's there, read the fastfile attributes from there. */ /* If not, then either create it ourselves or just fail (release builds) */ if( !out ) { /* Create a data file to contain the sizes of each wav file */ out = fopen( "data01.dat", "w" ); fprintf( out, "files: %d\n\n", MAX_SOUNDFX ); /* Retrieve the file size of each wav file (TEMPORARY) */ while( i < MAX_SOUNDFX ) { char filename[128]; sprintf( filename, "..\\Media\\sounds\\%s", sfx_files[i] ); fp = fopen( filename, "rb" ); if( !fp ) { MessageBox( NULL, filename, "Pale Dragon", MB_OK ); return 0; } fseek( fp, 0, SEEK_END ); tex_file_sizes[i] = ftell( fp ); if( out ) fprintf( out, "%d\n", sfx_file_sizes[i] ); fclose(fp); i++; } } else { int files = 0; /* Get the number of files within this fastfile */ fscanf( out, "files: %d\n\n", &files ); /* Get the sizes of each file within this fastfile */ while( i < files ) { fscanf( out, "%d\n", &tex_file_sizes[i] ); i++; } } /* Close the data file */ if( out ) fclose( out ); /* Initialize the fastfile */ if( !FastFileInit( "data01.bin", MAX_SOUNDFX ) ) return 0; /* Start loading files from the fast file */ i = 0; while( i < MAX_SOUNDFX ) { /* Get a handle to the file */ ff = FastFileOpen( sfx_files[i] ); if(ff) { /* If we find the file, allocate a buffer large enough so it can be read as a file in memory */ ALubyte* buffer = (ALubyte*) malloc( sfx_file_sizes[i] ); /* Now actually read in the file's data */ FastFileRead( ff, buffer, sfx_file_sizes[i] ); /* Now that we have a valid file pointer with data, create a wav file from it */ sfx[i].position.x = 0.0f; sfx[i].position.y = 0.0f; sfx[i].position.z = 0.0f; sfx[i].velocity.z = 0.0f; sfx[i].velocity.y = 0.0f; sfx[i].velocity.x = 0.0f; sfx[i].pitch = 1.0f; if( !create_sound_wav_from_memory( buffer, &sfx[i] ) ) failed++; /* Free the buffer */ free( buffer ); /* Close the fastfile handle */ FastFileClose( ff ); } else { failed++; } i++; } /* Uninitialize the fast file */ FastFileFini(); /* Show any failed wav loads */ if( failed ) { char string[64]; sprintf( string, "%d out of %d failed wav loads!", failed, MAX_SOUNDFX ); MessageBox( NULL, string, "Pale Dragon", MB_OK ); } #else struct sound_t snd; /* Set default values for sound effects */ while( i < MAX_SOUNDFX ) { memcpy( &sfx[i], &snd, sizeof( struct sound_t ) ); i++; } /* Load each sound effect from disk */ i = 0; if( !create_sound_wav( "..\\media\\sounds\\crystal3.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\firefa.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\hblast.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\lock.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\mmove.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\msel.wav", &sfx[i] ) ) return 0; i++; // if( !create_sound_au( "..\\media\\sounds\\msel.au", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\woosh2.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\expl1s.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\gold2.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\expl2s.wav", &sfx[i] ) ) return 0; i++; if( !create_sound_wav( "..\\media\\sounds\\firefa2.wav", &sfx[i] ) ) return 0; i++; #endif play_sound_effect_static( &sfx[5], 0 ); return 1; } void uninit_soundfx() { int i = 0; while( i < MAX_SOUNDFX ) { delete_sound( &sfx[i] ); i++; } } int init_fonts() { int size = 0; /* Initialize fonts */ memset( fonts, 0, sizeof( struct font_t ) * 7 ); /* Small font */ fonts[0].has_colour_key = 1; fonts[0].width = 160.0f; fonts[0].height = 64.0f; fonts[0].letter_width = 160.0f/20.0f; fonts[0].letter_height = 60.0f/5.0f; fonts[0].letter_offset = 0; fonts[0].texname = "sfont.tga"; fonts[0].texsize = 40991; if( !init_font( &fonts[0] ) ) return 0; /* Medium font */ fonts[1].has_colour_key = 1; fonts[1].width = 256.0f; fonts[1].height = 256.0f; fonts[1].letter_width = 16.0f; fonts[1].letter_height = 16.0f; fonts[1].letter_offset = 5.0f; fonts[1].texname = "mfont.tga"; fonts[1].texsize = 262175; if( !init_font( &fonts[1] ) ) return 0; /* Medium font */ fonts[2].has_colour_key = 1; fonts[2].width = 512.0f; fonts[2].height = 512.0f; fonts[2].letter_width = 32.0f; fonts[2].letter_height = 32.0f; fonts[2].letter_offset = 16.0f; fonts[2].texname = "lfont.tga"; fonts[2].texsize = 1048607; if( !init_font( &fonts[2] ) ) return 0; /* Small font 2 */ fonts[FONT_SMALL2].has_colour_key = 1; fonts[FONT_SMALL2].width = 181.0f; fonts[FONT_SMALL2].height = 56.0f; fonts[FONT_SMALL2].letter_width = 9.0f;//180.0f/20.0f; fonts[FONT_SMALL2].letter_height = 11.0f;//55.0f/5.0f; fonts[FONT_SMALL2].letter_offset = 1.0f; fonts[FONT_SMALL2].texname = "sfont2.tga"; fonts[FONT_SMALL2].texsize = 11883;//39644; fonts[FONT_SMALL2].char_offset = 0;//249; if( !init_font( &fonts[FONT_SMALL2] ) ) return 0; /* Menu font */ fonts[FONT_MENU].has_colour_key = 1; fonts[FONT_MENU].width = 258.0f; fonts[FONT_MENU].height = 99.0f; fonts[FONT_MENU].letter_width = 258.0f/16.0f; fonts[FONT_MENU].letter_height = 99.0f/6.0f; fonts[FONT_MENU].letter_offset = 6.0f; fonts[FONT_MENU].texname = "menufont.tga"; fonts[FONT_MENU].texsize = 102199; if( !init_font( &fonts[FONT_MENU] ) ) return 0; /* Violet font */ fonts[FONT_BOLD].has_colour_key = 1; fonts[FONT_BOLD].width = 255.0f; fonts[FONT_BOLD].height = 109.0f; fonts[FONT_BOLD].letter_width = 255.0f/16.0f; fonts[FONT_BOLD].letter_height = 109.0f/6.0f; fonts[FONT_BOLD].letter_offset = 0.0f; fonts[FONT_BOLD].texname = "7X8B2.tga"; fonts[FONT_BOLD].texsize = 111211; if( !init_font( &fonts[FONT_BOLD] ) ) return 0; /* Steel font */ fonts[FONT_STEEL].has_colour_key = 1; fonts[FONT_STEEL].width = 225.0f; fonts[FONT_STEEL].height = 79.0f; fonts[FONT_STEEL].letter_width = 225.0f/16.0f; fonts[FONT_STEEL].letter_height = 79.0f/6.0f; fonts[FONT_STEEL].letter_offset = 5.0f; fonts[FONT_STEEL].texname = "fsteel.tga"; fonts[FONT_STEEL].texsize = 71144; if( !init_font( &fonts[FONT_STEEL] ) ) return 0; return 1; } void uninit_fonts() { int i = 0; while( i < 7 ) { uninit_font( &fonts[i] ); i++; } } int init_splines( int stage ) { /* Initialise splines for a given stage */ char dir[32]; int i = 0; struct _iobuf* fp = NULL; struct Vector2 p[32]; sprintf( dir, "st%02d\\spline002.dat", stage ); fp = fopen( dir, "rb" ); if( !fp ) return 0; fread( &spline.num_base_points, 1, sizeof( int ), fp ); fread( &spline.tesselation, 1, sizeof( int ), fp ); fread( p, 1, sizeof( struct Vector2 ) * spline.num_base_points, fp ); fclose(fp); return create_spline( &spline, p, spline.num_base_points, spline.tesselation, 5.0f ); } void uninit_splines() { delete_spline( &spline ); } void reset_homing_blasts(); void reset_fullautoblasts(); int init_game() { /*if(AllocConsole()) { freopen("CONOUT$", "wt", stdout); SetConsoleTitle(L"MyApp : Debug Console"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); }*/ /* TEST: Initialize splines */ if( !init_splines(0) ) return 0; /* Seed random number generator */ srand( time( NULL ) ); /* Reset the player's dragon attributes */ reset_dragon(); /* Reset lock icons */ reset_all_locks( FALSE ); /* Reset all enemies */ reset_enemies(); /* Reset all homing blasts */ reset_homing_blasts(); /* Reset all fullauto blasts */ reset_fullautoblasts(); /* Reset explosions */ memset( explosions, 0, sizeof( struct explosion_t ) ); /* Reset powerups */ reset_powerups(); /* Reset smoke puffs */ reset_smoke_puffs(); /* Reset enemy shoots */ reset_enemy_shoots(); /* TEST: Boss */ if( !boss_mode ) init_boss(1); /* Set input type */ set_input_type(0); /* Reset mouse position */ center_mouse(); /* Create a render target */ render_target = create_texture_rectangle( 640, 480, 32 ); if( !render_target ) return 0; /* Initialize gold cache */ if( !init_gold_cache( 2000 ) ) return 0; /* Initialize textures */ if( !init_textures() ) return 0; /* Initialize sound effects */ if( !init_soundfx() ) return 0; /* Initialize fonts */ if( !init_fonts() ) return 0; /* Initialize menu data */ if( !init_menus() ) return 0; /* BGM test */ if( !wavestream_open( "00.wav", &bgm ) ) return 0; // wavestream_play(&bgm); return 1; } void uninit_game() { /* Uninit textures and sound effects */ uninit_splines(); wavestream_close(&bgm); uninit_menus(); uninit_fonts(); uninit_soundfx(); uninit_textures(); uninit_gold_cache(); delete_textures( 1, &render_target ); } void draw_background() { /* Draw the background image */ enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_quad( get_texture( tex_files[background+2] ), 0.0f, 0.0f, 640.0f, 480.0f ); disable_2d(); } void draw_environment() { float tex[] = { 0.0f, 10.0f, 15.0f, 10.0f, 15.0f, 0.0f, 0.0f, 0.0f }; static float h1 = 0; static float h2 = -480.0f; static int setup_trees_n_stuff = 0; /* Temporary */ int i = 0; // static float tree_x = 160.0f;//(float) ( rand() % 320 ); // static float tree_y = -121.0f; /* Place trees at random places */ if( !setup_trees_n_stuff ) { i = 0; setup_trees_n_stuff = 1; while( i < 8 ) { trees[i].x = (float) ( rand() % ( 320 - 121 ) ) + 160; trees[i].y = (float) -( rand() % 480 ) + 121; i++; } } /* Draw the menu and ingame boundaries */ enable_2d(); /* First half of the scrolling effect */ push_pos(); translate( 640.0f/4.0f, h1 ); set_colour( 0.0f, 0.0f, 0.0f, 1.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f/2.0f, 480.0f ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_quad2( get_texture( "grass1a" ), tex, 0.0f, 0.0f, 640.0f/2.0f, 480.0f ); pop_pos(); /* Second half of the scrolling effect */ push_pos(); translate( 640.0f/4.0f, h2 ); // set_colour( 0.0f, 0.0f, 0.0f, 1.0f ); // draw_quad( 0, 0.0f, 0.0f, 640.0f/2.0f, 480.0f ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_quad2( get_texture( "grass1a" ), tex, 0.0f, 0.0f, 640.0f/2.0f, 480.0f ); pop_pos(); push_pos(); // translate( 640.0f/4.0f, 0.0f ); transparent_blend( TRUE ); i = 0; while( i < 8 ) { draw_quad( get_texture( "palmtree" ), trees[i].x, trees[i].y, 101.0f, 121.0f ); i++; } transparent_blend( FALSE ); pop_pos(); disable_2d(); h1 += scroll_speed; h2 += scroll_speed; if( h1 >= 480.0f ) h1 = 0.0f; if( h2 >= 0.0f ) h2 = -480.0f; /* Update trees 'n stuff */ if( setup_trees_n_stuff ) { i = 0; while( i < 8 ) { trees[i].y += scroll_speed; if( trees[i].y >= 480.0f ) { trees[i].y = (float) -(( rand() % 480 ) + 121); trees[i].x = (float) (rand() % (320-121))+160; } i++; } } } void draw_boundaries() { float tex1[] = { 0.0f, 1.0f, 0.25f, 1.0f, 0.25f, 0.0f, 0.0f, 0.0f }; float tex2[] = { 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.75f, 0.0f }; /* Draw the play area boundaries */ enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Helps hide things outside of the game's boundaries */ push_pos(); draw_quad2( get_texture( tex_files[background+2] ), tex1, 0, 0, 160, 480 ); translate( 480.0f, 0.0f ); draw_quad2( get_texture( tex_files[background+2] ), tex2, 0, 0, 160, 480 ); pop_pos(); push_pos(); translate( 640.0f/4.0f, 0.0f ); draw_line( 0.0f, 0.0f, 0.0f, 480.0f ); draw_line( 640.0f/2.0f+1.0f, 0.0f, 640.0f/2.0f+1.0f, 480.0f ); pop_pos(); disable_2d(); } void get_attack_mode_changes() { static int change = 0; /* Change attack mode when the right mouse button is clicked */ if( changefire_button_down() && !change ) { change = 1; dragon.attack_mode = !dragon.attack_mode; /* If we are switching to full auto, release all locks on any targeted enemies since it will be of no use to us. */ if( dragon.attack_mode == 0 ) reset_all_locks( TRUE ); } /* Only register one click */ if( !changefire_button_down() && change ) change = 0; } void draw_crosshair() { float size = dragon.attack_mode == 0 ? 128.0f : 64.0f; unsigned int ch_tex = dragon.attack_mode == 0 ? get_texture( "cross2" ) : get_texture( "cross1" ); float x = ch.x; float y = ch.y; if( x < 160.0f ) x = 160.0f; if( x > 480.0f ) x = 480.0f; if( y < 0.0f ) y = 0.0f; if( y > 480.0f ) y = 480.0f; /* Draw the crosshair */ enable_2d(); transparent_blend( TRUE ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); translate( x-(size/2.0f), y-(size/2.0f) ); draw_quad( ch_tex, 0.0f, 0.0f, size, size ); transparent_blend( FALSE ); disable_2d(); } void acquire_new_locks() { /* Only check for locks when in lock mode */ /* Don't check if we are using full auto */ if( dragon.attack_mode == 1 ) { int i = 0; /* Loop though the list of enemies and check for locks */ while( i < MAX_ENEMIES ) { /* Check all enemies that aren't dead and that don't already have a lock acquired */ if( !enemies[i].dead && !enemies[i].dying && enemies[i].lock == -1 && shoot_button_down() ) { /* Check to see of the crosshair has caught this enemy */ if( ch.x > enemies[i].x-(enemies[i].sx/2.0f) && ch.x < enemies[i].x+(enemies[i].sx/2.0f) ) { if( ch.y > enemies[i].y-(enemies[i].sy/2.0f) && ch.y < enemies[i].y+(enemies[i].sy/2.0f) ) { /* We got a lock! Now check if there are any free locks */ int lock = find_inactive_lock(); if( lock != -1 ) { /* We have a free lock! Release the shoot button and blow this sucker away! */ enemies[i].lock = lock; locks[lock].owner = i; activate_lock( lock, enemies[i].x, enemies[i].y ); play_sound_effect_static( &sfx[3], 0 ); } } } } i++; } } } void reset_homing_blasts() { int i = 0; /* Reset all homing blasts */ memset( homingblasts, 0, sizeof( struct homingblast_t ) * MAX_HOMINGBLASTS ); while( i < MAX_HOMINGBLASTS ) { homingblasts[i].target = -1; homingblasts[i].fade_timer = 200; homingblasts[i].sx = homingblasts[i].sy = 32.0f; i++; } } int active_homing_blasts() { int i = 0; int count = 0; /* Return the number of active homing blasts */ while( i < MAX_HOMINGBLASTS ) { if( homingblasts[i].target != -1 ) count++; i++; } return count; } void ready_homing_blasts() { int i = 0; int j = 0; /* Assign each targetable entity a seperate homing blast to destroy it */ /* Limit the number of homing blasts assigned to MAX_HOMINGBLASTS. */ while( i < MAX_ENEMIES ) { if( enemies[i].lock != -1 ) { homingblasts[j].target = i; homingblasts[j].fade_timer = 200; homingblasts[j].x = dragon.x+(dragon.width/2.0f); homingblasts[j++].y = dragon.y+(dragon.height/2.0f); if( j == MAX_HOMINGBLASTS ) break; } i++; } } void clamp_float( float* d, float min, float max ) { if( *d < min ) *d = min; if( *d > max ) *d = max; } #if 0 void test_track() { static struct Vector2 p = { 0.0f, 320.0f }; static struct Vector2 v = { 0.0f, 0.0f }; float angle_to_player = angle( p.x, p.y, dragon.x, dragon.y ); float speed = 10.0f; v.x += cos(angle_to_player); v.y += sin(angle_to_player); clamp_float( &v.x, -speed, speed ); clamp_float( &v.y, -speed, speed ); p.x += v.x; p.y += v.y; enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); transparent_blend(TRUE); draw_quad( get_texture("hblast2"), p.x+(106.0f/2.0f), p.y+(149.0f/2.0f), 32.0f, 32.0f ); transparent_blend(FALSE); disable_2d(); } #endif void draw_homing_blasts() { int i = 0; /* Draw each active homing blast */ enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); transparent_blend( TRUE ); while( i < MAX_HOMINGBLASTS ) { if( homingblasts[i].target != -1 ) { draw_quad( get_texture( "hblast2" ), homingblasts[i].x-(homingblasts[i].sx/2.0f), homingblasts[i].y-(homingblasts[i].sy/2.0f), homingblasts[i].sx, homingblasts[i].sy ); } i++; } transparent_blend( FALSE ); disable_2d(); } void update_homing_blasts() { int i = 0; while( i < MAX_HOMINGBLASTS ) { if( homingblasts[i].target != -1 ) { float angle_to_target = angle( homingblasts[i].x, homingblasts[i].y, enemies[homingblasts[i].target].x /*-(enemies[homingblasts[i].target].sx/2.0f)*/, enemies[homingblasts[i].target].y /*-(enemies[homingblasts[i].target].sy/2.0f)*/ ); float speed = 10.0f; float cx = 0, cy = 0; homingblasts[i].vx += cos(angle_to_target); homingblasts[i].vy += sin(angle_to_target); #if 0 clamp_float( &homingblasts[i].vx, -speed, speed ); clamp_float( &homingblasts[i].vy, -speed, speed ); #else cx = cos( angle_to_target ); cy = sin( angle_to_target ); // cx = (abs(cx)); // cy = (abs(cy)); clamp_float( &homingblasts[i].vx, -speed*cx, speed*cx ); clamp_float( &homingblasts[i].vy, -speed*cy, speed*cy ); #endif homingblasts[i].x += homingblasts[i].vx; homingblasts[i].y += homingblasts[i].vy; } /* If this homing blast expires, let it shrink and set it as inactive */ if( --homingblasts[i].fade_timer <= 0 ) { homingblasts[i].sx -= 0.5f; homingblasts[i].sy -= 0.5f; if( homingblasts[i].sx <= 1.0f ) { homingblasts[i].target = -1; homingblasts[i].fade_timer = 200; homingblasts[i].sx = homingblasts[i].sy = 32.0f; } } i++; } } void check_homing_blasts_for_collisions() { int i = 0; /* Check each homing blast for a collision with an enemy. */ /* If we get a hit, then mark the enemy as dead and the homing blast as inactive and stop updating it. */ while( i < MAX_HOMINGBLASTS ) { /* Is this homing blast even tracking anything? */ if( homingblasts[i].target != -1 ) { /* Looks like it is. Let's see if it hit anything! */ /* The collision rect is (should be) a constant 32x32 pixels which is centered around the center of the targetable entity. */ int j = 0; while( j < MAX_ENEMIES ) { /* Target only living enemies */ if( !enemies[j].dead && !enemies[j].dying ) { /* Good, it's alive. Now, did we actually hit it? */ if( homingblasts[i].x >= (enemies[j].x-(enemies[j].sx/2.0f)) && homingblasts[i].x <= (enemies[j].x+(enemies[j].sx/2.0f)) && homingblasts[i].y >= (enemies[j].y-(enemies[j].sy/2.0f)) && homingblasts[i].y <= (enemies[j].y+(enemies[j].sy/2.0f)) ) { /* Do 5 damage */ enemies[j].energy -= 5; /* Is this enemy dead? */ if( enemies[j].energy <= 0 ) { /* Enemy zeppelin's don't die automatically */ if( enemies[j].type == ENEMY_ZEPPELIN ) enemies[j].dying = Yes; else { /* Katsu! */ kill_enemy( &enemies[j], 7 ); } } else { /* If not, just flash white for a frame */ enemies[j].flash = Yes; } /* Let homing blast go */ homingblasts[i].target = -1; homingblasts[i].fade_timer = 200; homingblasts[i].sx = homingblasts[i].sy = 32.0f; break; } } j++; } } i++; } } void reset_fullautoblasts() { /* Reset all full auto blasts */ memset( fullautoblasts, 0, sizeof( struct fullautoblast_t ) * MAX_FULLAUTOBLASTS ); } int find_inactive_fullautoblast() { int i = 0; /* Finds an inactive fullauto blast. If we find one, return that id number. If not, return -1 for a failure to find one */ while( i < MAX_FULLAUTOBLASTS ) { if( !fullautoblasts[i].active ) return i; i++; } return -1; } void activate_fullautoblast( int id ) { /* Activate full auto blast and initialize the variables that make it "go"! */ fullautoblasts[id].active = Yes; fullautoblasts[id].x = dragon.x + (dragon.width/2.0f); fullautoblasts[id].y = dragon.y + (dragon.height/2.0f); fullautoblasts[id].size = ( overdrive_timer > 0 ) ? 64.0f : 32.0f; fullautoblasts[id].angle = angle( dragon.x+(dragon.width/2.0f), dragon.y+(dragon.height/2.0f), ch.x, ch.y ); fullautoblasts[id].dirx = cos( fullautoblasts[id].angle ); fullautoblasts[id].diry = sin( fullautoblasts[id].angle ); fullautoblasts[id].speed = 15.0f; } void draw_fullautoblasts() { int i = 0; /* Draw each active fullauto blast */ enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); transparent_blend( TRUE ); while( i < MAX_FULLAUTOBLASTS ) { if( fullautoblasts[i].active ) { if( distance2d( fullautoblasts[i].x, fullautoblasts[i].y, dragon.x+(dragon.width/2.0f), dragon.y+(dragon.height/2.0f) ) > 15.0f ) { draw_quad( get_texture( "hblast2" ), fullautoblasts[i].x - ( fullautoblasts[i].size/2.0f ), fullautoblasts[i].y - ( fullautoblasts[i].size/2.0f ), fullautoblasts[i].size, fullautoblasts[i].size ); } } i++; } transparent_blend( FALSE ); disable_2d(); } void update_fullautoblasts() { int i = 0; /* Update each fullauto blast */ while( i < MAX_FULLAUTOBLASTS ) { /* Is this blast active ? */ if( fullautoblasts[i].active ) { /* If so, then move it in the desired direction */ /* But before we do that, let's check to see if this blast is */ /* beyond the boundaries, or if it hit anything */ if( fullautoblasts[i].x <= 0 || fullautoblasts[i].x > 640 || fullautoblasts[i].y <= 0 || fullautoblasts[i].y > 480 ) { /* Out of bounds */ fullautoblasts[i].active = No; } fullautoblasts[i].x += fullautoblasts[i].dirx * fullautoblasts[i].speed; fullautoblasts[i].y += fullautoblasts[i].diry * fullautoblasts[i].speed; } i++; } } void check_fullauto_blasts_for_collisions() { int i = 0; /* Check each fullauto blast for a collision with any enemies */ while( i < MAX_FULLAUTOBLASTS ) { /* Don't check any inactive ones */ if( fullautoblasts[i].active ) { /* If it is, check it against each active enemy */ int j = 0; /* Check each enemy for a collision test */ while( j < MAX_ENEMIES ) { /* Is this enemy alive so we can kill it? */ if( !enemies[j].dead && !enemies[j].dying ) { /* It is, let's check to see if the full auto blast hit it */ if( fullautoblasts[i].x >= (enemies[j].x-(enemies[j].sx/2.0f)) && fullautoblasts[i].x <= (enemies[j].x+(enemies[j].sx/2.0f)) && fullautoblasts[i].y >= (enemies[j].y-(enemies[j].sy/2.0f)) && fullautoblasts[i].y <= (enemies[j].y+(enemies[j].sy/2.0f)) ) { /* Do 1 damage for normal, 2 for overdrive */ enemies[j].energy -= (overdrive_timer > 0) ? 2 : 1; /* Is this enemy dead? */ if( enemies[j].energy <= 0 ) { /* Enemy zeppelin's don't die automatically */ if( enemies[j].type == ENEMY_ZEPPELIN ) enemies[j].dying = Yes; else { /* Katsu! */ kill_enemy( &enemies[j], 7 ); } } else { /* If not, just flash white for a frame */ enemies[j].flash = Yes; } /* Let fullauto blast go */ fullautoblasts[i].active = No; } } j++; } } i++; } } void activate_explosion( int expl_id, float x, float y, int type ) { explosions[expl_id].active = Yes; explosions[expl_id].frame = 0; explosions[expl_id].last_frame = 24; explosions[expl_id].timer = 0; explosions[expl_id].max_timer = 2; explosions[expl_id].x = x; explosions[expl_id].y = y; explosions[expl_id].sx = 320.0f / 5.0f; explosions[expl_id].sy = 320.0f / 5.0f; explosions[expl_id].type = type; } int find_inactive_explosion() { int i = 0; /* Find the first inactive explosion */ while( i < MAX_EXPLOSIONS ) { /* Is this explosion active? If so, go to the next one. If not, then return that explosion id. */ if( !explosions[i].active ) return i; i++; } return -1; } void render_explosions() { int i = 0; /* Render each active explosion */ while( i < MAX_EXPLOSIONS ) { /* Is this explosion active? */ if( explosions[i].active ) { /* Draw it! */ draw_explosion( &explosions[i] ); } i++; } } void update_explosions() { int i = 0; /* Update each active explosion */ while( i < MAX_EXPLOSIONS ) { /* Is this explosion active? */ if( explosions[i].active ) { /* Is it time to update this explosion? */ if( ++explosions[i].timer >= explosions[i].max_timer ) { /* Okay, let's move on to the next frame, then check if we reached the final frame. */ explosions[i].timer = 0; if( ++explosions[i].frame >= explosions[i].last_frame ) { /* Now that we have reached the last frame, set this explosion as inactive to be reused later */ explosions[i].active = No; } } } i++; } } void reset_smoke_puffs() { memset( &smoke, 0, sizeof( struct smoke_t ) * MAX_SMOKE ); } void add_smoke_puff( float x, float y, int type ) { int i = 0; /* Search for an unused smoke puff */ while( i < MAX_SMOKE ) { /* Is this puff active? */ if( !smoke[i].active ) { /* Activate it */ smoke[i].x = x; smoke[i].y = y; smoke[i].sx = smoke[i].sy = 4.0f; smoke[i].active = Yes; smoke[i].type = type; break; } i++; } } void draw_smoke_puffs() { int i = 0; /* Draw each smoke puff */ enable_2d(); transparent_blend(Yes); while( i < MAX_SMOKE ) { if( smoke[i].active ) { push_pos(); translate( smoke[i].x, smoke[i].y ), rotate( smoke[i].rot ); draw_quad( get_texture( "smoke" ), -(smoke[i].sx/2.0f), -(smoke[i].sy/2.0f), smoke[i].sx, smoke[i].sy ); pop_pos(); } i++; } transparent_blend(No); disable_2d(); } void update_smoke_puffs() { int i = 0; /* Draw each smoke puff */ while( i < MAX_SMOKE ) { if( smoke[i].active ) { /* Expand the size of this smoke puff */ smoke[i].sx += 1.0f; smoke[i].sy += 1.0f; /* Rotate the smoke puff */ smoke[i].rot += 0.5f; /* Move smoke puff according to it's movement type */ if( smoke[i].type == SMOKEANIM_RISE ) { smoke[i].y -= 1.0f; } /* Check for maximum size */ if( smoke[i].sx >= 64.0f || smoke[i].sx >= 64.0f ) smoke[i].active = No; } i++; } } void draw_player_dragon() { /* Update the dragon's position */ // dragon.x += dragon.vx; // dragon.y += dragon.vy; static int flash = No; /* Draw the dragon sprite */ if( !flash ) { enable_2d(); transparent_blend( TRUE ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); translate( dragon.x, dragon.y ); draw_quad( get_texture( "dragon1" ), 0.0f, 0.0f, dragon.width, dragon.height ); transparent_blend( FALSE ); disable_2d(); } if( dragon.inv_t > 0 ) { dragon.inv_t--; flash = !flash; } else flash = No; } void draw_hud() { char string[64]; /* Enable 2D drawing with colour key enabled */ enable_2d(); transparent_blend( TRUE ); /* draw the health bar */ push_pos(); set_colour( 0.0f, 1.0f, 0.0f, 0.5f ); translate( 160.0f, 480.0f - 50.0f ); draw_quad( 0, 10.0f, 0, (float) dragon.energy, 8.0f ); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_line( 10.0f, 0.0f, (float) dragon.max_energy + 10.0f, 0.0f ); draw_line( 10.0f, 8.0f, (float) dragon.max_energy + 10.0f, 8.0f ); draw_line( 10.0f, 0.0f, 10.0f, 8.0f ); draw_line( (float) dragon.max_energy + 10.0f, 0.0f, (float) dragon.max_energy + 10.0f, 8.0f ); pop_pos(); /* display player's score and attack mode*/ push_pos(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); // translate( 640.0f/4.0f, 0.0f ); sprintf( string, "SCORE: %d", score ); draw_text( &fonts[FONT_STEEL], 640.0f/4.0f, 0, string ); draw_text( &fonts[FONT_BOLD], 330.0f, 0, (dragon.attack_mode == 0) ? "FULL AUTO" : "HOMING" ); pop_pos(); /* draw the powerups obtained (crystals) */ push_pos(); transparent_blend( TRUE ); translate( 165.0f, 480.0f - 38.0f ); /* Special attack */ if( dragon.powers[0] ) set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); else set_colour( 1.0f, 1.0f, 1.0f, .5f ); draw_quad( get_texture( "diamond1" ), 0, 0, 32.0f, 32.0f ); /* Overdrive */ if( dragon.powers[1] ) set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); else set_colour( 1.0f, 1.0f, 1.0f, .5f ); draw_quad( get_texture( "diamond2" ), 32.0f, 0, 32.0f, 32.0f ); /* Barrier */ if( dragon.powers[2] ) set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); else set_colour( 1.0f, 1.0f, 1.0f, .5f ); draw_quad( get_texture( "diamond3" ), 64.0f, 0, 32.0f, 32.0f ); pop_pos(); /* Disable 2D drawing and colour key */ transparent_blend( FALSE ); disable_2d(); } int draw_shogun3d_logo() { static float a = 255.0f; static int timer = 0; int finished = 0; /* Enable 2D rendering */ enable_2d(); /* Draw the Shogun3D logo */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_quad( get_texture( "shogun3d" ), 0.0f, 0.0f, 640.0f, 480.0f ); /* Draw the transparent quad over it to simulate fading in/out */ transparent_blend( TRUE ); set_colour( 0.0f, 0.0f, 0.0f, a/255.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f, 480.0f ); transparent_blend( FALSE ); /* Return to default colour (opaque white) */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Disable 2D rendering */ disable_2d(); /* Update timing and alpha values */ if( timer != 200 && a > 0.0f ) a -= 5.0f; if( a <= 0.0f && timer != 200 ) timer++; if( timer >= 200 /*&& a < 255.0f*/ ) a += 5.0f; if( timer >= 200 && a >= 255.0f ) { timer = 0; finished = 1; } /* Return result */ return finished; } int draw_purpose_disclaimer() { static float a = 255.0f; static int timer = 0; int finished = 0; /* Enable 2D rendering */ enable_2d(); /* Draw black backdrop quad */ set_colour( 0.0f, 0.0f, 0.0f, 1.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f, 480.0f ); /* Set base colour to white */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Tell the whole world who I did this for and why =) */ draw_text( &fonts[2], 75, 200, "This game is a labour of love!" ); draw_text( &fonts[2], 75, 232, " For Lady Mia..." ); /* Draw some hearts to show her some love! */ draw_quad( get_texture( "heart1" ), 50.0f, 200.0f, 32.0f, 32.0f ); draw_quad( get_texture( "heart2" ), 560.0f, 200.0f, 32.0f, 32.0f ); // draw_quad( get_texture( "heart" ), 180.0f, 232, 32.0f, 32.0f ); // draw_quad( get_texture( "heart" ), 450.0f, 232, 32.0f, 32.0f ); // draw_text( &fonts[3], 0, 10, "%" ); // draw_text( &fonts[4], 0, 10, "LOVE YOU FOREVER!" ); // set_colour( 1.0f, 1.0f, 0.0f, 1.0f ); // draw_text( &fonts[5], 0, 10, "This is text..." ); /* Draw the transparent quad over it to simulate fading in/out */ #if 1 transparent_blend( TRUE ); set_colour( 0.0f, 0.0f, 0.0f, a/255.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f, 480.0f ); transparent_blend( FALSE ); #endif /* Return to default colour (opaque white) */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); /* Disable 2D rendering */ disable_2d(); #if 1 /* Update timing and alpha values */ if( timer != 200 && a > 0.0f ) a -= 5.0f; if( a <= 0.0f && timer != 200 ) timer++; if( timer >= 200 /*&& a < 255.0f*/ ) a += 5.0f; if( timer >= 200 && a >= 255.0f ) { timer = 0; finished = 1; } #endif /* Return result */ return finished; } int draw_hero_intro() { const float end_x = 320.0f-125.0f; const float end_y = 240.0f-160.0f; static struct Vector2 blur_v[5]; static float a = 255.0f; static float blur_a[5] = { (255.0f), (255.0f/2.0f), (255.0f/3.0f), (255.0f/4.0f), (255.0f/5.0f) }; static int timer = 0; int finished = 0; static int first = 1; int i = 4; if( first ) { play_sound_effect_static( &sfx[6], 0 ); blur_v[0].x = end_x-(20.0f*20.0f); blur_v[0].y = end_y-(20.0f*20.0f); blur_v[1].x = end_x-(20.0f*22.0f), blur_v[1].y = end_y-(20.0f*22.0f); blur_v[2].x = end_x-(20.0f*24.0f), blur_v[2].y = end_y-(20.0f*24.0f); blur_v[3].x = end_x-(20.0f*26.0f), blur_v[3].y = end_y-(20.0f*26.0f); blur_v[4].x = end_x-(20.0f*28.0f), blur_v[4].y = end_y-(20.0f*28.0f); first = 0; } enable_2d(); /* Draw white backdrop quad */ set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f, 480.0f ); transparent_blend( TRUE ); /* Draw the image of the hero */ while( i != -1 ) { set_colour( 1.0f, 1.0f, 1.0f, (blur_a[i]/255.0f) ); draw_quad( get_texture( "hero" ), blur_v[i].x, blur_v[i].y, 250.0f, 320.0f ); if( blur_v[i].x < end_x ) blur_v[i].x += 10.0f; if( blur_v[i].y < end_y ) blur_v[i].y += 10.0f; i--; } /* Draw transparent foreground */ set_colour( 1.0f, 1.0f, 1.0f, a/255.0f ); draw_quad( 0, 0.0f, 0.0f, 640.0f, 480.0f ); transparent_blend( FALSE ); disable_2d(); /* */ if( a > 0.0f && timer == 0 ) a -= 5.0f; if( a == 0.0f && timer != 50 ) timer++; if( timer >= 50 ) a += 5.0f; if( a >= 255.0f && timer >= 50 ) { timer = 0; finished = 1; wavestream_play(&bgm); } return finished; } int do_fade( int fade_in ) { int finished = No; extern float current_colour[4]; /* Enable 2D rendering */ enable_2d(); if( fade_in ) { static float a = 255.0f; transparent_blend(Yes); set_colour( current_colour[0], current_colour[0], current_colour[0], a/255.0f ); draw_quad( 0, 160.0f, 0.0f, 320.0f, 480.0f ); transparent_blend(No); a -= 5.0f; if( a < 0.0f ) { a = 255.0f; finished = Yes; } } else { static float a = 0.0f; transparent_blend(Yes); set_colour( current_colour[0], current_colour[0], current_colour[0], a/255.0f ); draw_quad( 0, 160.0f, 0.0f, 320.0f, 480.0f ); transparent_blend(No); a += 5.0f; if( a > 255.0f ) { a = 0.0f; finished = Yes; } } /* Disable 2D rendering */ disable_2d(); return finished; } int fade_out_bgm() { int finished = No; static float volume = 1.0f; /* Continually decrease bgm volume until it reaches 0 */ /* Are we done yet? */ if( volume < 0.0f ) { /* Mark as finished */ finished = Yes; volume = 1.0f; /* Close the current track */ wavestream_close(&bgm); /* Are we loading a new track? */ if( strcmp( "", bgm_next_track ) ) { /* If so, open it and start playing */ wavestream_open(bgm_next_track, &bgm); wavestream_play(&bgm); } else { /* If not, don't do a darn thing */ } } else { wavestream_set_volume( &bgm, volume ); volume -= 0.01f; } return finished; } #if 0 /* Test code for target tracking */ void clamp_float( float* d, float min, float max ) { if( *d < min ) *d = min; if( *d > max ) *d = max; } void test_track() { static struct Vector2 p = { 0.0f, 320.0f }; static struct Vector2 v = { 0.0f, 0.0f }; float angle_to_player = angle( p.x, p.y, dragon.x, dragon.y ); float speed = 10.0f; v.x += cos(angle_to_player); v.y += sin(angle_to_player); clamp_float( &v.x, -speed, speed ); clamp_float( &v.y, -speed, speed ); p.x += v.x; p.y += v.y; enable_2d(); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); transparent_blend(TRUE); draw_quad( get_texture("hblast2"), p.x+(106.0f/2.0f), p.y+(149.0f/2.0f), 32.0f, 32.0f ); transparent_blend(FALSE); disable_2d(); } #endif void adjust_framerate() { if( game_state == GAME_STATE_INGAME ) { if( get_spf() > 150 ) fps_limit = (int)(30.0f*((float)(game_speed/100.0f))); else fps_limit = (int)(60.0f*((float)(game_speed/100.0f)));; } else { fps_limit = 60; } } void render() { /* The background is displayed regardless of game state */ draw_background(); wavestream_update(&bgm); /* Input devices are updated regardless of game state */ update_input_devices(); switch( game_state ) { case GAME_STATE_INTRO1: if( draw_shogun3d_logo() ) game_state = GAME_STATE_INTRO2; break; case GAME_STATE_INTRO2: if( draw_purpose_disclaimer() ) game_state = GAME_STATE_INTRO3; break; case GAME_STATE_INTRO3: if( draw_hero_intro() ) game_state = GAME_STATE_MENU; break; case GAME_STATE_MENU: draw_menus(); break; case GAME_STATE_INGAME: update_crosshair_position(); draw_environment(); draw_boss(); update_boss(); draw_enemies(); render_explosions(); draw_player_dragon(); draw_active_locks(); acquire_new_locks(); update_active_locks(); update_explosions(); draw_homing_blasts(); update_homing_blasts(); draw_fullautoblasts(); update_fullautoblasts(); check_homing_blasts_for_collisions(); check_fullauto_blasts_for_collisions(); draw_enemy_shoots(); update_enemy_shoots(); draw_smoke_puffs(); update_smoke_puffs(); draw_powerups(); update_powerups(); draw_gold_peices(); update_gold_peices(); update_guard_if_activated(); /* Update guard first */ draw_guard_if_activated(); draw_hud(); get_attack_mode_changes(); handle_mouse_input(); move_dragon(); check_for_special_input(); update_overdrive_if_active(); draw_crosshair(); /* Should be last */ draw_boundaries(); #if 0 test_track(); #endif if( ingame_fade == 0 ) /* Fade out */ { if( do_fade(No) ) ingame_fade = -1; } if( ingame_fade == 1 ) /* Fade in */ { if( do_fade(Yes) ) ingame_fade = -1; } adjust_framerate(); break; case GAME_STATE_EXITING: uninit_game(); PostQuitMessage(0); break; } /* If we are transitioning to a new level... */ if( bgm_fade ) { if( fade_out_bgm() ) bgm_fade = No; } /* Copy the screen to a texture */ /*copy_scene_to_texture( &render_target, 640, 480 ); transparent_blend(No); set_colour( 1.0f, 1.0f, 1.0f, 1.0f ); enable_2d(); draw_quad( render_target, 0, 0, 320.0f, 240.0f ); disable_2d();*/ /* Disable vsync when ingame */ /*enable_vsync( (game_state != GAME_STATE_INGAME) );*/ }