#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) );*/
}