#include #include #include #include #include #include #include #include #include #include "gl.h" #include "afont.h" #include "sounds.h" /* Configuration */ static const char def_highscore_file[256] = SCORELIST; static const char highscore_file_env[256] = "ASTEROIDS_HIGHSCORE"; static char wizpw[] = "=1.512.TW7zx9.xnWHDBWfGyhP8x.zn4SV82F7l6Cwr4cOy/iX1"; /* should be const, but crypt() prototype is broken */ #define ALERT_FLASH (TICKS_PER_SEC/2) /* ticks */ #define ATTRACT_ACTION_TIME (TICKS_PER_SEC*3) /* ticks */ #define ATTRACT_SAUCER_CHANCE (20) /* one-out-of-N */ #define ATTRACT_SAUCER_XCHANCE (20) /* one-out-of-N */ #define AUTOREPEAT_DELAY ((TICKS_PER_SEC*3)/4) /* ticks */ #define AUTOREPEAT_INTERVAL (TICKS_PER_SEC/10) /* ticks */ #define BONUS_COUNTDOWN 60 /* points per second */ #define BONUS_INIT 3600 /* per large asteroid */ #define DEBRIS_DECAY (TICKS_PER_SEC) /* ticks */ #define DEBRIS_VELXY ((120*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define END_BONUS_WAIT_TIME ((TICKS_PER_SEC*3)/2) /* ticks */ #define EXIT_TIME (TICKS_PER_SEC*15) /* ticks */ #define EXPLODE_SEG_VELA (120 DEGREES/TICKS_PER_SEC) /* radians per tick */ #define EXPLODE_SEG_VELXY ((30*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define EXPLOSION_WAIT_TIME (TICKS_PER_SEC*2) /* ticks */ #define FIRSTFREESHIP 1024 /* points */ #define FREESHIPADD 131072 /* points */ #define FREESHIPLINEAR 100000 /* points, threshold */ #define FREESHIPMUL 2 /* multiplicative factor */ #define GAMEOVER_BELOW ((int)(200*PIXELSCALE)) /* pixels */ #define GAMEOVER_GROWTIME (TICKS_PER_SEC*3) /* ticks */ #define GAMEOVER_RISETIME (TICKS_PER_SEC*2) /* ticks */ #define HELP_BORDER ((int)(50*PIXELSCALE)) /* pixels */ #define HELP_TIMEOUT (TICKS_PER_SEC*60*5) /* ticks */ #define HYPER_TIME (TICKS_PER_SEC/5) /* ticks */ #define LEVELBEGIN_TIMEOUT (TICKS_PER_SEC*60*1) /* ticks */ #define MAKESAUCER_CHECK TICKS_PER_SEC /* ticks */ #define MIN_SAUCER_LEVEL 3 /* level number */ #define RECENTER_AVEL (1.8/TICKS_PER_SEC) /* radians per tick */ #define RECENTER_VEL ((180*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define RECOIL ((.001*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define RESUME_TIMEOUT (TICKS_PER_SEC*60*5) /* ticks */ #define ROCKPOOF_AFUZZ (30 DEGREES) /* angle */ #define ROCKPOOF_ATOXY (45*PIXELSCALE) /* pixels per radian */ #define ROCKPOOF_VELA (120 DEGREES/TICKS_PER_SEC) /* radians per tick */ #define ROCKPOOF_VELXY ((30*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define ROCKSIZE_BIG ((int)(40*PIXELSCALE)) /* pixels */ #define ROCKSIZE_MEDIUM ((int)(20*PIXELSCALE)) /* pixels */ #define ROCKSIZE_SMALL ((int)(10*PIXELSCALE)) /* pixels */ #define SAUCERSIZE ((int)(20*PIXELSCALE)) /* pixels */ #define SAUCER_DEAD_TIMEOUT (TICKS_PER_SEC*3) /* ticks */ #define SAUCER_DEFLECT_AINC (30 DEGREES) /* radians */ #define SAUCER_DUMB_AVEL (240 DEGREES / TICKS_PER_SEC) /* radians per tick */ #define SAUCER_DUMB_SHOOT_INTVLVAR ((TICKS_PER_SEC*10)/6) /* ticks */ #define SAUCER_DUMB_SHOOT_MININTVL ((TICKS_PER_SEC*5)/4) /* ticks */ #define SAUCER_FORESEE TICKS_PER_SEC /* ticks */ #define SAUCER_LEVEL0_TIME (TICKS_PER_SEC*85) /* ticks */ #define SAUCER_MARGIN ((int)(20*PIXELSCALE)) /* pixels */ #define SAUCER_MAXCHKROCKS 5 /* count */ #define SAUCER_MOVETIMER_MAX (TICKS_PER_SEC*3) /* ticks */ #define SAUCER_MOVETIMER_MIN (TICKS_PER_SEC*2) /* ticks */ #define SAUCER_REVERSE_CHANCE 10 /* one-out-of-N */ #define SAUCER_SHOOT_DELAY (TICKS_PER_SEC*5) /* ticks */ #define SAUCER_SHOTINTVL (TICKS_PER_SEC/10) /* ticks */ #define SAUCER_SMART_AVEL (120 DEGREES / TICKS_PER_SEC) /* radians per tick */ #define SAUCER_SMART_LEVEL0_TIME (TICKS_PER_SEC*40) /* ticks */ #define SAUCER_SMART_SHOOT_INTVLVAR (TICKS_PER_SEC) /* ticks */ #define SAUCER_SMART_SHOOT_MININTVL (TICKS_PER_SEC/2) /* ticks */ #define SAUCER_SMART_TIME0_LEVEL 20 /* level number */ #define SAUCER_SURETIME_DUMB (TICKS_PER_SEC*500) /* ticks */ #define SAUCER_SURETIME_SMART (TICKS_PER_SEC*300) /* ticks */ #define SAUCER_TIME0_LEVEL 15 /* level number */ #define SAUCER_VEL ((45*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define SAUCER_Y_FACTOR (.9) /* factor */ #define SCORELINE_MARGIN (60*PIXELSCALE) /* pixels */ #define SCORELINE_SPEED ((120*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define SCORELIST_EXPIRE (60*60*24) /* seconds */ #define SCORELIST_EXP_BASE 1.072 /* base for exponentiation */ #define SCORELIST_NEWTIMEOUT (TICKS_PER_SEC*60*2) /* ticks */ #define SCORELIST_SIZE 100 /* count */ #define SCORELIST_SIMUL 10 /* count */ #define SCORELIST_TOPN 3 /* count */ #define SCOREOBJ_EXPIRE (TICKS_PER_SEC*2) /* ticks */ #define SCOREWAIT_BUSYTIME (TICKS_PER_SEC/2) /* ticks */ #define SCORE_ROCK_BIG 50 /* points */ #define SCORE_ROCK_MEDIUM 100 /* points */ #define SCORE_ROCK_SMALL 200 /* points */ #define SCORE_SAUCER_DUMB 500 /* points */ #define SCORE_SAUCER_SMART 1000 /* points */ #define SETUP_ROCKFLASH (TICKS_PER_SEC/5) /* ticks */ #define SETUP_ROCKSPIN (20.0/TICKS_PER_SEC) /* degrees per tick */ #define SHIPSIZE ((int)(20*PIXELSCALE)) /* pixels */ #define SHOT_MAXAGE (TICKS_PER_SEC*5) /* ticks */ #define SHOT_MAXOURS 10 /* count */ #define SHOT_SPEED ((180*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define SHOWSCORE_CHECK (TICKS_PER_SEC*60*5) /* ticks */ #define SHOWSCORE_INITIALTIME (TICKS_PER_SEC*60) /* ticks */ #define SHOWSCORE_EACHTIME (TICKS_PER_SEC) /* ticks */ #define SHOWSCORE_TOPTIME (TICKS_PER_SEC*15) /* ticks */ #define SHOWSCORE_SLOTTIME (TICKS_PER_SEC*15) /* ticks */ #define SHOWSTOW_TOPY (YMAXSCREEN*.45) /* pixels */ #define SMALLSHIP_SCALE (.5) /* ratio */ #define SPINMAX_LEVEL 4 /* level number */ #define SPIN_MAX (200.0/TICKS_PER_SEC) /* degrees per tick */ #define STARTGAME_Y ((int)(140*PIXELSCALE)) /* pixels */ #define STOW_APART_N 5 /* count */ #define STOW_FROM_VEL ((180*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define STOW_TO_VEL ((1200*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define STOW_VEL ((60*PIXELSCALE)/TICKS_PER_SEC) /* pixels per tick */ #define STOW_X ((int)(90*PIXELSCALE)) /* pixels */ #define STOW_X_INC ((int)(30*PIXELSCALE)) /* pixels */ #define STOW_Y ((int)(30*PIXELSCALE)) /* pixels */ #define THRUST_ACCEL ((1000*PIXELSCALE)/(TICKS_PER_SEC*TICKS_PER_SEC)) /* pixels per tick per tick */ #define TICKHISTSIZE_L 60 /* count */ #define TICKHISTSIZE_S 6 /* count */ #define TURN_RATE (5.0/TICKS_PER_SEC) /* radians per tick */ #define TXTSIZ_ALERT (60*PIXELSCALE) /* pixels */ #define TXTSIZ_BEGINLEVEL (50*PIXELSCALE) /* pixels */ #define TXTSIZ_BONUSCOUNT (50*PIXELSCALE) /* pixels */ #define TXTSIZ_CLOCK (25*PIXELSCALE) /* pixels */ #define TXTSIZ_CONTINUE (25*PIXELSCALE) /* pixels */ #define TXTSIZ_GAMEOVER (180*PIXELSCALE) /* pixels */ #define TXTSIZ_HELPLINES (25*PIXELSCALE) /* pixels */ #define TXTSIZ_ROCKINFO (20*PIXELSCALE) /* pixels */ #define TXTSIZ_RSINVERT (20*PIXELSCALE) /* pixels */ #define TXTSIZ_SCOREBUSY (25*PIXELSCALE) /* pixels */ #define TXTSIZ_SCORELINE (30*PIXELSCALE) /* pixels */ #define TXTSIZ_SCORELIST (35*PIXELSCALE) /* pixels */ #define TXTSIZ_SCOREMSG (25*PIXELSCALE) /* pixels */ #define TXTSIZ_SCOREOBJ (20*PIXELSCALE) /* pixels */ #define TXTSIZ_SETUPINFO (20*PIXELSCALE) /* pixels */ #define TXTSIZ_SETUPMENU (30*PIXELSCALE) /* pixels */ #define TXTSIZ_SHOWSTOW_X (15*PIXELSCALE) /* pixels */ #define TXTSIZ_SHOWSTOW_Y (20*PIXELSCALE) /* pixels */ #define TXTSIZ_STOWCOUNT (20*PIXELSCALE) /* pixels */ #define TURNLEFT LEFTCTRLKEY #define TURNRIGHT LEFTALTKEY #define ROCKET RIGHTALTKEY #define GUN RIGHTCTRLKEY #define HYPERSPACE SPACEKEY /* Utility #defines */ #define forward extern #define NEW(t) ((t *) malloc(sizeof(t))) #define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0])) #define SQRT2 M_SQRT2 #define SQRT3 1.73205 #define DEGREES *(M_PI/180) #define GAMEOVER_TOTALTIME (GAMEOVER_GROWTIME+GAMEOVER_RISETIME) #define RANGEMAP(var,in_min,in_max,out_min,out_max) ((out_min)+((((var)-(in_min))*((out_max)-(out_min)))/((in_max)-(in_min)))) #define SCORELIST_SIMOFF ((SCORELIST_SIMUL-SCORELIST_TOPN)/2) #define SCORELIST_MINSCROLL (SCORELIST_TOPN+SCORELIST_SIMOFF) #define SCORELIST_MAXSCROLL (SCORELIST_SIZE-SCORELIST_SIMUL+SCORELIST_TOPN+SCORELIST_SIMOFF) /* Feh. No reliable way to do this automatically. */ #if defined(ANSI_PASTE) || (defined(__STDC__) && !defined(OLD_PASTE)) #define CONCAT(a,b) a##b #else #define CONCAT(a,b) a/**/b #endif /* Assumption checks */ #if SCORELIST_SIMUL > SCORELIST_SIZE #error "SCORELIST_SIMUL must be no greater than SCORELIST_SIZE" #endif #if SCORELIST_TOPN >= SCORELIST_SIMUL #error "SCORELIST_TOPN must be less than SCORELIST_SIMUL" #endif #if SCORELIST_TOPN < 1 #error "SCORELIST_TOPN must be at least 1" #endif #if SCORELIST_SIMUL < 4 #error "SCORELIST_SIMUL must be at least 4" #endif #if ! ((SCORELIST_SIMUL-SCORELIST_TOPN) & 1) #warning "SCORELIST_SIMUL-SCORELIST_TOPN should be odd" #endif /* Data structure definitions */ typedef struct rockshape ROCKSHAPE; typedef struct obj OBJ; typedef struct scoreent SCOREENT; typedef struct ship SHIP; typedef struct shot SHOT; typedef struct rock ROCK; typedef struct saucer SAUCER; typedef struct seg SEG; typedef struct menuent MENUENT; typedef struct setupmenu SETUPMENU; typedef struct set_var SET_VAR; typedef struct debris DEBRIS; typedef struct jet JET; typedef struct scoreobj SCOREOBJ; struct rockshape { float (*points)[2]; int npoints; } ; struct obj { float x; float y; float a; int movetype; #define OMT_VEL 1 #define OMT_DST 2 #define OMT_C_D 3 int movesleft; struct { float x; float y; float a; } v; struct { float x; float y; float a; float vxy; float va; } d; } ; struct scoreent { int score; int level; time_t when; unsigned char name[3]; } ; struct scoreobj { SCOREOBJ *link; char txt[32]; int ageleft; OBJ obj; } ; struct debris { DEBRIS *link; int ageleft; OBJ obj; } ; struct jet { unsigned int bit; int rampmax; int ramp; } ; struct ship { int dispmethod; #define SD_NONE 1 #define SD_PLAYING 2 #define SD_RECENTER 3 #define SD_FROMSTOW 4 #define SD_CENTER 5 #define SD_TOSTOW 6 #define SD_STOWED 7 #define SD_STOWMOVE 8 #define SD_HYPER 9 OBJ obj; } ; struct shot { SHOT *link; int ours; int age; OBJ obj; } ; struct rock { ROCK *link; int sizex; #define ROCK_BIG 1 #define ROCK_MEDIUM 2 #define ROCK_SMALL 3 int psize; int shape; int shapeinv; OBJ obj; } ; struct saucer { int age; int smart; int size; int movetimer; int shoottimer; int canshoot; int value; float movea; OBJ obj; } ; struct seg { float init_x1; float init_y1; float init_x2; float init_y2; float length; OBJ obj; } ; struct menuent { int key; #define KNMASK 0x0ff #define WIZ 0x100 /* | in for wizard-only commands */ #define NONWIZ 0x200 /* | in for non-wizard-only commands */ const char *text; void (*fxn)(long int, void *); long int liarg; void *varg; } ; struct setupmenu { MENUENT *entries; int nentries; void (*enter)(void); void (*leave)(void); } ; struct set_var { const char *name; char type; #define SVT__INT 1 #define SVT__FLOAT 2 int *p_int; float *p_float; } ; #define SVT_INT(v) SVT__INT,&v,0 #define SVT_FLOAT(v) SVT__FLOAT,0,&v /* Command-line stuff */ extern const char *__progname; static int debugging = 0; static int noscore_all = 0; static int nosound = 0; static const char *audiodev = 0; /* Game stuff */ static int level; static int nextfreeship; static int jetbits; static JET jets[] = { #define JET_LEFT 0 { 0x00000001, 1 }, #define JET_RIGHT 1 { 0x00000002, 1 }, #define JET_FWD 2 { 0x00000004, 1 } }; #define JET__N 3 static SHIP **ships; static int nships; static int maxships; static SHIP *playship; static SHOT *shots; static int ourshots; static int othershots; static ROCK *rocks; static int nrocks; static DEBRIS *debris; static SCOREOBJ *scoreobj; static int score; static int bonusleft; static int bonuscountdown; static int state; #define STATE_WAITBEGIN 1 #define STATE_PLAYING 2 #define STATE_ENDLEVEL 3 #define STATE_EXPLOSION 4 #define STATE_RESUME 5 #define STATE_GAMEOVER 6 #define STATE_SCOREWAIT 7 #define STATE_SHOWSCORE 8 #define STATE_NEWSCORE 9 #define STATE_EXITING 10 #define STATE_HELP 11 #define STATEPAIR(a,b) (((a)<<4)|(b)) static int stateage; static int setupage; static int hypertime; static SAUCER saucer; static int saucerstate; #define SAUCER_NONE 1 #define SAUCER_HAVE 2 #define SAUCER_DEAD 3 #define SAUCER_DONT 4 static int saucertimer; static int saucerdeadtime; static int saucercollforce; static int countspeed; static int levelage; static int recoil; /* Display stuff */ static int dodraw; static int wantdraw; static char noflames_char; #define NOFLAMES (&noflames_char) static char flames_char; #define FLAMES (&flames_char) static float scoreline_y; static float scoreline_desy; static int nstowed; static int nstowmove; static int stowstate; #define STOW_APART 1 #define STOW_TOGETHER 2 #define STOW_MERGING 3 #define STOW_SPLITTING 4 #if 0 static int stowtime; #endif static SEG *deadsaucersegs; static int deadsaucernsegs; static Matrix noxfmat; /* Timekeeping stuff */ static int realtime; static int tickhist_l[TICKHISTSIZE_L]; static int tickhist_s[TICKHISTSIZE_S]; static int tickhhead_l; static int tickhhead_s; static int tickhsum_l; static int tickhsum_s; static int tickspending[2]; static int tickrate[2]; static int skipticks; /* High-score list stuff */ static int highscore_fd; static SCOREENT scorelist[SCORELIST_SIZE+1]; static unsigned char highscore_scramble[sizeof(scorelist)]; static char scorenamevec[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ\177"; static int scorenameveclen = 28; static int scorelist_lw; static int scorelist_sw; static void (*scorelist_setfn)(const char *); static const char *scorelist_prompt; static int scoreslot; static int scoreletter; static int letterrep; static int reptime; static int checkhsfile; static int hschange; static int hsstate; #define HSS_SLOT 1 /* displaying around scoreslot */ #define HSS_SCROLL 2 /* displaying around scorescroll */ #define HSS_STEP 3 /* displaying just scorescroll */ #define HSS_TOP 4 /* displaying top scores */ #define HSS_TOPSLOT 5 /* HSS_SLOT except start from top */ static int scorescroll; static int hssinc; static int hssdec; static int hssrep; /* Help-mode stuff */ static int helpscreen; static int seenlasthelp; extern const char *helpscreens[]; extern int nhelpscreens; extern int helpmaxnl; extern int helpmaxmw; /* Setup-mode and other debugging stuff */ static int insetup; static unsigned int suppress_display; #define SUPPDIS_SETUP 0x00000001 #define SUPPDIS_ROCKSHAPE 0x00000002 #define SUPPDIS_SCORELIST 0x00000004 static unsigned int invulnerable; #define INVUL_SETUP 0x00000001 #define INVUL_RESUME 0x00000002 #define INVUL_HYPER 0x00000004 #define INVUL_VISIBLE (INVUL_SETUP|INVUL_RESUME) static int sinvulnerable; static int noscore_one; static int saucer_shoot_override; #define SSO_NONE 0 #define SSO_RANDOM 1 #define SSO_PLAYER 2 #define SSO_ROCK 3 #define SSO_LEAD 0x10 /* | in */ static int setup_link_alerts; static SETUPMENU setup_main_menu; static SETUPMENU *curmenu = &setup_main_menu; static SETUPMENU *lastcurmenu; static int iswiz = 0; static char setup_str[64]; static int setup_str_fill; static void (*setup_str_op)(int); #define SSE_DONE 1 #define SSE_DRAW 2 #define SSE_ABORT 3 static int setup_setvar; static int angle_to_debug; static int showstow; static int rockshape_overlay = 0; static int rockshape_overlay_invert = 0; static int do_rockshape_overlay = 0; static int scorelist_slot = 0; static int do_scorelist_overlay = 0; static int scorelist_overlay_base; static int flashing_rock_n = -1; static ROCK *flashing_rock = 0; static int do_fps = 1; static int do_rockscore = 0; /* Display definitions */ #define DRAWSEGARRAY(arr) drawsegs(&arr[0],ARRAYSIZE(arr)) #define DRAWSEGARRAY_OBJ(arr) drawsegobjs(&arr[0],ARRAYSIZE(arr)) static SEG shipsegs[] = { { 1, 0, 0, 1 }, { 1, 0, -1/SQRT2, 1/SQRT2 }, { 1, 0, -1, 0 }, { 1, 0, -1/SQRT2, -1/SQRT2 }, { 1, 0, 0, -1 } }; #define shipsegs_scale SHIPSIZE static SEG flame_t[] = { { -1/SQRT2, 1/SQRT2, -SQRT2, .5/SQRT2 }, { -SQRT2, .5/SQRT2, -1, 0 }, { -1/SQRT2, -1/SQRT2, -SQRT2, -.5/SQRT2 }, { -SQRT2, -.5/SQRT2, -1, 0 } }; #define flame_t_scale SHIPSIZE static SEG flame_l[] = { { 0.00, 1.0, 0.25, 1.1 }, { 0.25, 1.1, 0.50, 1.0 }, { 0.00, 1.0, 0.25, 0.9 }, { 0.25, 0.9, 0.50, 1.0 }, { 0.00, -1.0, -0.25, -1.1 }, { -0.25, -1.1, -0.50, -1.0 }, { 0.00, -1.0, -0.25, -0.9 }, { -0.25, -0.9, -0.50, -1.0 } }; #define flame_l_scale SHIPSIZE static SEG flame_r[] = { { 0.00, 1.0, -0.25, 1.1 }, { -0.25, 1.1, -0.50, 1.0 }, { 0.00, 1.0, -0.25, 0.9 }, { -0.25, 0.9, -0.50, 1.0 }, { 0.00, -1.0, 0.25, -1.1 }, { 0.25, -1.1, 0.50, -1.0 }, { 0.00, -1.0, 0.25, -0.9 }, { 0.25, -0.9, 0.50, -1.0 } }; #define flame_r_scale SHIPSIZE static SEG invsegs[] = { { 1, 0, 1/SQRT2, 1/SQRT2 }, { 0, 1, 1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, 1/SQRT2 }, { 0, 1, -1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, -1/SQRT2 }, { 0, -1, -1/SQRT2, -1/SQRT2 }, { 1, 0, 1/SQRT2, -1/SQRT2 }, { 0, -1, 1/SQRT2, -1/SQRT2 } }; #define invsegs_scale (SHIPSIZE * 1.25) static SEG sinvsegs[] = { { 1, 0, 1/SQRT2, 1/SQRT2 }, { 0, 1, 1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, 1/SQRT2 }, { 0, 1, -1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, -1/SQRT2 }, { 0, -1, -1/SQRT2, -1/SQRT2 }, { 1, 0, 1/SQRT2, -1/SQRT2 }, { 0, -1, 1/SQRT2, -1/SQRT2 } }; #define sinvsegs_scale (SAUCERSIZE * 1.25) static SEG saucersmartsegs[] = { { 1, 0, 1/SQRT2, 1/SQRT2 }, { 0, 1, -1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, -1/SQRT2 }, { 0, -1, 1/SQRT2, -1/SQRT2 }, { .2, 0, .2/SQRT2, .2/SQRT2 }, { 0, .2, -.2/SQRT2, .2/SQRT2 }, { -.2, 0, -.2/SQRT2, -.2/SQRT2 }, { 0, -.2, .2/SQRT2, -.2/SQRT2 }, { -1, 0, 1, 0 }, { 0, -1, 0, 1 }, { 1/SQRT2, 1/SQRT2, -1/SQRT2, -1/SQRT2 }, { -1/SQRT2, 1/SQRT2, 1/SQRT2, -1/SQRT2 } }; #define saucersmartsegs_scale SAUCERSIZE static SEG saucerdumbsegs[] = { { 1, 0, 1/SQRT2, 1/SQRT2 }, { 0, 1, -1/SQRT2, 1/SQRT2 }, { -1, 0, -1/SQRT2, -1/SQRT2 }, { 0, -1, 1/SQRT2, -1/SQRT2 }, { .4, 0, -.2, SQRT3*.2 }, { -.2, SQRT3*.2, -.2, -SQRT3*.2 }, { -.2, -SQRT3*.2, .4, 0 } }; #define saucerdumbsegs_scale SAUCERSIZE static SEG leadsegs[] = { { 1, 1, 1, -1 }, { 1, 1, -1, 1 }, { -1, -1, 1, -1 }, { -1, -1, -1, 1 } }; #define leadsegs_scale (SAUCERSIZE * 1.5) /* if you add segment arrays, add them to scalesegs() as well */ static float rockshape0[][2] = { { -1, 0 }, { -1/SQRT2, 1/SQRT2 }, { 0, 1 }, { 1/SQRT2, 1/SQRT2 }, { 1, 0 }, { 1/SQRT2, -1/SQRT2 }, { 0, -1 }, { -1/SQRT2, -1/SQRT2 } }; static float rockshape1[][2] = { { -1.0, 0.0 }, { -0.5, 0.3 }, { -0.6, 0.4 }, { -0.6, 0.7 }, { -0.1, 0.9 }, { 0.0, 1.0 }, { 0.1, 1.0 }, { 0.3, 0.5 }, { 0.5, 0.3 }, { 0.5, 0.7 }, { 0.9, 0.1 }, { 1.0, 0.0 }, { 0.7, 0.0 }, { 0.6, -0.8 }, { 0.0, -1.0 }, { -1/SQRT2, -1/SQRT2 } }; static float rockshape2[][2] = { { -1.0, 0.0 }, { -0.4, 0.1 }, { -0.9, 0.3 }, { -0.6, 0.7 }, { -0.65, 0.6 }, { 0.0, 1.0 }, { 0.1, 1.0 }, { 0.3, 0.5 }, { 0.5, 0.3 }, { 0.5, 0.7 }, { 0.9, 0.1 }, { 0.7, -0.1 }, { 0.6, -0.8 }, { 0.0, -1.0 }, { 0.0, -0.8 }, { -0.2, -0.85 }, { -0.7, -0.6, } }; static ROCKSHAPE rockshapes[] = { { rockshape0, ARRAYSIZE(rockshape0) }, { rockshape1, ARRAYSIZE(rockshape1) }, { rockshape2, ARRAYSIZE(rockshape2) } }; #define NROCKSHAPES ARRAYSIZE(rockshapes) /* Setup menu definitions */ static SET_VAR set_vars[] = { { "score", SVT_INT(score) }, #define SV_SCORE 0 { "bonus", SVT_INT(bonusleft) }, #define SV_BONUS 1 { "next-ship score", SVT_INT(nextfreeship) }, #define SV_FREESHIP 2 { "state age", SVT_INT(stateage) }, #define SV_STATEAGE 3 { "recoil strength", SVT_INT(recoil) }, #define SV_RECOIL 4 { "scoreline y", SVT_FLOAT(scoreline_y) }, #define SV_SCORELINE_Y 5 { "scoreline desired y", SVT_FLOAT(scoreline_desy) }, #define SV_SCORELINE_DESY 6 }; static SETUPMENU setup_main_menu; static SETUPMENU setup_ship_menu; static SETUPMENU setup_saucer_menu; static SETUPMENU setup_saucer_smarts_menu; static SETUPMENU setup_saucer_create_destroy_menu; static SETUPMENU setup_saucer_shot_menu; static SETUPMENU setup_game_menu; static SETUPMENU setup_wiz_menu; static SETUPMENU setup_game_kbd_menu; static SETUPMENU setup_game_set_numbers_menu; static SETUPMENU setup_game_set_toggles_menu; static SETUPMENU setup_rock_menu; static SETUPMENU setup_rock_shape_menu; static SETUPMENU setup_wiz_scorelist_menu; #define X (10) #define MDEC(f) static void f(long int, void *) #define TOMENU(menu) setup_any_newmenu, 0, &menu MDEC(setup_any_return); MDEC(setup_any_newmenu); MDEC(setup_any_set_num); MDEC(setup_any_toggle_boolean); static MENUENT setup_main_menu_ents[] = { { X, "MAIN SETUP" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Ship control", TOMENU(setup_ship_menu) }, { 2, "Saucer control", TOMENU(setup_saucer_menu) }, { 3, "Game control", TOMENU(setup_game_menu) }, { 4, "Rock control", TOMENU(setup_rock_menu) }, { 5, "Wizard options", TOMENU(setup_wiz_menu) } }; static SETUPMENU setup_main_menu = { &setup_main_menu_ents[0], ARRAYSIZE(setup_main_menu_ents) }; MDEC(setup_ship_incship); MDEC(setup_ship_invulnerable); MDEC(setup_ship_showstow); static MENUENT setup_ship_menu_ents[] = { { X, "SHIP CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Ship +", setup_ship_incship, 1 }, { 2, "Ship -", setup_ship_incship, -1 }, { 3, "Toggle vulnerability", setup_ship_invulnerable }, { 4, "Toggle stow debug display", setup_ship_showstow }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { 9, "Back to main menu", TOMENU(setup_main_menu) } }; static SETUPMENU setup_ship_menu = { &setup_ship_menu_ents[0], ARRAYSIZE(setup_ship_menu_ents) }; MDEC(setup_saucer_invulnerable); static MENUENT setup_saucer_menu_ents[] = { { X, "SAUCER CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Toggle vulnerability", setup_saucer_invulnerable }, { 2, "Create/destroy", TOMENU(setup_saucer_create_destroy_menu) }, { 3, "Shot control", TOMENU(setup_saucer_shot_menu) }, { 4, "Intelligence control", TOMENU(setup_saucer_smarts_menu) }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { 9, "Back to main menu", TOMENU(setup_main_menu) } }; static SETUPMENU setup_saucer_menu = { &setup_saucer_menu_ents[0], ARRAYSIZE(setup_saucer_menu_ents) }; MDEC(setup_saucer_forbid); MDEC(setup_saucer_create); MDEC(setup_saucer_explode); MDEC(setup_saucer_destroy); static MENUENT setup_saucer_create_destroy_menu_ents[] = { { X, "SAUCER CREATION/DESTRUCTION" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Forbid creation", setup_saucer_forbid }, { 2, "Create smart", setup_saucer_create, 1 }, { 3, "Create dumb", setup_saucer_create, 0 }, { 4, "Explode", setup_saucer_explode }, { 5, "Destroy", setup_saucer_destroy }, { X, "" }, { X, "" }, { X, "" }, { 9, "Back to saucer control menu", TOMENU(setup_saucer_menu) } }; static SETUPMENU setup_saucer_create_destroy_menu = { &setup_saucer_create_destroy_menu_ents[0], ARRAYSIZE(setup_saucer_create_destroy_menu_ents) }; MDEC(setup_saucer_collision); MDEC(setup_saucer_set_smarts); MDEC(setup_saucer_notice_collision); static MENUENT setup_saucer_smarts_menu_ents[] = { { X, "SAUCER INTELLIGENCE CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Toggle collision marks", setup_saucer_collision }, { 2, "Make saucer smart", setup_saucer_set_smarts, 1 }, { 3, "Make saucer dumb", setup_saucer_set_smarts, 0 }, { 4, "Force collision noticing", setup_saucer_notice_collision, 1 }, { 5, "Suppress collision noticing", setup_saucer_notice_collision, -1 }, { 6, "Normal collision noticing", setup_saucer_notice_collision, 0 }, { X, "" }, { X, "" }, { 9, "Back to saucer control menu", TOMENU(setup_saucer_menu) } }; static SETUPMENU setup_saucer_smarts_menu = { &setup_saucer_smarts_menu_ents[0], ARRAYSIZE(setup_saucer_smarts_menu_ents) }; /* argument values must match values in saucer_shoot() and drawsaucer() */ MDEC(setup_saucer_shot_choose); MDEC(setup_saucer_shot_shoot); static MENUENT setup_saucer_shot_menu_ents[] = { { X, "SAUCER SHOT CONTROL" }, { X, "" }, { 0, "Return to game", setup_any_return }, { 1, "Allow normal choice", setup_saucer_shot_choose, SSO_NONE }, { 2, "Force random angle", setup_saucer_shot_choose, SSO_RANDOM }, { 3, "Force at player", setup_saucer_shot_choose, SSO_PLAYER }, { 4, "Force at a rock", setup_saucer_shot_choose, SSO_ROCK }, { 5, "Force leading player", setup_saucer_shot_choose, SSO_PLAYER|SSO_LEAD }, { 6, "Force leading a rock", setup_saucer_shot_choose, SSO_ROCK|SSO_LEAD }, { 7, "Shoot", setup_saucer_shot_shoot, 0 }, { 8, "Debug shoot", setup_saucer_shot_shoot, 1 }, { 9, "Back to saucer control menu", TOMENU(setup_saucer_menu) } }; static SETUPMENU setup_saucer_shot_menu = { &setup_saucer_shot_menu_ents[0], ARRAYSIZE(setup_saucer_shot_menu_ents) }; MDEC(setup_game_inclevel); MDEC(setup_game_singlestep); static MENUENT setup_game_menu_ents[] = { { X, "GAME CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Level +", setup_game_inclevel, 1 }, { 2, "Level -", setup_game_inclevel, -1 }, { 3, "Single-step", setup_game_singlestep }, { 4, "Manipulate keyboard", TOMENU(setup_game_kbd_menu) }, { 5, "Set numbers", TOMENU(setup_game_set_numbers_menu) }, { 6, "Set toggles", TOMENU(setup_game_set_toggles_menu) }, { X, "" }, { X, "" }, { 9, "Back to main menu", TOMENU(setup_main_menu) } }; static SETUPMENU setup_game_menu = { &setup_game_menu_ents[0], ARRAYSIZE(setup_game_menu_ents) }; MDEC(setup_game_kbd_press); MDEC(setup_game_kbd_release); static MENUENT setup_game_kbd_menu_ents[] = { { X, "GAME KEYBOARD CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Press turn-left", setup_game_kbd_press, TURNLEFT }, { 2, "Press turn-right", setup_game_kbd_press, TURNRIGHT }, { 3, "Press thrust", setup_game_kbd_press, ROCKET }, { 4, "Release turn-left", setup_game_kbd_release, TURNLEFT }, { 5, "Release turn-right", setup_game_kbd_release, TURNRIGHT }, { 6, "Release thrust", setup_game_kbd_release, ROCKET }, { 7, "Hyperspace", setup_game_kbd_press, HYPERSPACE }, { 8, "Fire", setup_game_kbd_press, GUN }, { 9, "Back to game control menu", TOMENU(setup_game_menu) } }; static SETUPMENU setup_game_kbd_menu = { &setup_game_kbd_menu_ents[0], ARRAYSIZE(setup_game_kbd_menu_ents) }; static MENUENT setup_game_set_numbers_menu_ents[] = { { X, "SET NUMBERS" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Set score", setup_any_set_num, SV_SCORE }, { 2, "Set bonus", setup_any_set_num, SV_BONUS }, { 3, "Set next-ship score", setup_any_set_num, SV_FREESHIP }, { 4, "Set state age", setup_any_set_num, SV_STATEAGE }, { 5, "Set recoil", setup_any_set_num, SV_RECOIL }, { 6, "Set scoreline Y", setup_any_set_num, SV_SCORELINE_Y }, { 7, "Set scoreline desired Y", setup_any_set_num, SV_SCORELINE_DESY }, { X, "" }, { 9, "Back to game control menu", TOMENU(setup_game_menu) } }; static SETUPMENU setup_game_set_numbers_menu = { &setup_game_set_numbers_menu_ents[0], ARRAYSIZE(setup_game_set_numbers_menu_ents) }; static MENUENT setup_game_set_toggles_menu_ents[] = { { X, "SET TOGGLES" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Toggle fps display", setup_any_toggle_boolean, 0, &do_fps }, { 2, "Toggle rock score display", setup_any_toggle_boolean, 0, &do_rockscore }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { 9, "Back to game control menu", TOMENU(setup_game_menu) } }; static SETUPMENU setup_game_set_toggles_menu = { &setup_game_set_toggles_menu_ents[0], ARRAYSIZE(setup_game_set_toggles_menu_ents) }; MDEC(setup_rock_inc); MDEC(setup_rock_explode); MDEC(setup_rock_destroy); MDEC(setup_rock_create); static MENUENT setup_rock_menu_ents[] = { { X, "ROCK CONTROL" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Previous rock", setup_rock_inc, -1 }, { 2, "Next rock", setup_rock_inc, 1 }, { 3, "Explode rock", setup_rock_explode }, { 4, "Destroy rock", setup_rock_destroy }, { 5, "Create large rock", setup_rock_create, ROCK_BIG }, { 6, "Create medium rock", setup_rock_create, ROCK_MEDIUM }, { 7, "Create small rock", setup_rock_create, ROCK_SMALL }, { 8, "Shape options", TOMENU(setup_rock_shape_menu) }, { 9, "Back to main menu", TOMENU(setup_main_menu) } }; static void setup_rock_menu_enter(void); static void setup_rock_menu_leave(void); static SETUPMENU setup_rock_menu = { &setup_rock_menu_ents[0], ARRAYSIZE(setup_rock_menu_ents), setup_rock_menu_enter, setup_rock_menu_leave }; MDEC(setup_rock_shape_inc); MDEC(setup_rock_shape_invert); static MENUENT setup_rock_shape_menu_ents[] = { { X, "ROCK SHAPE INFO" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Previous shape", setup_rock_shape_inc, -1 }, { 2, "Next shape", setup_rock_shape_inc, 1 }, { 3, "Toggle inversion", setup_rock_shape_invert }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { 9, "Back to rock menu", TOMENU(setup_rock_menu) } }; static void setup_rock_shape_menu_enter(void); static void setup_rock_shape_menu_leave(void); static SETUPMENU setup_rock_shape_menu = { &setup_rock_shape_menu_ents[0], ARRAYSIZE(setup_rock_shape_menu_ents), setup_rock_shape_menu_enter, setup_rock_shape_menu_leave }; MDEC(setup_wiz_enterpw); MDEC(setup_wiz_rescore); MDEC(setup_wiz_exit_now); MDEC(setup_wiz_dont_exit); static MENUENT setup_wiz_menu_ents[] = { { X, "WIZARD OPTIONS" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Enter password", setup_wiz_enterpw }, { X, "" }, { X, "" }, { X, "" }, { X, "" }, { 5|WIZ, "High-score list control", TOMENU(setup_wiz_scorelist_menu) }, { X|NONWIZ, "" }, { 6|WIZ, "Stop exiting", setup_wiz_dont_exit }, { X|NONWIZ, "" }, { 7|WIZ, "Exit immediately", setup_wiz_exit_now }, { X|NONWIZ, "" }, { 8|WIZ, "Re-enable scoring", setup_wiz_rescore }, { X|NONWIZ, "" }, { 9, "Back to main menu", TOMENU(setup_main_menu) } }; static SETUPMENU setup_wiz_menu = { &setup_wiz_menu_ents[0], ARRAYSIZE(setup_wiz_menu_ents) }; MDEC(setup_wiz_scorelist_next); MDEC(setup_wiz_scorelist_prev); MDEC(setup_wiz_scorelist_jump); MDEC(setup_wiz_scorelist_setwho); MDEC(setup_wiz_scorelist_setlevel); MDEC(setup_wiz_scorelist_setscore); MDEC(setup_wiz_scorelist_setwhen); MDEC(setup_wiz_scorelist_clear); static MENUENT setup_wiz_scorelist_menu_ents[] = { { X, "HIGH-SCORE LIST OPTIONS" }, { X, "" }, { 0, "Return to the game", setup_any_return }, { 1, "Previous list entry", setup_wiz_scorelist_prev }, { 2, "Next list entry", setup_wiz_scorelist_next }, { 3, "Jump to list entry", setup_wiz_scorelist_jump }, { 4, "Set Who", setup_wiz_scorelist_setwho }, { 5, "Set Level", setup_wiz_scorelist_setlevel }, { 6, "Set Score", setup_wiz_scorelist_setscore }, { 7, "Set When", setup_wiz_scorelist_setwhen }, { 8, "Clear this entry", setup_wiz_scorelist_clear }, { 9, "Back to wizard menu", TOMENU(setup_wiz_menu) } }; static void setup_wiz_scorelist_menu_enter(void); static void setup_wiz_scorelist_menu_leave(void); static SETUPMENU setup_wiz_scorelist_menu = { &setup_wiz_scorelist_menu_ents[0], ARRAYSIZE(setup_wiz_scorelist_menu_ents), setup_wiz_scorelist_menu_enter, setup_wiz_scorelist_menu_leave }; #undef X #undef MDEC /* Compatability */ #ifdef DECLARE_RANDOM extern unsigned long int random(void); extern void srandom(int); #endif #ifdef DECLARE_CRYPT extern char *crypt(const char *, const char *); #endif #ifdef PROVIDE_PROGNAME const char *__progname; #endif /* Code */ static int rnd(unsigned int n) { return(random()%n); } static double frndo(void) { return((random()&0x00ffffff)/(double)0x01000000); } static double frndc(void) { return((random()&0x00ffffff)/(double)0x00ffffff); } static void parseargs(int ac, char **av) { int errs; int skip; errs = 0; skip = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs another argument\n",__progname,*av); errs ++; continue; } #define WANTARGS(n) do { if ((skip+=(n)) >= ac) goto needarg; } while (0) if (!strcmp(*av,"-debug")) { debugging = 1; } else if (!strcmp(*av,"-nosound")) { nosound = 1; } else if (!strcmp(*av,"-audio")) { WANTARGS(1); audiodev = av[1]; } else if (!strcmp(*av,"-idev")) { WANTARGS(1); if (setidev(av[1])) { int first; first = 1; listidev(({ static void foo(const char *n, const char *d) { if (first) fprintf(stderr,"Known -idev values:\n"); fprintf(stderr,"\t%-20s %s\n",n,d); first = 0; } &foo; })); errs ++; } } else if (!strcmp(*av,"-odev")) { WANTARGS(1); if (setodev(av[1])) { int first; first = 1; listodev(({ static void foo(const char *n, const char *d) { if (first) fprintf(stderr,"Known -odev values:\n"); fprintf(stderr,"\t%-20s %s\n",n,d); first = 0; } &foo; })); errs ++; } } else if (**av == '-') { fprintf(stderr,"%s: unrecognized flag %s\n",__progname,*av); errs ++; } else { fprintf(stderr,"%s: extra argument: %s\n",__progname,*av); errs ++; } } if (errs) exit(1); } static SHOT *newshot(void) { return(NEW(SHOT)); } static void linkshot(SHOT *s) { s->link = shots; shots = s; } static void freeshot(SHOT *s) { free(s); } static DEBRIS *newdebris(void) { return(NEW(DEBRIS)); } static void freedebris(DEBRIS *d) { free(d); } static void cleardebris(void) { while (debris) { DEBRIS *d; d = debris; debris = d->link; freedebris(d); } debris = 0; } static ROCK *newrock(void) { return(NEW(ROCK)); } static void linkrock(ROCK *r) { r->link = rocks; rocks = r; nrocks ++; } static void freerock(ROCK *r) { free(r); nrocks --; } static ROCK *randomrock(void) { register ROCK *r; register int nr; if (nrocks < 1) return(0); for (r=rocks,nr=rnd(nrocks);nr>0;nr--,r=r->link) ; if (r == 0) abort(); return(r); } static void newbigrock(void) { ROCK *r; float a; float v; float valimit; if ((state != STATE_WAITBEGIN) || (level >= SPINMAX_LEVEL)) { valimit = SPIN_MAX; } else { valimit = (SPIN_MAX * level) / (float) SPINMAX_LEVEL; } valimit = valimit DEGREES; r = newrock(); linkrock(r); r->sizex = ROCK_BIG; r->psize = ROCKSIZE_BIG; r->shape = rnd(NROCKSHAPES); r->shapeinv = random() & 1; switch (random() & 3) { case 0: r->obj.x = 0; r->obj.y = frndc() * YMAXSCREEN; break; case 1: r->obj.x = XMAXSCREEN; r->obj.y = frndc() * YMAXSCREEN; break; case 2: r->obj.x = frndc() * XMAXSCREEN; r->obj.y = 0; break; case 3: r->obj.x = frndc() * XMAXSCREEN; r->obj.y = YMAXSCREEN; break; } r->obj.a = frndo() * 360 DEGREES; a = frndo() * 360 DEGREES; v = (frndc() + 1) / 3; r->obj.movetype = OMT_VEL; r->obj.v.x = cos(a) * v; r->obj.v.y = sin(a) * v; r->obj.v.a = frndc() * valimit; if (random() & 1) r->obj.v.a = - r->obj.v.a; } static void initrandom(void) { time_t now; time(&now); srandom(now+getpid()); } static void scalesegarray(SEG *segs, int nsegs, float scale) { int i; for (i=nsegs-1;i>=0;i--) { segs->init_x1 *= scale; segs->init_y1 *= scale; segs->init_x2 *= scale; segs->init_y2 *= scale; segs->length = hypot(segs->init_x1-segs->init_x2,segs->init_y1-segs->init_y2); segs ++; } } static void scalesegs(void) { #define SCALESEGARRAY(arr) scalesegarray(&arr[0],ARRAYSIZE(arr),CONCAT(arr,_scale)) SCALESEGARRAY(shipsegs); SCALESEGARRAY(flame_t); SCALESEGARRAY(flame_l); SCALESEGARRAY(flame_r); SCALESEGARRAY(invsegs); SCALESEGARRAY(sinvsegs); SCALESEGARRAY(saucersmartsegs); SCALESEGARRAY(saucerdumbsegs); SCALESEGARRAY(leadsegs); #undef SCALESEGARRAY } static void highscore_open(void) { int i; int j; char *env; if (noscore_all) { highscore_fd = -1; return; } env = getenv(&highscore_file_env[0]); highscore_fd = open(env?env:&def_highscore_file[0],O_RDWR|O_CREAT,0666); if (highscore_fd >= 0) { if (flock(highscore_fd,LOCK_SH|LOCK_NB) < 0) { switch (errno) { case EWOULDBLOCK: break; default: close(highscore_fd); highscore_fd = -1; break; } } else { flock(highscore_fd,LOCK_UN); } } j = 0x31; for (i=0;i=0;i--) { if (scorelist[i].score < scorelist[i+1].score) { any = 1; se = scorelist[i]; scorelist[i] = scorelist[i+1]; scorelist[i+1] = se; } } for (i=0;iscore = 0; e->level = 0; e->name[0] = 0; e->name[1] = 0; e->name[2] = 0; } static int highscore_expire(void) { time_t now; double exp; int i; int any; time(&now); any = 0; exp = SCORELIST_EXPIRE; for (i=SCORELIST_SIZE;i>=0;i--) { if (scorelist[i].score && ((unsigned long int)(now-scorelist[i].when) > exp)) { if (i < SCORELIST_SIZE) bcopy(&scorelist[i+1],&scorelist[i],(SCORELIST_SIZE-i)*sizeof(SCOREENT)); highscore_blank(&scorelist[SCORELIST_SIZE]); } exp *= SCORELIST_EXP_BASE; any ++; } return(any); } static int highscore_insert(void) { int l; int m; int h; l = -1; h = SCORELIST_SIZE; while (h-l > 1) { m = (l + h) >> 1; if (scorelist[m].score >= score) l = m; else h = m; } scoreslot = h; if (h == SCORELIST_SIZE) return(0); bcopy(&scorelist[h],&scorelist[h+1],(SCORELIST_SIZE-h)*sizeof(SCOREENT)); scorelist[scoreslot].score = score; scorelist[scoreslot].level = level; time(&scorelist[scoreslot].when); scorelist[scoreslot].name[0] = 0; scorelist[scoreslot].name[1] = 0; scorelist[scoreslot].name[2] = 0; highscore_expire(); return(1); } static void queuebuttons(void) { unqdevice(KEYBD); qdevice(TURNLEFT); qdevice(TURNRIGHT); qdevice(ROCKET); qdevice(GUN); qdevice(HYPERSPACE); qdevice(ESCKEY); qdevice(HKEY); qdevice(F9KEY); qdevice(ZEROKEY); qdevice(ONEKEY); qdevice(TWOKEY); qdevice(THREEKEY); qdevice(FOURKEY); qdevice(FIVEKEY); qdevice(SIXKEY); qdevice(SEVENKEY); qdevice(EIGHTKEY); qdevice(NINEKEY); } static void queuekeybd(void) { qdevice(KEYBD); unqdevice(TURNLEFT); unqdevice(TURNRIGHT); unqdevice(ROCKET); unqdevice(GUN); unqdevice(HYPERSPACE); unqdevice(ESCKEY); unqdevice(HKEY); unqdevice(F9KEY); unqdevice(ZEROKEY); unqdevice(ONEKEY); unqdevice(TWOKEY); unqdevice(THREEKEY); unqdevice(FOURKEY); unqdevice(FIVEKEY); unqdevice(SIXKEY); unqdevice(SEVENKEY); unqdevice(EIGHTKEY); unqdevice(NINEKEY); } static void initships(void) { maxships = 1; ships = malloc(maxships*sizeof(SHIP *)); ships[0] = NEW(SHIP); nships = 0; nstowed = 0; nstowmove = 0; } static void initticks(void) { int i; for (i=0;i maxships) { ships = realloc(ships,n*sizeof(SHIP *)); for (;maxshipsdispmethod = SD_STOWED; ships[i]->obj.y = 30; ships[i]->obj.a = 90 DEGREES; ships[i]->obj.movetype = OMT_VEL; ships[i]->obj.v.x = 0; ships[i]->obj.v.y = 0; ships[i]->obj.v.a = 0; } nships = n; nstowed = n; nstowmove = 0; if (n > STOW_APART_N) { stowstate = STOW_TOGETHER; for (i=0;iobj.x = STOW_X; } else { stowstate = STOW_APART; for (i=0;iobj.x = (STOW_X - ((STOW_APART_N-1)*STOW_X_INC*.5)) + (STOW_X_INC * i); } playship = 0; } static void initgame(void) { realtime = 0; initticks(); wantdraw = 1; shots = 0; ourshots = 0; othershots = 0; score = 0; bonusleft = BONUS_INIT; bonuscountdown = 0; rocks = 0; nrocks = 0; debris = 0; scoreobj = 0; initnships(4); state = STATE_SHOWSCORE; saucerstate = SAUCER_NONE; recoil = 500; stateage = 0; insetup = 0; checkhsfile = 1; scoreline_y = TXTSIZ_SCORELINE; scoreline_desy = TXTSIZ_SCORELINE; hypertime = 0; scoreslot = -1; } static void add_debris(int mincount, int maxcount, int agelimit, OBJ *o, int rad, float avalnom) { int n; DEBRIS *d; int ox; int oy; float r; float a; float vfactor; for (n=mincount+rnd(maxcount+1-mincount);n>0;n--) { d = newdebris(); d->link = debris; debris = d; d->ageleft = rnd(agelimit); do { ox = (frndc() - .5) * 2 * rad; oy = (frndc() - .5) * 2 * rad; } while (hypot(ox,oy) > rad); d->obj.x = o->x + ox; d->obj.y = o->y + oy; d->obj.a = 0; d->obj.movetype = OMT_VEL; d->obj.v = o->v; r = (frndc() - .5) * 2 * DEBRIS_VELXY; a = frndo() * 360 DEGREES; vfactor = ((avalnom == 0) ? 1 : (o->v.a / avalnom)) + frndo(); d->obj.v.x += r * cos(a) * vfactor; d->obj.v.y += r * sin(a) * vfactor; d->obj.v.a = 0; } } static void add_shot_debris(SHOT *s) { add_debris(5,5,DEBRIS_DECAY,&s->obj,1,0); } static void clearshots(int dodebris) { if (shots && dodebris) sound_once(SOUND_SHOTDIE); while (shots) { SHOT *s; s = shots; shots = s->link; if (dodebris) add_shot_debris(s); freeshot(s); } ourshots = 0; othershots = 0; } static void initlevel(void) { int i; clearshots(0); cleardebris(); while (rocks) { ROCK *r; r = rocks->link; freerock(rocks); rocks = r; } i = level; for (;i>0;i--) newbigrock(); bonusleft = level * BONUS_INIT; levelage = 0; saucertimer = 0; if (saucerstate != SAUCER_DONT) saucerstate = SAUCER_NONE; } static void movecd(OBJ *o) { float dx; float dy; float da; float dxy; float txy; float ta; int t; if (o->movetype != OMT_DST) return; dx = o->d.x - o->x; dy = o->d.y - o->y; da = o->d.a - o->a; if (da > M_PI) da -= 2*M_PI; else if (da < -M_PI) da += 2*M_PI; dxy = hypot(dx,dy); txy = (dxy <= o->d.vxy) ? 1 : (dxy / o->d.vxy); ta = (fabs(da) <= o->d.va) ? 1 : (fabs(da) / o->d.va); t = ceil((txy>ta)?txy:ta) + .1; o->v.x = (o->d.x - o->x) / t; o->v.y = (o->d.y - o->y) / t; o->v.a = da / t; o->movesleft = t; o->movetype = OMT_C_D; } #define MOVECD(o) (((o)->movetype == OMT_DST) && (movecd((o)),1)) static float closest_t(float dx, float dy, float dvx, float dvy) { float num; float den; num = - (dx * dvx) - (dy * dvy); den = (dvx * dvx) + (dvy * dvy); if (num <= 0) return(-1); if (num > den*SHOT_MAXAGE) { return(SHOT_MAXAGE+1); } return(num/den); } /* there is something slightly wrong with this code. I haven't bothered fixing it because it looks hairy and because it's not really a problem if the smart saucers aren't quite perfect survivalists. */ static int saucer_collision_danger(SAUCER *s, OBJ *o, float size) { float dx; float dy; float dvx; float dvy; float ndx; float ndy; float bestt; float bestd; float t; float d; int i; int j; dx = o->x - s->obj.x; dy = o->y - s->obj.y; MOVECD(o); MOVECD(&s->obj); dvx = o->v.x - s->obj.v.x; dvy = o->v.y - s->obj.v.y; bestt = SHOT_MAXAGE + 1; bestd = 1e10; dx += XMAXSCREEN; dy += YMAXSCREEN; for (i=0;i<3;i++) { for (j=0;j<3;j++) { if (hypot(dx,dy) < s->size+size+SAUCER_MARGIN) return(1); t = closest_t(dx,dy,dvx,dvy); if ((t > 0) && (t < SAUCER_FORESEE)) { ndx = (o->x - s->obj.x) + (t * dvx); ndy = (o->y - s->obj.y) + (t * dvy); while (ndx > XMAXSCREEN/2) ndx -= XMAXSCREEN; while (ndx < -XMAXSCREEN/2) ndx += XMAXSCREEN; while (ndy > YMAXSCREEN/2) ndy -= YMAXSCREEN; while (ndy < -YMAXSCREEN/2) ndy += YMAXSCREEN; d = hypot(ndx,ndy); if (d < bestd) { bestt = t; bestd = d; } } dx -= XMAXSCREEN; } dx += 3 * XMAXSCREEN; dy -= YMAXSCREEN; } if ((bestt < SHOT_MAXAGE) && (bestd < s->size+size+SAUCER_MARGIN)) return(1); return(0); } static void drawscoreline(void) { char strbuf[256]; if (noscore_all || noscore_one) { sprintf(&strbuf[0],"Level: %d Score: (%d) Bonus left: %d",level,score,(bonusleft<0)?0:bonusleft); } else { sprintf(&strbuf[0],"Level: %d Score: %d Bonus left: %d",level,score,(bonusleft<0)?0:bonusleft); } pushmatrix(); translate(XMAXSCREEN*.5,scoreline_y,0); scale(TXTSIZ_SCORELINE,TXTSIZ_SCORELINE,1); font_centrestring(&strbuf[0]); popmatrix(); if (scoreline_y+TXTSIZ_SCORELINE > YMAXSCREEN) { pushmatrix(); translate(XMAXSCREEN*.5,scoreline_y-YMAXSCREEN,0); scale(TXTSIZ_SCORELINE,TXTSIZ_SCORELINE,1); font_centrestring(&strbuf[0]); popmatrix(); } } static void tickscoreline(float y) { if ((scoreline_desy < YMAXSCREEN/2) && (y < (2*TXTSIZ_SCORELINE)+SHIPSIZE+SCORELINE_MARGIN)) { scoreline_desy = YMAXSCREEN - (2*TXTSIZ_SCORELINE); } else if ((scoreline_desy > YMAXSCREEN/2) && (y > YMAXSCREEN-(2*TXTSIZ_SCORELINE)-SHIPSIZE-SCORELINE_MARGIN)) { scoreline_desy = TXTSIZ_SCORELINE; } if (scoreline_y == scoreline_desy) return; if (scoreline_desy < YMAXSCREEN/2) { scoreline_y += SCORELINE_SPEED; if (scoreline_y >= YMAXSCREEN) scoreline_y -= YMAXSCREEN; if ((scoreline_y < YMAXSCREEN/2) && (scoreline_y >= scoreline_desy)) scoreline_y = scoreline_desy; } else { scoreline_y -= SCORELINE_SPEED; if (scoreline_y < 0) scoreline_y += YMAXSCREEN; if ((scoreline_y >= YMAXSCREEN/2) && (scoreline_y < scoreline_desy)) scoreline_y = scoreline_desy; } } static void tickscorelinec(void) { scoreline_desy = TXTSIZ_SCORELINE; tickscoreline(YMAXSCREEN/2); } static void drawobj(OBJ *o, void (*f)(void *), void *a) { pushmatrix(); translate(o->x,o->y,0); rot(o->a/(1 DEGREES),'z'); (*f)(a); popmatrix(); } static void drawsegs(SEG *s, int n) { float p[2]; for (;n>0;n--,s++) { bgnline(); p[0] = s->init_x1; p[1] = s->init_y1; v2f(p); p[0] = s->init_x2; p[1] = s->init_y2; v2f(p); endline(); } } static void drawseg(void *arg) { SEG *ss; float p[2]; ss = (SEG *) arg; scale(ss->length,ss->length,1); bgnline(); p[0] = -.5; p[1] = 0; v2f(p); p[0] = .5; v2f(p); endline(); } static void drawsegobjs(SEG *s, int n) { for (;n>0;n--,s++) drawobj(&s->obj,drawseg,s); } static void drawbigship(void *arg) { DRAWSEGARRAY(shipsegs); if ((arg == FLAMES) && playship) { if (jetbits & jets[JET_FWD].bit) DRAWSEGARRAY(flame_t); if (jetbits & jets[JET_LEFT].bit) DRAWSEGARRAY(flame_l); if (jetbits & jets[JET_RIGHT].bit) DRAWSEGARRAY(flame_r); } if (invulnerable & INVUL_VISIBLE) DRAWSEGARRAY(invsegs); } static void drawsmallship(void *arg __attribute__ ((unused))) { pushmatrix(); scale(SMALLSHIP_SCALE,SMALLSHIP_SCALE,1); DRAWSEGARRAY(shipsegs); popmatrix(); } static void sortstow(void) { int i; int x; int lastm; SHIP *s; /* yes, I know it's a bubble sort. It'll usually be sorted, and the sort must be stable for the non-stowed ships. */ do { x = 0; s = ships[0]; for (i=1;idispmethod; s = ships[i]; if ( (lastm != SD_STOWED) && (lastm != SD_STOWMOVE) && ( (s->dispmethod == SD_STOWED) || (s->dispmethod == SD_STOWMOVE) ) ) { ships[i] = ships[i-1]; ships[i-1] = s; s = ships[i]; x ++; } } } while (x > 0); } static void doshowstow(void) { int i; int y; const char *cp; SHIP *s; long int p[2]; char strbuf[256]; char badbuf[20]; pushmatrix(); switch (stowstate) { case STOW_APART: cp = "APART"; break; case STOW_TOGETHER: cp = "TOGETHER"; break; case STOW_MERGING: cp = "MERGING"; break; case STOW_SPLITTING: cp = "SPLITTING"; break; default: sprintf(&badbuf[0],"%d",stowstate); cp = &badbuf[0]; break; } sprintf(&strbuf[0]," stowstate=STOW_%-9s nstowed=%d nstowmove=%d",cp,nstowed,nstowmove); translate(XMAXSCREEN/4,SHOWSTOW_TOPY,0); scale(TXTSIZ_SHOWSTOW_X,TXTSIZ_SHOWSTOW_Y,1); font_leftstring(&strbuf[0]); popmatrix(); for (i=0;idispmethod) { case SD_NONE: cp = "NONE"; break; case SD_PLAYING: cp = "PLAYING"; break; case SD_RECENTER: cp = "RECENTER"; break; case SD_FROMSTOW: cp = "FROMSTOW"; break; case SD_CENTER: cp = "CENTER"; break; case SD_TOSTOW: cp = "TOSTOW"; break; case SD_STOWED: cp = "STOWED"; break; case SD_STOWMOVE: cp = "STOWMOVE"; break; case SD_HYPER: cp = "HYPER"; break; default: sprintf(&badbuf[0],"%d",s->dispmethod); cp = &badbuf[0]; break; } sprintf(&strbuf[0],"%2d: dispmethod=SD_%-8s movetype=",i,cp); switch (s->obj.movetype) { case OMT_VEL: sprintf(&strbuf[strlen(strbuf)],"OMT_VEL: %g %g %g",s->obj.v.x,s->obj.v.y,s->obj.v.a); break; case OMT_DST: sprintf(&strbuf[strlen(strbuf)],"OMT_DST: %g %g %g %g %g",s->obj.d.x,s->obj.d.y,s->obj.d.a,s->obj.d.vxy,s->obj.d.va); break; case OMT_C_D: sprintf(&strbuf[strlen(strbuf)],"OMT_C_D: %g %g %g %g %g -> %g %g %g / %d",s->obj.d.x,s->obj.d.y,s->obj.d.a,s->obj.d.vxy,s->obj.d.va,s->obj.v.x,s->obj.v.y,s->obj.v.a,s->obj.movesleft); break; } font_leftstring(&strbuf[0]); popmatrix(); bgnline(); p[0] = XMAXSCREEN/4; p[1] = y + (TXTSIZ_SHOWSTOW_Y*.75); v2i(p); p[0] = (XMAXSCREEN/4) - (3*TXTSIZ_SHOWSTOW_X); p[1] = y + (TXTSIZ_SHOWSTOW_Y*.75); v2i(p); p[0] = s->obj.x; p[1] = s->obj.y; v2i(p); endline(); } } static void draw_hyper_ship(void) { float s; float t; float tr; tr = hypertime / (float)HYPER_TIME; t = ((hypertime > HYPER_TIME/2) ? HYPER_TIME - hypertime : hypertime) / (HYPER_TIME/2.); pushmatrix(); translate( (playship->obj.x * (1-t)) + ((XMAXSCREEN/2) * t), (playship->obj.y * (1-t)) + ((YMAXSCREEN/2) * t), 0 ); rot(playship->obj.a+(tr*360),'z'); s = (1 * (1-t)) + ((YMAXSCREEN/(float)SHIPSIZE) * t); scale(s,s,1); drawbigship(NOFLAMES); popmatrix(); } static void drawships(void) { int i; SHIP *s; sortstow(); if (showstow) doshowstow(); for (i=0;idispmethod) { case SD_NONE: break; case SD_PLAYING: drawobj(&s->obj,drawbigship,FLAMES); break; case SD_RECENTER: case SD_CENTER: drawobj(&s->obj,drawbigship,NOFLAMES); break; case SD_FROMSTOW: case SD_TOSTOW: drawobj(&s->obj,drawsmallship,0); break; case SD_STOWED: case SD_STOWMOVE: break; case SD_HYPER: draw_hyper_ship(); break; default: fprintf(stderr,"bad dispmethod %d for %x in drawships\n",s->dispmethod,(int)s); abort(); break; } } if (nstowed > 0) { switch (stowstate) { case STOW_APART: case STOW_SPLITTING: for (i=nstowed-1;i>=0;i--) { s = ships[i]; drawobj(&s->obj,drawsmallship,0); } break; case STOW_MERGING: if (nstowed-nstowmove > 1) { pushmatrix(); translate(STOW_X-(SHIPSIZE*SMALLSHIP_SCALE*2),STOW_Y-(TXTSIZ_STOWCOUNT/2),0); scale(TXTSIZ_STOWCOUNT,TXTSIZ_STOWCOUNT,1); { char strbuf[32]; sprintf(&strbuf[0],"%d",nstowed-nstowmove); font_rightstring(&strbuf[0]); } popmatrix(); } for (i=nstowed-1;i>=0;i--) { s = ships[i]; drawobj(&s->obj,drawsmallship,0); } break; case STOW_TOGETHER: pushmatrix(); translate(STOW_X-(SHIPSIZE*SMALLSHIP_SCALE*2),STOW_Y-(TXTSIZ_STOWCOUNT/2),0); scale(TXTSIZ_STOWCOUNT,TXTSIZ_STOWCOUNT,1); { char strbuf[32]; sprintf(&strbuf[0],"%d",nstowed); font_rightstring(&strbuf[0]); } popmatrix(); pushmatrix(); translate(STOW_X,STOW_Y,0); rot(90.,'z'); drawsmallship(0); popmatrix(); break; } } } static void drawrock(void *arg) { ROCK *r; int i; ROCKSHAPE *s; r = (ROCK *) arg; if (insetup && (r == flashing_rock) && ((setupage % (2*SETUP_ROCKFLASH)) < SETUP_ROCKFLASH)) return; scale(r->shapeinv?-r->psize:r->psize,r->psize,1); s = &rockshapes[r->shape]; bgnclosedline(); for (i=0;inpoints;i++) v2f(s->points[i]); endclosedline(); } static void drawshot(void *arg) { long int p[2]; if (((SHOT *)arg)->ours) { bgnline(); p[0] = -1; p[1] = 0; v2i(p); p[0] = 1; v2i(p); endline(); bgnline(); p[0] = 0; p[1] = -1; v2i(p); p[1] = 1; v2i(p); endline(); } else { bgnline(); p[0] = -2; p[1] = -2; v2i(p); p[0] = 2; p[1] = 2; v2i(p); endline(); bgnline(); p[0] = -2; p[1] = 2; v2i(p); p[0] = 2; p[1] = -2; v2i(p); endline(); } } static void draw1debris(void *arg) { long int p[2]; if (((DEBRIS *)arg)->ageleft >= (DEBRIS_DECAY*2)) { bgnclosedline(); p[0] = -2; p[1] = 0; v2i(p); p[0] = 0; p[1] = 2; v2i(p); p[0] = 2; p[1] = 0; v2i(p); p[0] = 0; p[1] = -2; v2i(p); endclosedline(); } else if (((DEBRIS *)arg)->ageleft >= DEBRIS_DECAY) { bgnclosedline(); p[0] = -1; p[1] = 0; v2i(p); p[0] = 0; p[1] = 1; v2i(p); p[0] = 1; p[1] = 0; v2i(p); p[0] = 0; p[1] = -1; v2i(p); endclosedline(); } else { bgnpoint(); p[0] = 0; p[1] = 0; v2i(p); endpoint(); } } static void drawdebris(void) { DEBRIS *d; for (d=debris;d;d=d->link) { drawobj(&d->obj,draw1debris,d); } } static void drawscoreobj(void *sov) { SCOREOBJ *so; so = sov; scale(TXTSIZ_SCOREOBJ,TXTSIZ_SCOREOBJ,1); translate(0,-.5,0); font_centrestring(&so->txt[0]); } static void drawscoreobjs(void) { SCOREOBJ *so; for (so=scoreobj;so;so=so->link) drawobj(&so->obj,drawscoreobj,so); } static void drawobjects(void) { ROCK *r; SHOT *s; int nr; nr = 0; for (r=rocks;r;r=r->link) { drawobj(&r->obj,drawrock,r); nr ++; } if (nr != nrocks) { fprintf(stderr,"nrocks = %d, actual value is %d\n",nrocks,nr); nrocks = nr; } if (setup_link_alerts && (saucerstate == SAUCER_HAVE)) { ROCK *r; for (r=rocks;r;r=r->link) { if (saucer_collision_danger(&saucer,&r->obj,r->psize)) { float p[2]; bgnline(); p[0] = saucer.obj.x; p[1] = saucer.obj.y; v2f(p); p[0] = r->obj.x; p[1] = r->obj.y; v2f(p); endline(); } } } for (s=shots;s;s=s->link) { drawobj(&s->obj,drawshot,s); } drawdebris(); drawscoreobjs(); } static void drawsaucer(void *arg) { int i; float p[2]; if (((SAUCER *)arg)->smart) { DRAWSEGARRAY(saucersmartsegs); } else { DRAWSEGARRAY(saucerdumbsegs); } if (((SAUCER *)arg)->size != SHIPSIZE) { float s; s = ((SAUCER *)arg)->size / (float)SHIPSIZE; scale(s,s,1); } if (sinvulnerable) DRAWSEGARRAY(sinvsegs); if (saucer_shoot_override) { if ((saucer_shoot_override & SSO_LEAD) && ((realtime / 15) & 1)) DRAWSEGARRAY(leadsegs); pushmatrix(); switch (saucer_shoot_override & ~SSO_LEAD) { case SSO_NONE: break; case SSO_RANDOM: dorandom:; for (i=0;i<360;i+=30) { rot(30.,'z'); bgnline(); p[0] = 0; p[1] = 0; v2f(p); p[0] = (3 * ((SAUCER *)arg)->size) / 2; v2f(p); endline(); } break; case SSO_PLAYER: if (! playship) goto dorandom; loadmatrix(noxfmat); bgnline(); p[0] = playship->obj.x; p[1] = playship->obj.y; v2f(p); p[0] = ((SAUCER *)arg)->obj.x; p[1] = ((SAUCER *)arg)->obj.y; v2f(p); endline(); break; case SSO_ROCK: { ROCK *r; r = randomrock(); if (r == 0) goto dorandom; loadmatrix(noxfmat); bgnline(); p[0] = r->obj.x; p[1] = r->obj.y; v2f(p); p[0] = ((SAUCER *)arg)->obj.x; p[1] = ((SAUCER *)arg)->obj.y; v2f(p); endline(); } break; } popmatrix(); } } static void drawthings(void) { drawobjects(); drawscoreline(); switch (saucerstate) { case SAUCER_HAVE: drawobj(&saucer.obj,drawsaucer,&saucer); break; case SAUCER_DEAD: drawsegobjs(deadsaucersegs,deadsaucernsegs); break; } } static void show_gameover(unsigned int age) { float f; pushmatrix(); f = (age < GAMEOVER_GROWTIME) ? 0 : (age < GAMEOVER_TOTALTIME) ? ((age-GAMEOVER_GROWTIME)/(float)GAMEOVER_RISETIME) : 1; translate( XMAXSCREEN/2, ((YMAXSCREEN/2.)*(1-f)) + ((YMAXSCREEN-GAMEOVER_BELOW)*f), 0 ); f = (age < GAMEOVER_GROWTIME) ? (age / (float)GAMEOVER_GROWTIME) : 1; scale(f*TXTSIZ_GAMEOVER,f*TXTSIZ_GAMEOVER,1); font_centrestring("GAME OVER"); popmatrix(); } static void drawscorelistline(time_t now, int lno, int underpos, int mark) { int j; struct tm *when; static const char *daynames[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *monthnames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char linebuf[256]; if (scorelist[lno].score == 0) { sprintf(&linebuf[0],"%3d. %*s %*d %12s",lno+1,scorelist_lw,"",scorelist_sw,0,""); } else { unsigned int delta; when = localtime(&scorelist[lno].when); delta = now - scorelist[lno].when; if (delta > 60*60*24*182) { sprintf(&linebuf[0],"%3d. %*d %*d %3s %2d \1\1\2%04d\1\1\2",lno+1, scorelist_lw,scorelist[lno].level,scorelist_sw,scorelist[lno].score, monthnames[when->tm_mon],when->tm_mday,when->tm_year+1900); } else if (delta > 60*60*24*7) { sprintf(&linebuf[0],"%3d. %*d %*d %3s %2d %02d:%02d",lno+1, scorelist_lw,scorelist[lno].level,scorelist_sw,scorelist[lno].score, monthnames[when->tm_mon],when->tm_mday,when->tm_hour,when->tm_min); } else { sprintf(&linebuf[0],"%3d. %*d %*d \1\1\2%3s\1\1\2 %02d:%02d",lno+1, scorelist_lw,scorelist[lno].level,scorelist_sw,scorelist[lno].score, daynames[when->tm_wday],when->tm_hour,when->tm_min); } for (j=0;j<3;j++) linebuf[j+5] = scorenamevec[scorelist[lno].name[j]]; } font_centrestring(&linebuf[0]); j = 3+2+3+2+scorelist_lw+2+scorelist_sw+2+12; if (underpos >= 0) { memset(&linebuf[0],' ',j); linebuf[j] = '\0'; linebuf[5+underpos] = '_'; font_centrestring(&linebuf[0]); } if (mark) { memset(&linebuf[1],' ',j+2); linebuf[0] = '>'; linebuf[j+3] = '<'; linebuf[j+4] = '\0'; font_centrestring(&linebuf[0]); } } static void compute_scorelist_widths(void) { int maxlevel; int i; char linebuf[256]; maxlevel = scorelist[0].level; for (i=1;i maxlevel) maxlevel = scorelist[i].level; sprintf(&linebuf[0],"%d",maxlevel); scorelist_lw = strlen(&linebuf[0]); if (scorelist_lw < 5) scorelist_lw = 5; /* "Level" */ sprintf(&linebuf[0],"%d",scorelist[0].score); scorelist_sw = strlen(&linebuf[0]); if (scorelist_sw < 5) scorelist_sw = 5; /* "Score" */ } static void draw_scorelist_headings(void) { char linebuf[256]; sprintf(&linebuf[0],"Rank Who %*s %*s When ",scorelist_lw,"Level",scorelist_sw,"Score"); font_centrestring(&linebuf[0]); translate(0,-2,0); } static void draw_scorelist_nonscoring(void) { pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_SCOREMSG,TXTSIZ_SCOREMSG,1); font_centrestring("Non-scoring game"); popmatrix(); } static void draw_scorelist_unavailable(void) { pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_SCOREMSG,TXTSIZ_SCOREMSG,1); font_centrestring("High score list unavailable"); popmatrix(); } static void drawscorelist(void (*fn)(time_t, int)) { int i; time_t now; if (noscore_all) { draw_scorelist_nonscoring(); return; } if (highscore_fd < 0) { draw_scorelist_unavailable(); return; } time(&now); compute_scorelist_widths(); pushmatrix(); translate(XMAXSCREEN/2,(YMAXSCREEN-GAMEOVER_BELOW+STARTGAME_Y)/2,0); scale(TXTSIZ_SCORELIST,TXTSIZ_SCORELIST,1); translate(0,(SCORELIST_SIMUL+2)/2.0,0); draw_scorelist_headings(); for (i=0;i SCORELIST_MAXSCROLL) return(v+SCORELIST_SIZE-SCORELIST_SIMUL); return(v+(around-SCORELIST_MINSCROLL)); } static void dsl_newscore(time_t now, int i) { i = around_map(scoreslot,i); drawscorelistline(now,i,((i==scoreslot)&&((stateage/5)&1))?scoreletter:-1,i==scoreslot); } static void limit_scorescroll(void) { if (scorescroll < SCORELIST_MINSCROLL) scorescroll = SCORELIST_MINSCROLL; if (scorescroll > SCORELIST_MAXSCROLL) scorescroll = SCORELIST_MAXSCROLL; } static void dsl_showscore(time_t now, int i) { if (hschange == 0) { hsstate = HSS_SLOT; hschange = SHOWSCORE_INITIALTIME; hssinc = 0; hssdec = 0; hssrep = 0; } if (stateage >= hschange) { switch (hsstate) { case HSS_SLOT: scorescroll = scoreslot; if (scorescroll < 0) scorescroll = 0; /* fall through */ case HSS_TOPSLOT: case HSS_SCROLL: hsstate = HSS_STEP; hschange += SHOWSCORE_EACHTIME; break; case HSS_STEP: scorescroll ++; if ((scorescroll >= SCORELIST_SIZE) || !scorelist[scorescroll].score) { hsstate = HSS_TOP; hschange += SHOWSCORE_TOPTIME; } else { hschange += SHOWSCORE_EACHTIME; } break; case HSS_TOP: if (scoreslot >= SCORELIST_SIMUL) { hsstate = HSS_TOPSLOT; hschange += SHOWSCORE_SLOTTIME; } else { hsstate = HSS_STEP; hschange += SHOWSCORE_EACHTIME; } scorescroll = 0; break; default: hsstate = HSS_STEP; scorescroll = 0; } } switch (hsstate) { case HSS_SLOT: case HSS_TOPSLOT: i = around_map(scoreslot,i); drawscorelistline(now,i,-1,i==scoreslot); break; case HSS_SCROLL: i = around_map(scorescroll,i); drawscorelistline(now,i,-1,i==scoreslot); break; case HSS_STEP: if (i) return; pushmatrix(); translate(0,-.5*(SCORELIST_SIMUL-1),0); drawscorelistline(now,scorescroll,-1,i==scoreslot); popmatrix(); break; case HSS_TOP: drawscorelistline(now,i,-1,i==scoreslot); break; } } static void dsl_exiting(time_t now, int i) { drawscorelistline(now,i,-1,0); } static void drawhelpline(void) { pushmatrix(); translate(XMAXSCREEN/2,STARTGAME_Y,0); scale(TXTSIZ_HELPLINES,TXTSIZ_HELPLINES,1); translate(0,-2,0); font_centrestring("Press h for help"); popmatrix(); } static void check_cm_change(void) { if (curmenu != lastcurmenu) { if (lastcurmenu->leave) (*lastcurmenu->leave)(); if (curmenu->enter) (*curmenu->enter)(); lastcurmenu = curmenu; } } static void draw_setup(void) { int nitems; int maxlen; int l; int i; const char *cp; char nstr[2]; char txtbuf[256]; check_cm_change(); nitems = 0; maxlen = 0; for (i=0;inentries;i++) { if ((curmenu->entries[i].key & WIZ) && !iswiz) continue; if ((curmenu->entries[i].key & NONWIZ) && iswiz) continue; { nitems ++; l = strlen(curmenu->entries[i].text); if (l > maxlen) maxlen = l; } } maxlen += 3; pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN,0); scale(TXTSIZ_SETUPMENU,TXTSIZ_SETUPMENU,1); translate(-maxlen*ASPECT/2,-2,0); nstr[1] = '\0'; for (i=0;inentries;i++) { if ((curmenu->entries[i].key & WIZ) && !iswiz) continue; if ((curmenu->entries[i].key & NONWIZ) && iswiz) continue; { translate(0,-1,0); pushmatrix(); if ((curmenu->entries[i].key & KNMASK) < 10) { nstr[0] = '0' + (curmenu->entries[i].key & KNMASK); font_leftstring(nstr); } translate(3*ASPECT,0,0); font_leftstring(curmenu->entries[i].text); popmatrix(); } } if (setup_str_op) { pushmatrix(); translate(0,-2,0); (*setup_str_op)(SSE_DRAW); popmatrix(); } popmatrix(); if (do_rockshape_overlay) { ROCKSHAPE *s; float sc; int i; pushmatrix(); translate(XMAXSCREEN/4,YMAXSCREEN/4,0); sc = YMAXSCREEN / 5; scale(rockshape_overlay_invert?-sc:sc,sc,1); s = &rockshapes[rockshape_overlay]; bgnclosedline(); for (i=0;inpoints;i++) v2f(s->points[i]); endclosedline(); popmatrix(); pushmatrix(); translate((3*XMAXSCREEN)/4,YMAXSCREEN/4,0); sc = YMAXSCREEN / 5; scale(rockshape_overlay_invert?-sc:sc,sc,1); rot(setupage*SETUP_ROCKSPIN,'z'); s = &rockshapes[rockshape_overlay]; bgnclosedline(); for (i=0;inpoints;i++) v2f(s->points[i]); endclosedline(); popmatrix(); pushmatrix(); translate(XMAXSCREEN/2,TXTSIZ_RSINVERT*2,0); scale(TXTSIZ_RSINVERT,TXTSIZ_RSINVERT,1); font_centrestring(rockshape_overlay_invert?"INVERTED":"NORMAL"); popmatrix(); } if (flashing_rock) { pushmatrix(); translate(XMAXSCREEN-(ASPECT*TXTSIZ_ROCKINFO),YMAXSCREEN-(TXTSIZ_ROCKINFO*1.5),0); scale(TXTSIZ_ROCKINFO,TXTSIZ_ROCKINFO,1); sprintf(&txtbuf[0],"rock %d: sz=%d sh=%d (%d,%d,%.1f) v(%.2f,%.2f,%.2f)", flashing_rock_n, flashing_rock->psize, flashing_rock->shapeinv?-flashing_rock->shape:flashing_rock->shape, (int)flashing_rock->obj.x, (int)flashing_rock->obj.y, flashing_rock->obj.a, flashing_rock->obj.v.x, flashing_rock->obj.v.y, flashing_rock->obj.v.a ); font_rightstring(&txtbuf[0]); popmatrix(); } if (do_scorelist_overlay) { time_t now; time(&now); compute_scorelist_widths(); if (noscore_all) { draw_scorelist_nonscoring(); } else if (highscore_fd < 0) { draw_scorelist_unavailable(); } else { pushmatrix(); translate(XMAXSCREEN/2,2,0); scale(TXTSIZ_SCORELIST,TXTSIZ_SCORELIST,1); translate(0,SCORELIST_SIMUL+2,0); draw_scorelist_headings(); if (scorelist_overlay_base+SCORELIST_SIMUL > SCORELIST_SIZE) scorelist_overlay_base = SCORELIST_SIZE-SCORELIST_SIMUL; if (scorelist_overlay_base < 0) scorelist_overlay_base = 0; for (i=0;i>",state); cp = &txtbuf[0]; break; } sprintf(&txtbuf[0],"%s age=%u",cp,stateage); font_leftstring(&txtbuf[0]); translate(0,-1,0); switch (saucerstate) { case SAUCER_NONE: cp = "SAUCER_NONE"; break; case SAUCER_HAVE: cp = "SAUCER_HAVE"; break; case SAUCER_DEAD: cp = "SAUCER_DEAD"; break; case SAUCER_DONT: cp = "SAUCER_DONT"; break; default: sprintf(&txtbuf[0],"<>",saucerstate); cp = &txtbuf[0]; break; } font_leftstring(cp); translate(0,-1,0); switch (saucerstate) { case SAUCER_HAVE: sprintf(&txtbuf[0],"saucer: age=%d smart=%d size=%d",saucer.age,saucer.smart,saucer.size); font_leftstring(&txtbuf[0]); translate(0,-1,0); sprintf(&txtbuf[0]," timers (m=%d s=%d) canshoot=%d movea=%g",saucer.movetimer,saucer.shoottimer,saucer.canshoot,saucer.movea); font_leftstring(&txtbuf[0]); translate(0,-1,0); break; } sprintf(&txtbuf[0],"nships=%d nrocks=%d\n",nships,nrocks); font_leftstring(&txtbuf[0]); if (iswiz) { translate(0,-1,0); font_leftstring("WIZ"); } switch (state) { case STATE_NEWSCORE: sprintf(&txtbuf[0],"scoreletter=%d name=%d %d %d", scoreletter, scorelist[scoreslot].name[0], scorelist[scoreslot].name[1], scorelist[scoreslot].name[2]); translate(0,-1,0); font_leftstring(&txtbuf[0]); break; } popmatrix(); } static void draw_helpscreen(void) { const char *scr; char *nlp; float xscale; float yscale; if (helpscreen == nhelpscreens-1) seenlasthelp = 1; pushmatrix(); translate(0,YMAXSCREEN,0); xscale = (XMAXSCREEN - (2 * HELP_BORDER)) / (float) (helpmaxmw * ASPECT); yscale = (YMAXSCREEN - (2 * HELP_BORDER)) / (float) ((helpmaxnl+5) * 1.25); if (xscale < yscale) { translate(HELP_BORDER,(((helpmaxnl+5)*1.25*xscale)-YMAXSCREEN)/2,0); scale(xscale,xscale,1); } else { translate((XMAXSCREEN-(helpmaxmw*ASPECT*yscale))/2,-HELP_BORDER,0); scale(yscale,yscale,1); } scr = helpscreens[helpscreen]; translate(0,.125,0); pushmatrix(); while (1) { nlp = index(scr,'\n'); if (nlp == 0) break; translate(0,-1.25,0); font_leftstring(scr); scr = nlp + 1; } popmatrix(); translate(0,.125-(1.25*(helpmaxnl+3)),0); if (seenlasthelp) { static char *t = 0; if (t == 0) { const char *n; n = keyname(HYPERSPACE); t = malloc(6+strlen(n)+13+1); sprintf(t,"Press %s to exit help",n); } font_leftstring(t); } translate(0,-1.25,0); if (helpscreen < nhelpscreens-1) { static char *t = 0; if (t == 0) { const char *n; n = keyname(TURNRIGHT); t = malloc(6+strlen(n)+9+1); sprintf(t,"Press %s to go on",n); } font_leftstring(t); } translate(0,-1.25,0); if (helpscreen > 0) { static char *t = 0; if (t == 0) { const char *n; n = keyname(TURNLEFT); t = malloc(6+strlen(n)+11+1); sprintf(t,"Press %s to go back",n); } font_leftstring(t); } popmatrix(); } static void draw_time(void) { static unsigned long int prev = 0; static char clocktxt[6] = "00:00"; unsigned long int s; s = cursec(); if (s != prev) { time_t t; struct tm *tm; prev = s; t = s; tm = localtime(&t); strftime(&clocktxt[0],sizeof(clocktxt),"%H:%M",tm); } pushmatrix(); translate(XMAXSCREEN-(ASPECT*TXTSIZ_CLOCK),YMAXSCREEN-(TXTSIZ_CLOCK*1.5),0); scale(TXTSIZ_CLOCK,TXTSIZ_CLOCK,1); font_rightstring(&clocktxt[0]); if (do_fps) { char fps[16]; sprintf(&fps[0],"%d/%d",tickrate[0],TICKS_PER_SEC); translate(0,-1,0); font_rightstring(&fps[0]); } popmatrix(); } static void drawalert(void) { int alert; alert = 0; if (saucerstate == SAUCER_HAVE) { if ((stateage / ALERT_FLASH) & 1) { pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN-(TXTSIZ_ALERT*1.1),0); scale(TXTSIZ_ALERT,TXTSIZ_ALERT,1); font_centrestring("ALERT"); popmatrix(); } alert = 1; } sound_repeat(SOUND_ALERT,alert); } static void drawit(void) { char strbuf[256]; if (! suppress_display) { draw_time(); switch (state) { case STATE_WAITBEGIN: drawscoreline(); drawships(); if (level == 1) drawhelpline(); pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_BEGINLEVEL,TXTSIZ_BEGINLEVEL,1); sprintf(&strbuf[0],"Press FIRE to begin level %d",level); font_centrestring(&strbuf[0]); popmatrix(); break; case STATE_RESUME: pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_CONTINUE,TXTSIZ_CONTINUE,1); font_centrestring("Press FIRE to continue"); popmatrix(); /* fall through */ case STATE_PLAYING: drawthings(); drawships(); drawalert(); break; case STATE_EXPLOSION: drawthings(); drawships(); drawalert(); DRAWSEGARRAY_OBJ(shipsegs); break; case STATE_GAMEOVER: drawthings(); show_gameover(stateage); break; case STATE_SCOREWAIT: drawthings(); show_gameover(GAMEOVER_TOTALTIME); if (stateage > SCOREWAIT_BUSYTIME) { pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_SCOREBUSY,TXTSIZ_SCOREBUSY,1); font_centrestring("Score file busy, waiting..."); popmatrix(); } break; case STATE_NEWSCORE: drawscorelist(&dsl_newscore); drawthings(); show_gameover(GAMEOVER_TOTALTIME); break; case STATE_SHOWSCORE: drawscorelist(&dsl_showscore); drawthings(); drawhelpline(); show_gameover(GAMEOVER_TOTALTIME); pushmatrix(); translate(XMAXSCREEN/2,STARTGAME_Y,0); scale(TXTSIZ_HELPLINES,TXTSIZ_HELPLINES,1); translate(0,-1,0); font_centrestring("Press HYPERSPACE to start a new game"); popmatrix(); break; case STATE_ENDLEVEL: drawscoreline(); drawdebris(); drawships(); drawscoreobjs(); pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale(TXTSIZ_BONUSCOUNT,TXTSIZ_BONUSCOUNT,1); pushmatrix(); translate(0,1,0); sprintf(&strbuf[0],"Bonus: %d",(bonusleft>0)?bonusleft:0); font_centrestring(&strbuf[0]); popmatrix(); pushmatrix(); translate(0,-1,0); sprintf(&strbuf[0],"Score: %d",score); font_centrestring(&strbuf[0]); popmatrix(); popmatrix(); break; case STATE_EXITING: pushmatrix(); translate(XMAXSCREEN/2,YMAXSCREEN/2,0); scale((EXIT_TIME-stateage)/(float)EXIT_TIME,(EXIT_TIME-stateage)/(float)EXIT_TIME,1); translate(-XMAXSCREEN/2,-YMAXSCREEN/2,0); drawscorelist(&dsl_exiting); drawthings(); show_gameover(GAMEOVER_TOTALTIME); popmatrix(); break; case STATE_HELP: draw_helpscreen(); break; } } if (insetup) { draw_setup(); } } static void tostowship(SHIP *s) { s->dispmethod = SD_TOSTOW; s->obj.movetype = OMT_DST; s->obj.d.y = STOW_Y; s->obj.d.a = 90 DEGREES; switch (stowstate) { case STOW_APART: case STOW_SPLITTING: s->obj.d.x = (STOW_X - ((STOW_APART_N-1)*STOW_X_INC*.5)) + (STOW_X_INC * nstowed); break; case STOW_TOGETHER: case STOW_MERGING: s->obj.d.x = STOW_X; break; } s->obj.d.vxy = STOW_TO_VEL; s->obj.d.va = 1; } static void stowcheck(void) { int i; int j; SHIP *s; if (nstowed > STOW_APART_N) { for (i=0;idispmethod == SD_STOWED) || (s->dispmethod == SD_STOWMOVE)) { s->dispmethod = SD_STOWMOVE; s->obj.movetype = OMT_DST; s->obj.d.x = STOW_X; s->obj.d.y = STOW_Y; s->obj.d.a = 90 DEGREES; s->obj.d.vxy = STOW_VEL; s->obj.d.va = 1; } } nstowmove = nstowed; stowstate = STOW_MERGING; } else { j = 0; for (i=0;idispmethod == SD_STOWED) || (s->dispmethod == SD_STOWMOVE)) { s->dispmethod = SD_STOWMOVE; s->obj.movetype = OMT_DST; s->obj.d.x = (STOW_X - ((STOW_APART_N-1)*STOW_X_INC*.5)) + (STOW_X_INC * j); s->obj.d.y = STOW_Y; s->obj.d.a = 90 DEGREES; s->obj.d.vxy = STOW_VEL; s->obj.d.va = 1; j ++; } } nstowmove = nstowed; stowstate = STOW_SPLITTING; } for (i=0;idispmethod == SD_TOSTOW) tostowship(ships[i]); } static void gainship(float x, float y) { SHIP *s; getshiproom(nships+1); s = ships[nships]; nships ++; s->obj.x = x; s->obj.y = y; s->obj.a = 90 DEGREES; tostowship(s); } static int loseship(void) /* return true if no more ships */ { SHIP *s; if (nships < 1) { playship = 0; return(1); } if (playship) { if (playship != ships[nships-1]) { int i; for (i=nships-2;i>=0;i--) if (playship == ships[i]) break; if (i < 0) { fprintf(stderr,"can't find playship in ships array"); abort(); } s = ships[i]; ships[i] = ships[nships-1]; ships[nships-1] = s; } nships --; playship = 0; } if (nships < 1) return(1); sortstow(); s = (nstowed > 0) ? ships[nstowed-1] : ships[0]; nstowed --; if (s->dispmethod == SD_STOWMOVE) nstowmove --; s->dispmethod = SD_FROMSTOW; s->obj.movetype = OMT_DST; s->obj.d.x = XMAXSCREEN / 2; s->obj.d.y = YMAXSCREEN / 2; s->obj.d.a = 90 DEGREES; s->obj.d.vxy = STOW_FROM_VEL; s->obj.d.va = 1; playship = s; stowcheck(); return(0); } static void set_jet_sounds(int bits) { sound_repeat(SOUND_THRUST,(bits&jets[JET_FWD].bit)?1:0); sound_repeat(SOUND_TURN,(bits&(jets[JET_LEFT].bit|jets[JET_RIGHT].bit))?1:0); } static void clearjets(void) { int i; jetbits = 0; for (i=0;idispmethod) { case SD_PLAYING: case SD_HYPER: break; default: playship->obj.x = XMAXSCREEN / 2; playship->obj.y = YMAXSCREEN / 2; playship->obj.a = 90 DEGREES; playship->obj.movetype = OMT_VEL; playship->obj.v.x = 0; playship->obj.v.y = 0; playship->obj.v.a = 0; switch (playship->dispmethod) { case SD_STOWED: nstowed --; break; case SD_STOWMOVE: nstowmove --; break; } playship->dispmethod = SD_PLAYING; break; } clearjets(); } static void explode_segs(OBJ *obj, SEG *segs, int nsegs) { int i; SEG *ss; float sa; float ca; /* printf("explode_segs: obj %x segs %x nsegs %d\n",(int)obj,(int)segs,nsegs); printf("explode_segs: obj has pos %g %g %g vel %g %g %g\n",obj->x,obj->y,obj->a,obj->v.x,obj->v.y,obj->v.a); */ MOVECD(obj); sa = sin(obj->a); ca = cos(obj->a); for (i=0;ilength = hypot(ss->init_x1-ss->init_x2,ss->init_y1-ss->init_y2); ss->obj.x = obj->x + ((ca*(ss->init_x1+ss->init_x2))/2) - ((sa*(ss->init_y1+ss->init_y2))/2); ss->obj.y = obj->y + ((sa*(ss->init_x1+ss->init_x2))/2) + ((ca*(ss->init_y1+ss->init_y2))/2); ss->obj.a = obj->a + atan2(ss->init_y1-ss->init_y2,ss->init_x1-ss->init_x2); ss->obj.movetype = OMT_VEL; ss->obj.v.x = obj->v.x + ((frndc()-.5) * 2 * EXPLODE_SEG_VELXY); ss->obj.v.y = obj->v.y + ((frndc()-.5) * 2 * EXPLODE_SEG_VELXY); ss->obj.v.a = obj->v.a + ((frndc()-.5) * 2 * EXPLODE_SEG_VELA); /* printf("explode_segs: seg %x gets pos %g %g %g vel %g %g %g\n",(int)ss,ss->obj.x,ss->obj.y,ss->obj.a,ss->obj.v.x,ss->obj.v.y,ss->obj.v.a); */ } } static void setrecenter(void) { playship->dispmethod = SD_RECENTER; playship->obj.movetype = OMT_DST; playship->obj.d.x = XMAXSCREEN / 2; playship->obj.d.y = YMAXSCREEN / 2; playship->obj.d.a = 90 DEGREES; playship->obj.d.vxy = RECENTER_VEL; playship->obj.d.va = RECENTER_AVEL; } static void set_countspeed(void) { if (bonusleft < 0) bonusleft = 0; countspeed = bonusleft / (5*TICKS_PER_SEC); if (countspeed < 1) countspeed = 1; } static void enterstate(int newstate) { wantdraw = 1; if (newstate != STATE_EXITING) { switch (STATEPAIR(state,newstate)) { case STATEPAIR(STATE_WAITBEGIN,STATE_PLAYING): initlevel(); playship_playable(); break; case STATEPAIR(STATE_WAITBEGIN,STATE_SHOWSCORE): checkhsfile = 1; hschange = 0; break; case STATEPAIR(STATE_WAITBEGIN,STATE_HELP): break; case STATEPAIR(STATE_PLAYING,STATE_ENDLEVEL): set_countspeed(); setrecenter(); clearshots(1); set_jet_sounds(0); break; case STATEPAIR(STATE_PLAYING,STATE_EXPLOSION): explode_segs(&playship->obj,&shipsegs[0],ARRAYSIZE(shipsegs)); playship->dispmethod = SD_NONE; sound_once(SOUND_SHIPDIE); set_jet_sounds(0); break; case STATEPAIR(STATE_ENDLEVEL,STATE_WAITBEGIN): sound_repeat(SOUND_BONUSCOUNT,0); if (! playship) loseship(); level ++; break; case STATEPAIR(STATE_ENDLEVEL,STATE_GAMEOVER): sound_repeat(SOUND_BONUSCOUNT,0); level ++; break; case STATEPAIR(STATE_EXPLOSION,STATE_ENDLEVEL): set_countspeed(); sound_repeat(SOUND_BONUSCOUNT,1); break; case STATEPAIR(STATE_EXPLOSION,STATE_RESUME): break; case STATEPAIR(STATE_EXPLOSION,STATE_GAMEOVER): sound_repeat(SOUND_ALERT,0); break; case STATEPAIR(STATE_RESUME,STATE_PLAYING): playship_playable(); invulnerable &= ~INVUL_RESUME; break; case STATEPAIR(STATE_GAMEOVER,STATE_SCOREWAIT): break; case STATEPAIR(STATE_SCOREWAIT,STATE_SHOWSCORE): checkhsfile = 1; hschange = 0; break; case STATEPAIR(STATE_SCOREWAIT,STATE_NEWSCORE): scoreletter = 0; sound_once(SOUND_LIST); break; case STATEPAIR(STATE_SHOWSCORE,STATE_WAITBEGIN): realtime = 0; score = 0; clearshots(0); level = 1; initnships(2); playship = 0; loseship(); if (! iswiz) { if (insetup) setup_any_return(0,0); suppress_display = 0; invulnerable = 0; sinvulnerable = 0; saucer_shoot_override = SSO_NONE; setup_link_alerts = 0; showstow = 0; recoil = 500; } nextfreeship = FIRSTFREESHIP; saucerstate = SAUCER_NONE; break; case STATEPAIR(STATE_SHOWSCORE,STATE_HELP): break; case STATEPAIR(STATE_NEWSCORE,STATE_SHOWSCORE): highscore_write(); highscore_unlock(); checkhsfile = 1; hschange = 0; sound_once(SOUND_LIST); break; case STATEPAIR(STATE_HELP,STATE_SHOWSCORE): checkhsfile = 1; hschange = 0; break; case STATEPAIR(STATE_EXITING,STATE_SHOWSCORE): checkhsfile = 1; hschange = 0; break; default: fprintf(stderr,"bad transition in enterstate: %d to %d\n",state,newstate); abort(); break; } } state = newstate; stateage = 0; wantdraw = 1; } static void do_tickhist(int *h, int hsz, int *hh, int *hsum, int pend) { *hsum -= h[*hh]; h[*hh] = pend; *hsum += pend; *hh = (*hh + 1) % hsz; } static void doticks(void) { int i; if (skipticks) { tickspending[0] = 0; tickspending[1] = 0; skipticks = 0; return; } if (tickrate[0] > 1) { do_tickhist(&tickhist_l[0],TICKHISTSIZE_L,&tickhhead_l,&tickhsum_l,tickspending[1]); if ((tickhsum_l / (float)TICKHISTSIZE_L) < tickrate[0]-1) { #ifdef RATE_DEBUG printf("%d:%02d: average timer1=%g less than tickrate[0]=%d-1, lowering\n",realtime/60,realtime%60,tickhsum_l/(float)TICKHISTSIZE_L,tickrate[0]); #endif tickrate[0] --; noise(TIMER0,tickrate[0]); if (tickrate[0] > 1) { tickrate[1] = tickrate[0] - 1; noise(TIMER1,tickrate[1]); } else { unqdevice(TIMER1); } } } tickspending[1] = 0; do_tickhist(&tickhist_s[0],TICKHISTSIZE_S,&tickhhead_s,&tickhsum_s,tickspending[0]); if ((tickhsum_s / (float)TICKHISTSIZE_S) > tickrate[0]) { #ifdef RATE_DEBUG printf("%d:%02d: average timer0=%g > tickrate[0]=%d, raising\n",realtime/60,realtime%60,tickhsum_s/(float)TICKHISTSIZE_S,tickrate[0]); #endif tickrate[0] ++; tickrate[1] = tickrate[0] - 1; for (i=0;i 0)) { wantdraw = 0; dodraw = 1; } tickspending[0] = 0; } static void doescape(void) { switch (state) { case STATE_EXITING: return; break; case STATE_SHOWSCORE: case STATE_HELP: break; default: if (noscore_one || noscore_all || !highscore_lock()) break; /* fall through */ case STATE_NEWSCORE: highscore_read(); if (highscore_insert()) { int i; for (i=0;i<3;i++) scorelist[scoreslot].name[i] = rnd(27); highscore_write(); sound_once(SOUND_LIST); } highscore_unlock(); break; } enterstate(STATE_EXITING); if (insetup) setup_any_return(0,0); suppress_display = 0; skipticks = 1; } static void fire(void) { SHOT *s; s = newshot(); linkshot(s); ourshots ++; s->ours = 1; s->age = 0; s->obj = playship->obj; s->obj.a = 0; /* s->obj.x += cos(ship.obj.a) * SHIPSIZE; s->obj.y += sin(ship.obj.a) * SHIPSIZE; */ s->obj.movetype = OMT_VEL; s->obj.v.x += cos(playship->obj.a) * SHOT_SPEED; s->obj.v.y += sin(playship->obj.a) * SHOT_SPEED; s->obj.v.a = 0; if (recoil) { playship->obj.v.x -= cos(playship->obj.a) * recoil * RECOIL; playship->obj.v.y -= sin(playship->obj.a) * recoil * RECOIL; } sound_once(SOUND_GUN); } static void hyperspace_start(void) { invulnerable |= INVUL_HYPER; hypertime = HYPER_TIME; playship->dispmethod = SD_HYPER; sound_once(SOUND_HYPER); clearjets(); } static void hyperspace_mid(void) { playship->obj.x = rnd(XMAXSCREEN); playship->obj.y = rnd(YMAXSCREEN); playship->obj.movetype = OMT_VEL; playship->obj.v.x = 0; playship->obj.v.y = 0; playship->obj.v.a = 0; } static void hyperspace_end(void) { invulnerable &= ~INVUL_HYPER; playship->dispmethod = SD_PLAYING; } static void adjletter(int inc) { int let; let = scorelist[scoreslot].name[scoreletter]; let += inc; if (let < 0) let += scorenameveclen; else if (let >= scorenameveclen) let -= scorenameveclen; scorelist[scoreslot].name[scoreletter] = let; sound_once(SOUND_LETTER); } static void nextletter(void) { if (scorenamevec[scorelist[scoreslot].name[scoreletter]] == '\177') { scorelist[scoreslot].name[scoreletter] = 0; if (scoreletter > 0) scoreletter --; } else { scoreletter ++; if (scoreletter > 2) enterstate(STATE_SHOWSCORE); } sound_once(SOUND_LETTER); } static void setjet(int j) { jetbits |= jets[j].bit; set_jet_sounds(jetbits); } static void clrjet(int j) { jetbits &= ~jets[j].bit; set_jet_sounds(jetbits); } static void hss_step(void) { scorescroll += hssinc - hssdec; limit_scorescroll(); hsstate = HSS_SCROLL; hschange = stateage + SHOWSCORE_INITIALTIME; } static void controlbutton(long int dev, short int val) { switch (state) { case STATE_WAITBEGIN: if (val && (dev == GUN)) enterstate(STATE_PLAYING); break; case STATE_PLAYING: switch (dev) { case TURNLEFT: if (val) setjet(JET_LEFT); else clrjet(JET_LEFT); break; case TURNRIGHT: if (val) setjet(JET_RIGHT); else clrjet(JET_RIGHT); break; case GUN: if (val) fire(); break; case ROCKET: if (val) setjet(JET_FWD); else clrjet(JET_FWD); break; case HYPERSPACE: if (val) hyperspace_start(); break; } break; case STATE_RESUME: switch (dev) { case TURNLEFT: playship_playable(); if (val) setjet(JET_LEFT); else clrjet(JET_LEFT); break; case TURNRIGHT: playship_playable(); if (val) setjet(JET_RIGHT); else clrjet(JET_RIGHT); break; case GUN: playship_playable(); if (val) enterstate(STATE_PLAYING); break; case ROCKET: playship_playable(); if (val) setjet(JET_FWD); else clrjet(JET_FWD); break; case HYPERSPACE: playship_playable(); if (val) { enterstate(STATE_PLAYING); hyperspace_start(); } break; } break; case STATE_NEWSCORE: if (val) { switch (dev) { case TURNLEFT: adjletter(-1); letterrep = -1; reptime = AUTOREPEAT_DELAY; break; case TURNRIGHT: adjletter(1); letterrep = 1; reptime = AUTOREPEAT_DELAY; break; case GUN: nextletter(); letterrep = 0; break; } } else { letterrep = 0; } break; case STATE_SHOWSCORE: switch (dev) { case TURNLEFT: if (val) hssdec = 1; else hssdec = 0; if (0) { case TURNRIGHT: if (val) hssinc = 1; else hssinc = 0; } switch (hsstate) { case HSS_SLOT: case HSS_TOPSLOT: scorescroll = scoreslot; limit_scorescroll(); break; case HSS_TOP: scorescroll = SCORELIST_TOPN + SCORELIST_SIMOFF; break; } if (! hssrep) hss_step(); break; case ROCKET: if (val) hssrep = 1; else hssrep = 0; break; } if (val && (dev == HYPERSPACE)) enterstate(STATE_WAITBEGIN); break; case STATE_HELP: if (val) { switch (dev) { case HYPERSPACE: enterstate(STATE_SHOWSCORE); break; case TURNLEFT: if (helpscreen > 0) { helpscreen --; wantdraw = 1; } break; case TURNRIGHT: if (helpscreen < nhelpscreens-1) { helpscreen ++; wantdraw = 1; } break; } } break; default: /* all other states ignore all buttons */ break; } } static void addscoreobj(int points, float x, float y) { SCOREOBJ *so; so = malloc(sizeof(SCOREOBJ)); sprintf(&so->txt[0],"%d",points); so->ageleft = SCOREOBJ_EXPIRE; so->obj.x = x; so->obj.y = y; so->obj.a = 0; so->obj.movetype = OMT_VEL; so->obj.v.x = 0; so->obj.v.y = 0; so->obj.v.a = 0; so->link = scoreobj; scoreobj = so; } static void incscore(int amt, float x, float y, int showit) { score += amt; if (score > nextfreeship) { if (nextfreeship < FREESHIPLINEAR) { nextfreeship *= FREESHIPMUL; } else { nextfreeship += FREESHIPADD; } gainship(x,y); sound_once(SOUND_FREESHIP); } if (showit) addscoreobj(amt,x,y); } static void poofrock(ROCK *r, ROCK **rp, int addscore) { int nr; ROCK *rs[5]; int i; float a; switch (r->sizex) { case ROCK_SMALL: if (addscore) incscore(SCORE_ROCK_SMALL,r->obj.x,r->obj.y,do_rockscore); add_debris(6,12,2*DEBRIS_DECAY,&r->obj,r->psize,.5*SPIN_MAX DEGREES); nr = 0; break; case ROCK_MEDIUM: if (addscore) incscore(SCORE_ROCK_MEDIUM,r->obj.x,r->obj.y,do_rockscore); add_debris(10,20,3*DEBRIS_DECAY,&r->obj,r->psize,.5*SPIN_MAX DEGREES); r->sizex = ROCK_SMALL; r->psize = ROCKSIZE_SMALL; nr = rnd(3) + 3; break; case ROCK_BIG: if (addscore) incscore(SCORE_ROCK_BIG,r->obj.x,r->obj.y,do_rockscore); add_debris(15,30,4*DEBRIS_DECAY,&r->obj,r->psize,.5*SPIN_MAX DEGREES); r->sizex = ROCK_MEDIUM; r->psize = ROCKSIZE_MEDIUM; nr = 2; break; default: abort(); break; } *rp = r->link; for (i=0;isizex = r->sizex; rs[i]->psize = r->psize; rs[i]->obj = r->obj; } freerock(r); a = frndo() * 360 DEGREES; for (i=0;ishape = rnd(NROCKSHAPES); r->shapeinv = random() & 1; r->obj.v.x += (r->obj.v.a * cos(a+(frndo()*ROCKPOOF_AFUZZ)) * ROCKPOOF_ATOXY) + ((frndc() - .5) * 2 * ROCKPOOF_VELXY); r->obj.v.y += (r->obj.v.a * sin(a+(frndo()*ROCKPOOF_AFUZZ)) * ROCKPOOF_ATOXY) + ((frndc() - .5) * 2 * ROCKPOOF_VELXY); r->obj.v.a += (frndc() - .5) * 2 * ROCKPOOF_VELA; } sound_once(SOUND_POOFROCK); } static int checkclear(void) { return( (rocks == 0) && ((saucerstate == SAUCER_NONE) || (saucerstate == SAUCER_DONT)) && (othershots == 0) && (hypertime == 0) ); } static void explodesaucer(SAUCER *s, int addscore) { if (addscore) incscore(s->value,s->obj.x,s->obj.y,1); add_debris(20,30,4*DEBRIS_DECAY,&s->obj,s->size,0); saucerstate = SAUCER_DEAD; deadsaucersegs = s->smart ? &saucersmartsegs[0] : &saucerdumbsegs[0]; deadsaucernsegs = s->smart ? ARRAYSIZE(saucersmartsegs) : ARRAYSIZE(saucerdumbsegs); explode_segs(&s->obj,deadsaucersegs,deadsaucernsegs); saucerdeadtime = 0; sound_once(SOUND_SAUCERDIE); sound_repeat(SOUND_ALERT,0); } static int objtouch(OBJ *o1, OBJ *o2, float d) { float dx; float dy; dx = o1->x - o2->x; dy = o1->y - o2->y; if ((fabs(dx) > d) || (fabs(dy) > d) || (hypot(dx,dy) > d)) return(0); return(1); } static int tick_obj(OBJ *o) { MOVECD(o); o->x += o->v.x; o->y += o->v.y; o->a += o->v.a; if (o->x < 0) o->x += XMAXSCREEN; else if (o->x > XMAXSCREEN) o->x -= XMAXSCREEN; if (o->y < 0) o->y += YMAXSCREEN; else if (o->y > YMAXSCREEN) o->y -= YMAXSCREEN; if (o->a < 0) o->a += 2*M_PI; else if (o->a > 2*M_PI) o->a -= 2*M_PI; if ((o->movetype == OMT_C_D) && (--o->movesleft < 1)) { o->x = o->d.x; o->y = o->d.y; o->a = o->d.a; o->v.x = 0; o->v.y = 0; o->v.a = 0; return(1); } return(0); } static void tickscoreobjs(void) { SCOREOBJ *so; SCOREOBJ **sop; for (sop=&scoreobj;(so=*sop);) { so->ageleft --; if (so->ageleft < 1) { *sop = so->link; free(so); } else { tick_obj(&so->obj); sop = &so->link; } } } static void tickrocks(void) { ROCK *r; for (r=rocks;r;r=r->link) { tick_obj(&r->obj); } } static void tickshots(void) { SHOT *s; SHOT **sp; int nours; int nenemy; nours = 0; nenemy = 0; for (sp=&shots;(s=*sp);) { s->age ++; if (s->age > SHOT_MAXAGE) { *sp = s->link; add_shot_debris(s); if (s->ours) ourshots --; else othershots --; freeshot(s); sound_once(SOUND_SHOTDIE); } else { tick_obj(&s->obj); sp = &s->link; if (s->ours) nours ++; else nenemy ++; } } if (nours != ourshots) { printf("nours %d ourshots %d\n",nours,ourshots); ourshots = nours; } if (nenemy != othershots) { printf("nenemy %d othershots %d\n",nenemy,othershots); othershots = nenemy; } if (nours > SHOT_MAXOURS) { SHOT *save[SHOT_MAXOURS]; int nsave; int maxage; int i; int oldest; nsave = 0; maxage = 0; for (s=shots;s;s=s->link) { if (s->ours) { if (nsave < SHOT_MAXOURS) { i = nsave++; } else { maxage = 0; oldest = 0; /* shut up -Wuninitialized */ for (i=0;iage > maxage) { maxage = save[i]->age; oldest = i; } } i = (s->age < maxage) ? oldest : SHOT_MAXOURS; } if (i < SHOT_MAXOURS) { save[i] = s; } } } for (sp=&shots;(s=*sp);) { if (s->ours) { for (i=0;i= SHOT_MAXOURS) { *sp = s->link; add_shot_debris(s); ourshots --; freeshot(s); continue; } } sp = &s->link; } sound_once(SOUND_SHOTDIE); } } static void shootrocks(void) { ROCK *r; ROCK **rp; SHOT *s; SHOT **sp; for (rp=&rocks;(r=*rp);) { for (sp=&shots;(s=*sp);) { if (objtouch(&s->obj,&r->obj,r->psize)) { *sp = s->link; if (s->ours) ourshots --; else othershots --; poofrock(r,rp,s->ours); freeshot(s); goto nextrock; } sp = &s->link; } rp = &r->link; nextrock:; } } static int amIdead(void) { int rv; ROCK *r; ROCK **rp; SHOT *s; SHOT **sp; rv = 0; if (! invulnerable) { for (rp=&rocks;(r=*rp);) { if (objtouch(&r->obj,&playship->obj,r->psize+SHIPSIZE)) { poofrock(r,rp,1); rv = 1; break; } rp = &r->link; } if (othershots > 0) { for (sp=&shots;(s=*sp);) { if (!s->ours && objtouch(&playship->obj,&s->obj,(float)SHIPSIZE)) { *sp = s->link; othershots --; freeshot(s); rv = 1; break; } sp = &s->link; } } if ((saucerstate == SAUCER_HAVE) && !sinvulnerable) { if (objtouch(&saucer.obj,&playship->obj,(float)(saucer.size+SHIPSIZE))) { explodesaucer(&saucer,1); rv = 1; } } } return(rv); } static void killsaucer(void) { SHOT *s; SHOT **sp; ROCK *r; ROCK **rp; if ((saucerstate == SAUCER_HAVE) && !sinvulnerable) { if (ourshots > 0) { for (sp=&shots;(s=*sp);) { if (s->ours && objtouch(&saucer.obj,&s->obj,(float)saucer.size)) { *sp = s->link; ourshots --; explodesaucer(&saucer,1); freeshot(s); break; } sp = &s->link; } } for (rp=&rocks;(r=*rp);) { if (objtouch(&r->obj,&saucer.obj,r->psize+saucer.size)) { if (setup_link_alerts) { /* printf("saucer killed by rock %ld at %d\n",(long int)&r->obj,realtime); */ } poofrock(r,rp,0); explodesaucer(&saucer,0); break; } rp = &r->link; } } } static void setsaucerv(SAUCER *s) { s->obj.movetype = OMT_VEL; s->obj.v.x = cos(s->movea) * SAUCER_VEL; s->obj.v.y = sin(s->movea) * SAUCER_VEL; if (! rnd(SAUCER_REVERSE_CHANCE)) s->obj.v.a = - s->obj.v.a; s->movetimer = SAUCER_MOVETIMER_MIN + rnd(SAUCER_MOVETIMER_MAX+1-SAUCER_MOVETIMER_MIN); } static void makesaucer(int smart) { saucer.age = 0; saucer.smart = smart; saucer.size = SAUCERSIZE; switch (random() & 3) { case 0: saucer.obj.x = 0; saucer.obj.y = YMAXSCREEN * SAUCER_Y_FACTOR; break; case 1: saucer.obj.x = 0; saucer.obj.y = YMAXSCREEN * (1-SAUCER_Y_FACTOR); break; case 2: saucer.obj.x = XMAXSCREEN; saucer.obj.y = YMAXSCREEN * SAUCER_Y_FACTOR; break; case 3: saucer.obj.x = XMAXSCREEN; saucer.obj.y = YMAXSCREEN * (1-SAUCER_Y_FACTOR); break; } saucer.obj.a = 0; if (saucer.obj.x == 0) { saucer.movea = (frndc() - .5) * 180 DEGREES; } else { saucer.movea = (frndc() + .5) * 180 DEGREES; } saucer.obj.v.a = ((random() & 1) ? -1 : 1) * (smart ? SAUCER_SMART_AVEL : SAUCER_DUMB_AVEL); setsaucerv(&saucer); saucerstate = SAUCER_HAVE; saucer.shoottimer = SAUCER_SHOOT_DELAY; saucer.value = smart ? SCORE_SAUCER_SMART : SCORE_SAUCER_DUMB; } static void saucer_deflect(SAUCER *s) { s->movea += (frndc() - .5) * 2 * SAUCER_DEFLECT_AINC; if (s->movea < 0) s->movea += 2 * M_PI; else if (s->movea > 2*M_PI) s->movea -= 2 * M_PI; setsaucerv(s); } #define OK 0 #define NOSQRT 1 #define NEGATIVE 2 #define TOOBIG 3 static int solve_lead(float rx, float ry, float vx, float vy, float *tp) { float a; float b; float c; float d; float t1; float t2; a = (vx * vx) + (vy * vy) - (SHOT_SPEED * SHOT_SPEED); b = - 2 * ((rx * vx) + (ry * vy)); c = (rx * rx) + (ry * ry); d = (b * b) - (4 * a * c); if (d < 0) { return(NOSQRT); } else { d = sqrt(d); t1 = (-b + d) / (2 * a); t2 = (-b - d) / (2 * a); if (t1 < 0) { if (t2 < 0) return(NEGATIVE); *tp = t2; return((t2 0) && (t2 < t1)) t1 = t2; *tp = t1; return((t1v.x - to->v.x; vy = from->v.y - to->v.y; } else { vx = from->v.x; vy = from->v.y; } xwfwd = (vx > 0) ? XMAXSCREEN : -XMAXSCREEN; ywfwd = (vy > 0) ? YMAXSCREEN : -YMAXSCREEN; if (angle_to_debug) printf("angle_to: from (%g,%g) vel (%g,%g) to (%g,%g) vel (%g,%g)\n",from->x,from->y,from->v.x,from->v.y,to->x,to->y,to->v.x,to->v.y); if (angle_to_debug) printf("angle_to: smart %d, using relative vel (%g,%g)\n",smart,vx,vy); rx = to->x - from->x; ry = to->y - from->y; bestt = SHOT_MAXAGE + 1; besta = frndo() * 360 DEGREES; rh2max = (SHOT_MAXAGE * (hypot(vx,vy) + SHOT_SPEED) * 1.05); /* 1.05 is a fudge factor */ rh2max *= rh2max; rx += XMAXSCREEN; ry += YMAXSCREEN; for (i=0;i<3;i++) { for (j=0;j<3;j++) { if ((solve_lead(rx,ry,vx,vy,&t) == OK) && (t < bestt)) { bestt = t; besta = LEAD_ANGLE(rx,ry,vx,vy,t); } rx -= XMAXSCREEN; } ry -= YMAXSCREEN; rx += 3 * XMAXSCREEN; } if (smart) { rx -= XMAXSCREEN; ry += 2 * YMAXSCREEN; while (1) { ab_x = ((rx-xwfwd) * vy) + (ry * vx); ab_y = (rx * vy) + ((ry-ywfwd) * vx); if (fabs(ab_x) < fabs(ab_y)) rx -= xwfwd; else ry -= ywfwd; rh2 = (rx * rx) + (ry * ry); if (rh2 >= rh2max) break; if ((solve_lead(rx,ry,vx,vy,&t) == OK) && (t < bestt)) { bestt = t; besta = LEAD_ANGLE(rx,ry,vx,vy,t); } } } return(besta); } static void setsaucershoot(SAUCER *s) { s->shoottimer = s->smart ? rnd(SAUCER_SMART_SHOOT_INTVLVAR) + SAUCER_SMART_SHOOT_MININTVL : rnd(SAUCER_DUMB_SHOOT_INTVLVAR) + SAUCER_DUMB_SHOOT_MININTVL ; if (s->canshoot < 0) s->canshoot = SAUCER_SHOTINTVL; else s->canshoot += SAUCER_SHOTINTVL; } static void saucer_shoot(SAUCER *s) { SHOT *sh; float a; int st; sh = newshot(); linkshot(sh); othershots ++; sh->ours = 0; sh->age = 0; sh->obj = s->obj; sh->obj.a = 0; if (saucer_shoot_override != SSO_NONE) { st = saucer_shoot_override; } else { static int random_tbl[] = { SSO_RANDOM, SSO_PLAYER, SSO_ROCK, SSO_PLAYER|SSO_LEAD, SSO_PLAYER|SSO_LEAD, SSO_ROCK|SSO_LEAD, SSO_ROCK|SSO_LEAD }; st = random_tbl[rnd(s->smart?7:3)]; } /* case values must match arguments in saucer shot menu and drawsaucer() */ switch (st) { case SSO_RANDOM: a = frndo() * 360 DEGREES; break; case SSO_PLAYER: if (playship) { a = angle_to(&s->obj,&playship->obj,0); } else { a = frndo() * 360 DEGREES; } break; case SSO_ROCK: if (nrocks > 0) { a = angle_to(&s->obj,&randomrock()->obj,0); } else { a = frndo() * 360 DEGREES; } break; case SSO_PLAYER|SSO_LEAD: if (playship) { a = angle_to(&s->obj,&playship->obj,1); } else { a = frndo() * 360 DEGREES; } break; case SSO_ROCK|SSO_LEAD: if (nrocks > 0) { a = angle_to(&s->obj,&randomrock()->obj,1); } else { a = frndo() * 360 DEGREES; } break; default: fprintf(stderr,"bad saucer shoot type %d\n",st); abort(); break; } /* sh->obj.x += cos(a) * s->size; sh->obj.y += sin(a) * s->size; */ sh->obj.movetype = OMT_VEL; sh->obj.v.x += cos(a) * SHOT_SPEED; sh->obj.v.y += sin(a) * SHOT_SPEED; sh->obj.v.a = 0; setsaucershoot(s); sound_once(SOUND_GUN); s->value ++; } static void saucer_shoot_alert(SAUCER *s, ROCK *r) { SHOT *sh; float a; int n; switch (r->sizex) { case ROCK_BIG: n = 5; break; case ROCK_MEDIUM: n = 3; break; case ROCK_SMALL: n = 1; break; default: abort(); } while (n-- > 0) /* decrement before body of loop! */ { sh = newshot(); linkshot(sh); othershots ++; sh->ours = 0; sh->age = 0; sh->obj = s->obj; sh->obj.a = 0; a = angle_to(&s->obj,&r->obj,1); /* sh->obj.x += cos(a) * s->size; sh->obj.y += sin(a) * s->size; */ sh->obj.movetype = OMT_VEL; sh->obj.v.x += (cos(a) * SHOT_SPEED); sh->obj.v.y += (sin(a) * SHOT_SPEED); sh->obj.v.a = 0; sh->obj.x -= n * sh->obj.v.x; sh->obj.y -= n * sh->obj.v.y; setsaucershoot(s); s->value ++; sound_once(SOUND_GUN); } } static ROCK *saucer_collision_alert(SAUCER *s) { ROCK *r; ROCK *rvr; int nfound; int rocksleft; int checkleft; rvr = 0; nfound = 0; if (nrocks <= SAUCER_MAXCHKROCKS) { for (r=rocks;r;r=r->link) { if (saucer_collision_danger(s,&r->obj,r->psize)) { nfound ++; if ((nfound < 2) || !rnd(nfound)) { rvr = r; } } } } else { checkleft = SAUCER_MAXCHKROCKS; rocksleft = nrocks; for (r=rocks;r&&checkleft;r=r->link) { if (rnd(rocksleft) < checkleft) { if (saucer_collision_danger(s,&r->obj,r->psize)) { nfound ++; if ((nfound < 2) || !rnd(nfound)) { rvr = r; } } checkleft --; } rocksleft --; } } return(rvr); } static void saucer_tick(int cancreate) { int i; switch (saucerstate) { case SAUCER_NONE: if (cancreate && ((levelage % MAKESAUCER_CHECK) == 0)) { if (level >= MIN_SAUCER_LEVEL) { int t; int st; t = RANGEMAP(level,MIN_SAUCER_LEVEL,SAUCER_TIME0_LEVEL,SAUCER_LEVEL0_TIME,0) - saucertimer; st = RANGEMAP(level,MIN_SAUCER_LEVEL,SAUCER_SMART_TIME0_LEVEL,SAUCER_SMART_LEVEL0_TIME,0); if (t < -st) { if (rnd(SAUCER_SURETIME_SMART) < -st-t) { makesaucer(1); } } if ((t < 0) && (saucerstate == SAUCER_NONE)) { if (rnd(SAUCER_SURETIME_DUMB) < -t) { makesaucer(0); } } } } break; case SAUCER_HAVE: tick_obj(&saucer.obj); saucer.movetimer --; saucer.canshoot --; if (saucer.movetimer < 0) saucer_deflect(&saucer); if ( (saucer.canshoot <= 0) && ( (saucercollforce > 0) || ( (saucercollforce == 0) && saucer.smart && ((state == STATE_PLAYING) || !rnd(15)) ) ) ) { ROCK *alertrock; alertrock = saucer_collision_alert(&saucer); if (alertrock) { saucer_shoot_alert(&saucer,alertrock); saucer_deflect(&saucer); } } saucer.shoottimer --; if ((saucer.shoottimer < 0) && (saucer.canshoot <= 0)) saucer_shoot(&saucer); break; case SAUCER_DEAD: for (i=deadsaucernsegs-1;i>=0;i--) tick_obj(&deadsaucersegs[i].obj); saucerdeadtime ++; if (saucerdeadtime > SAUCER_DEAD_TIMEOUT) saucerstate = SAUCER_NONE; break; case SAUCER_DONT: break; } } static void tickships(void) { int i; SHIP *s; for (i=nships-1;i>=0;i--) { s = ships[i]; top:; switch (s->dispmethod) { case SD_RECENTER: case SD_FROMSTOW: wantdraw = 1; if (tick_obj(&s->obj)) { s->dispmethod = SD_CENTER; goto top; } break; case SD_TOSTOW: wantdraw = 1; if (tick_obj(&s->obj)) { s->dispmethod = SD_STOWED; nstowed ++; stowcheck(); } break; case SD_STOWMOVE: wantdraw = 1; if (tick_obj(&s->obj)) { s->dispmethod = SD_STOWED; nstowmove --; if (nstowmove == 0) { switch (stowstate) { case STOW_MERGING: stowstate = STOW_TOGETHER; break; case STOW_SPLITTING: stowstate = STOW_APART; break; } } } break; case SD_CENTER: s->obj.x = XMAXSCREEN / 2; s->obj.y = YMAXSCREEN / 2; s->obj.a = 90 DEGREES; s->obj.movetype = OMT_VEL; s->obj.v.x = 0; s->obj.v.y = 0; s->obj.v.a = 0; break; case SD_PLAYING: wantdraw = 1; tick_obj(&s->obj); break; case SD_HYPER: hypertime --; if (hypertime == (HYPER_TIME/2)) { hyperspace_mid(); } else if (hypertime == 0) { hyperspace_end(); } break; } } } static void tickdebris(void) { DEBRIS **dp; DEBRIS *d; dp = &debris; while ((d = *dp)) { if (d->ageleft < 1) { *dp = d->link; freedebris(d); } else { dp = &d->link; d->ageleft --; tick_obj(&d->obj); } } } static void tickshipsegs(void) { int i; for (i=0;ibit) { if (j->ramp < j->rampmax) j->ramp ++; } else { if (j->ramp > 0) j->ramp --; } } playship->obj.v.a = TURN_RATE * ((jets[JET_LEFT].ramp/(double)jets[JET_LEFT].rampmax) - (jets[JET_RIGHT].ramp/(double)jets[JET_RIGHT].rampmax)); if (jets[JET_FWD].ramp) { playship->obj.v.x += (cos(playship->obj.a) * THRUST_ACCEL * jets[JET_FWD].ramp) / jets[JET_FWD].rampmax; playship->obj.v.y += (sin(playship->obj.a) * THRUST_ACCEL * jets[JET_FWD].ramp) / jets[JET_FWD].rampmax; } } static void close_and_exit(void) { cpack(0x00000000); clear(); swapbuffers(); cursoff(); closegl(); exit(0); } static void realtick(void) { stateage ++; switch (state) { case STATE_WAITBEGIN: if (stateage > LEVELBEGIN_TIMEOUT) enterstate((level==1)?STATE_SHOWSCORE:STATE_PLAYING); tickships(); tickrocks(); tickscorelinec(); break; case STATE_PLAYING: wantdraw = 1; if (checkclear()) { enterstate(STATE_ENDLEVEL); break; } bonuscountdown += BONUS_COUNTDOWN; while (bonuscountdown >= TICKS_PER_SEC) { bonusleft --; bonuscountdown -= TICKS_PER_SEC; } levelage ++; if (saucerstate == SAUCER_NONE) saucertimer ++; tickscoreline(playship->obj.y); tickjets(); saucer_tick(nrocks>0); tickships(); tickdebris(); tickrocks(); tickshots(); shootrocks(); tickscoreobjs(); killsaucer(); if (amIdead()) { enterstate(STATE_EXPLOSION); sound_once(SOUND_SHIPDIE); break; } break; case STATE_ENDLEVEL: if (bonusleft > 0) { sound_repeat(SOUND_BONUSCOUNT,1); bonusleft -= countspeed; if (bonusleft < 0) { countspeed += bonusleft; bonusleft = 0; } incscore(countspeed,XMAXSCREEN/2,YMAXSCREEN/2,0); wantdraw = 1; } else { sound_repeat(SOUND_BONUSCOUNT,0); if (bonusleft > -END_BONUS_WAIT_TIME) { bonusleft --; } else { enterstate((nships<1)?STATE_GAMEOVER:STATE_WAITBEGIN); } } tickscorelinec(); tickdebris(); tickships(); tickscoreobjs(); break; case STATE_EXPLOSION: wantdraw = 1; if ((stateage > EXPLOSION_WAIT_TIME) && (ourshots == 0)) { if (rocks == 0) { loseship(); enterstate(STATE_ENDLEVEL); } else { enterstate(loseship()?STATE_GAMEOVER:STATE_RESUME); } } saucer_tick(0); tickscorelinec(); tickships(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); tickscoreobjs(); tickshipsegs(); break; case STATE_RESUME: wantdraw = 1; if (playship->dispmethod == SD_PLAYING) { invulnerable |= INVUL_RESUME; tickjets(); } saucer_tick(0); tickscorelinec(); tickships(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); tickscoreobjs(); if (stateage > RESUME_TIMEOUT) enterstate(STATE_PLAYING); break; case STATE_GAMEOVER: wantdraw = 1; if (stateage > 300) enterstate(STATE_SCOREWAIT); saucer_tick(0); tickscorelinec(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); tickscoreobjs(); break; case STATE_SCOREWAIT: wantdraw = 1; if (noscore_all || noscore_one) { enterstate(STATE_SHOWSCORE); } else { if ((stateage % 6) == 0) { if (highscore_lock()) { skipticks = 1; highscore_read(); if (highscore_insert()) { enterstate(STATE_NEWSCORE); } else { enterstate(STATE_SHOWSCORE); highscore_unlock(); } } } } tickscorelinec(); saucer_tick(0); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); tickscoreobjs(); break; case STATE_SHOWSCORE: wantdraw = 1; noscore_one = 0; invulnerable = 0; sinvulnerable = 0; if (saucerstate == SAUCER_DONT) saucerstate = SAUCER_NONE; if ((stateage % SHOWSCORE_CHECK) == 0) checkhsfile = 1; if (checkhsfile && ((stateage % 6) == 0) && highscore_lock()) { highscore_read(); if (highscore_expire()) highscore_write(); highscore_unlock(); checkhsfile = 0; } if (hssrep && (hssinc|hssdec) && ((stateage%2)==0)) hss_step(); if ((stateage % ATTRACT_ACTION_TIME) == 0) { ROCK *r; ROCK **rp; int nr; for (nr=0,r=rocks;r;nr++,r=r->link) ; if ((saucerstate == SAUCER_NONE) && !rnd(ATTRACT_SAUCER_CHANCE)) { makesaucer(0); } else if ((saucerstate == SAUCER_NONE) && (nr > 4) && !rnd(ATTRACT_SAUCER_XCHANCE)) { makesaucer(1); } else if ( (nr == 0) || (rnd(nr) < (((saucerstate==SAUCER_HAVE)&&saucer.smart)?3:1)) ) { newbigrock(); } else if (saucerstate == SAUCER_NONE) { int x; for (rp=&rocks,x=rnd(nr);(r=*rp)&&(x>0);x--,rp=&r->link) ; poofrock(r,rp,0); } } saucer_tick(0); tickscorelinec(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); break; case STATE_NEWSCORE: wantdraw = 1; if (letterrep) { reptime --; if (reptime < 0) { reptime = AUTOREPEAT_INTERVAL; adjletter(letterrep); } } if (stateage > SCORELIST_NEWTIMEOUT) { highscore_write(); highscore_unlock(); enterstate(STATE_SHOWSCORE); } saucer_tick(0); tickscorelinec(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); break; case STATE_EXITING: wantdraw = 1; if (stateage >= EXIT_TIME) close_and_exit(); saucer_tick(0); tickscorelinec(); tickdebris(); tickrocks(); tickshots(); shootrocks(); killsaucer(); tickscoreobjs(); break; case STATE_HELP: if (stateage >= HELP_TIMEOUT) enterstate(STATE_SHOWSCORE); break; } } static void tick(void) { setupage ++; if (insetup) { wantdraw = 1; return; } realtick(); } static void digitkey(long int dev, short int val) { int keyval; int i; if (! val) return; switch (dev) { case ZEROKEY: keyval = 0; break; case ONEKEY: keyval = 1; break; case TWOKEY: keyval = 2; break; case THREEKEY: keyval = 3; break; case FOURKEY: keyval = 4; break; case FIVEKEY: keyval = 5; break; case SIXKEY: keyval = 6; break; case SEVENKEY: keyval = 7; break; case EIGHTKEY: keyval = 8; break; case NINEKEY: keyval = 9; break; default: abort(); break; } check_cm_change(); for (i=0;inentries;i++) { if ((curmenu->entries[i].key & WIZ) && !iswiz) continue; if ((curmenu->entries[i].key & NONWIZ) && iswiz) continue; if ((curmenu->entries[i].key&KNMASK) == keyval) { (*curmenu->entries[i].fxn)(curmenu->entries[i].liarg,curmenu->entries[i].varg); wantdraw = 1; break; } } } static void keybd(short int val) { if (!insetup || (setup_str_op == 0)) { queuebuttons(); return; } if ((val == '\b') || (val == '\177')) { if (setup_str_fill > 0) { setup_str_fill --; setup_str[setup_str_fill] = '\0'; } } else if ((val == '\r') || (val == '\n')) { void (*f)(int); f = setup_str_op; setup_str_op = 0; queuebuttons(); (*f)(SSE_DONE); } else if ((val == '\33') || (val == '\7')) { void (*f)(int); f = setup_str_op; setup_str_op = 0; queuebuttons(); (*f)(SSE_ABORT); } else { if (setup_str_fill < ARRAYSIZE(setup_str)-1) { setup_str[setup_str_fill++] = val; setup_str[setup_str_fill] = '\0'; } } wantdraw = 1; } static void handle_event(long int dev, short int val) { if (insetup) { switch (dev) { case PIECECHANGE: wantdraw = 1; break; case REDRAW: wantdraw = 1; break; case ESCKEY: if (val) doescape(); break; case ZEROKEY: case ONEKEY: case TWOKEY: case THREEKEY: case FOURKEY: case FIVEKEY: case SIXKEY: case SEVENKEY: case EIGHTKEY: case NINEKEY: digitkey(dev,val); break; case KEYBD: keybd(val); break; case TIMER0: tickspending[0] += tickrate[0]; break; case TIMER1: tickspending[1] += tickrate[1]; break; case TIMER2: realtime ++; tick(); break; case HYPERSPACE: if (val) suppress_display ^= SUPPDIS_SETUP; break; default: /* ignore anything setup mode doesn't understand */ break; } } else { switch (dev) { case PIECECHANGE: wantdraw = 1; break; case REDRAW: wantdraw = 1; break; case TURNLEFT: case TURNRIGHT: case ROCKET: case GUN: case HYPERSPACE: controlbutton(dev,val); break; case ESCKEY: if (val) doescape(); break; case HKEY: if (val) { if ((state == STATE_SHOWSCORE) || ((state == STATE_WAITBEGIN) && (level == 1))) { enterstate(STATE_HELP); helpscreen = 0; seenlasthelp = 0; wantdraw = 1; } } break; case TIMER0: tickspending[0] += tickrate[0]; break; case TIMER1: tickspending[1] += tickrate[1]; break; case TIMER2: realtime ++; tick(); break; case INPUTCHANGE: /* why am I getting these? I didn't ask for them... */ break; case RIGHTMOUSE: /* ignore these; only purpose is to suppress menu */ break; case F9KEY: if (! insetup) { insetup = 1; wantdraw = 1; noscore_one = 1; lastcurmenu = curmenu; if (curmenu->enter) (*curmenu->enter)(); sound_pause(1); } break; case ZEROKEY: case ONEKEY: case TWOKEY: case THREEKEY: case FOURKEY: case FIVEKEY: case SIXKEY: case SEVENKEY: case EIGHTKEY: case NINEKEY: case KEYBD: /* ignore these; they're used only in setup mode */ break; default: fprintf(stderr,"got unexpected event %ld, val=%hd\n",dev,val); break; } } } static void qhandle(void) { do { short int val; long int dev; dev = qread(&val); handle_event(dev,val); } while (qtest()); } static void setup_getstr(void (*fxn)(int), const char *init) { setup_str_op = fxn; if (init != &setup_str[0]) strcpy(&setup_str[0],init); setup_str_fill = strlen(init); queuekeybd(); } static void setup_any_return(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { insetup = 0; if (curmenu->leave) (*curmenu->leave)(); suppress_display &= ~SUPPDIS_SETUP; sound_pause(0); } static void setup_any_newmenu(long int liarg __attribute__((__unused__)), void *varg) { curmenu = varg; } static void setnum_aux(int op) { const char *n; char oldstr[64]; switch (op) { case SSE_DONE: switch (set_vars[setup_setvar].type) { case SVT__INT: *set_vars[setup_setvar].p_int = atoi(&setup_str[0]); break; case SVT__FLOAT: *set_vars[setup_setvar].p_float = atof(&setup_str[0]); break; } break; case SSE_DRAW: n = set_vars[setup_setvar].name; font_leftstring("Enter new "); translate(10*ASPECT,0,0); font_leftstring(n); translate(strlen(n)*ASPECT,0,0); font_leftstring(" (was "); translate(6*ASPECT,0,0); switch (set_vars[setup_setvar].type) { case SVT__INT: sprintf(&oldstr[0],"%d",*set_vars[setup_setvar].p_int); break; case SVT__FLOAT: sprintf(&oldstr[0],"%g",*set_vars[setup_setvar].p_float); break; default: sprintf(&oldstr[0],"?type=%d",set_vars[setup_setvar].type); break; } font_leftstring(&oldstr[0]); translate(strlen(&oldstr[0])*ASPECT,0,0); font_leftstring("):"); translate(3*ASPECT,0,0); font_leftstring(&setup_str[0]); translate(strlen(&setup_str[0])*ASPECT,0,0); font_leftstring("_"); break; case SSE_ABORT: break; default: abort(); break; } } static void setup_any_toggle_boolean(long int liarg __attribute__((__unused__)), void *varg) { int *ip; ip = varg; *ip = ! *ip; } static void setup_any_set_num(long int liarg, void *varg __attribute__((__unused__))) { setup_getstr(setnum_aux,""); setup_setvar = liarg; } static void setup_ship_incship(long int liarg, void *varg __attribute__((__unused__))) { if (liarg > 0) { gainship(rnd(XMAXSCREEN),rnd(YMAXSCREEN)); } else { if (nships > 1) { int x; SHIP *s; sortstow(); nships --; x = (playship == ships[0]) ? 1 : 0; if (x != nships) { s = ships[nships]; ships[nships] = ships[x]; ships[x] = s; } s = ships[nships]; switch (s->dispmethod) { case SD_STOWED: nstowed --; break; case SD_STOWMOVE: nstowed --; nstowmove --; break; } stowcheck(); } else if ((nships == 1) && (state == STATE_ENDLEVEL)) { playship = 0; nships = 0; nstowed = 0; nstowmove = 0; } } } static void setup_ship_invulnerable(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { invulnerable ^= INVUL_SETUP; } static void setup_ship_showstow(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { showstow = ! showstow; } static void setup_saucer_invulnerable(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { sinvulnerable = ! sinvulnerable; } static void setup_saucer_create(long int liarg, void *varg __attribute__((__unused__))) { switch (saucerstate) { case SAUCER_HAVE: break; default: makesaucer(liarg); break; } } static void setup_saucer_explode(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (saucerstate == SAUCER_HAVE) explodesaucer(&saucer,0); } static void setup_saucer_destroy(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { saucerstate = SAUCER_NONE; } static void setup_saucer_collision(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { setup_link_alerts = ! setup_link_alerts; } static void setup_saucer_set_smarts(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (saucerstate == SAUCER_HAVE) { saucer.smart = liarg; saucer.obj.v.a = ((random() & 1) ? -1 : 1) * (liarg ? SAUCER_SMART_AVEL : SAUCER_DUMB_AVEL); } } static void setup_saucer_notice_collision(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { saucercollforce = liarg; } static void setup_saucer_forbid(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { saucerstate = SAUCER_DONT; } static void setup_saucer_shot_choose(long int liarg, void *varg __attribute__((__unused__))) { saucer_shoot_override = liarg; } static void setup_saucer_shot_shoot(long int liarg, void *varg __attribute__((__unused__))) { if (saucerstate == SAUCER_HAVE) { angle_to_debug = liarg; saucer_shoot(&saucer); angle_to_debug = 0; } } static void setup_game_inclevel(long int liarg, void *varg __attribute__((__unused__))) { level += liarg; if (level < 1) level = 1; } static void setup_game_singlestep(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { realtick(); } static void setup_game_kbd_press(long int liarg, void *varg __attribute__((__unused__))) { insetup = 0; handle_event(liarg,1); insetup = 1; } static void setup_game_kbd_release(long int liarg, void *varg __attribute__((__unused__))) { insetup = 0; handle_event(liarg,0); insetup = 1; } static void setup_reset_flashrock(void) { int i; ROCK *r; if (nrocks < 1) { flashing_rock_n = -1; flashing_rock = 0; } else { if (flashing_rock_n < 0) flashing_rock_n = 0; if (flashing_rock_n >= nrocks) flashing_rock_n = nrocks - 1; for (r=rocks,i=flashing_rock_n;r&&(i>0);i--,r=r->link) ; flashing_rock = r; } } static void setup_rock_menu_enter(void) { setup_reset_flashrock(); } static void setup_rock_menu_leave(void) { flashing_rock = 0; } static void setup_rock_inc(long int liarg, void *varg __attribute__((__unused__))) { flashing_rock_n += liarg; setup_reset_flashrock(); } static void setup_rock_explode(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { ROCK **rp; ROCK *r; for (rp=&rocks;(r=*rp)&&(r!=flashing_rock);rp=&r->link) ; if (r) poofrock(r,rp,0); setup_reset_flashrock(); } static void setup_rock_destroy(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { ROCK **rp; ROCK *r; for (rp=&rocks;(r=*rp)&&(r!=flashing_rock);rp=&r->link) ; if (r) { *rp = r->link; freerock(r); } setup_reset_flashrock(); } static void setup_rock_create(long int liarg, void *varg __attribute__((__unused__))) { ROCK *r; float a; float v; r = newrock(); linkrock(r); r->sizex = liarg; switch (liarg) { case ROCK_BIG: r->psize = ROCKSIZE_BIG; break; case ROCK_SMALL: r->psize = ROCKSIZE_SMALL; break; case ROCK_MEDIUM: r->psize = ROCKSIZE_MEDIUM; break; default: abort(); break; } r->shape = rnd(NROCKSHAPES); r->shapeinv = random() & 1; r->obj.x = frndc() * XMAXSCREEN; r->obj.y = frndc() * YMAXSCREEN; r->obj.a = frndo() * 360 DEGREES; a = frndo() * 360 DEGREES; v = (frndc() + 1) / 3; r->obj.movetype = OMT_VEL; r->obj.v.x = cos(a) * v; r->obj.v.y = sin(a) * v; r->obj.v.a = frndc() * SPIN_MAX DEGREES; if (random() & 1) r->obj.v.a = - r->obj.v.a; flashing_rock_n = 0; setup_reset_flashrock(); } static void setup_rock_shape_menu_enter(void) { do_rockshape_overlay = 1; suppress_display |= SUPPDIS_ROCKSHAPE; setupage = 0; } static void setup_rock_shape_menu_leave(void) { do_rockshape_overlay = 0; suppress_display &= ~SUPPDIS_ROCKSHAPE; } static void setup_rock_shape_inc(long int liarg, void *varg __attribute__((__unused__))) { rockshape_overlay += liarg; if (rockshape_overlay < 0) rockshape_overlay += NROCKSHAPES; if (rockshape_overlay >= NROCKSHAPES) rockshape_overlay -= NROCKSHAPES; } static void setup_rock_shape_invert(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { rockshape_overlay_invert = ! rockshape_overlay_invert; } static void setup_wiz_scorelist_menu_enter(void) { do_scorelist_overlay = 1; scorelist_overlay_base = 0; suppress_display |= SUPPDIS_SCORELIST; } static void setup_wiz_scorelist_menu_leave(void) { do_scorelist_overlay = 0; suppress_display &= ~SUPPDIS_SCORELIST; } static void adjust_overlay_base(void) { scorelist_overlay_base = scorelist_slot - (SCORELIST_SIMUL/2); } static void setup_wiz_scorelist_next(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (scorelist_slot < SCORELIST_SIZE-1) scorelist_slot ++; adjust_overlay_base(); } static void setup_wiz_scorelist_prev(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (scorelist_slot > 0) scorelist_slot --; adjust_overlay_base(); } static void scorelist_jump_aux(int op) { switch (op) { case SSE_DONE: scorelist_slot = atoi(&setup_str[0]) - 1; if (scorelist_slot > SCORELIST_SIZE-1) scorelist_slot = SCORELIST_SIZE-1; if (scorelist_slot < 0) scorelist_slot = 0; adjust_overlay_base(); break; case SSE_DRAW: font_leftstring("Jump to entry #"); translate(15*ASPECT,0,0); font_leftstring(&setup_str[0]); translate(strlen(&setup_str[0])*ASPECT,0,0); font_leftstring("_"); break; case SSE_ABORT: break; default: abort(); break; } } static void setup_wiz_scorelist_jump(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { setup_getstr(scorelist_jump_aux,""); } static void scorelist_set_aux(int op) { switch (op) { case SSE_DONE: (*scorelist_setfn)(&setup_str[0]); break; case SSE_DRAW: font_leftstring(scorelist_prompt); translate(strlen(scorelist_prompt)*ASPECT,0,0); font_leftstring(": "); translate(2*ASPECT,0,0); font_leftstring(&setup_str[0]); translate(strlen(&setup_str[0])*ASPECT,0,0); font_leftstring("_"); break; case SSE_ABORT: break; default: abort(); break; } } static void setwho_set(const char *s) { char t[3]; int letx; int i; strncpy(&t[0],s,3); for (i=0;i<3;i++) { switch (t[i]) { case 'a': case 'A': letx = 1; break; case 'b': case 'B': letx = 2; break; case 'c': case 'C': letx = 3; break; case 'd': case 'D': letx = 4; break; case 'e': case 'E': letx = 5; break; case 'f': case 'F': letx = 6; break; case 'g': case 'G': letx = 7; break; case 'h': case 'H': letx = 8; break; case 'i': case 'I': letx = 9; break; case 'j': case 'J': letx = 10; break; case 'k': case 'K': letx = 11; break; case 'l': case 'L': letx = 12; break; case 'm': case 'M': letx = 13; break; case 'n': case 'N': letx = 14; break; case 'o': case 'O': letx = 15; break; case 'p': case 'P': letx = 16; break; case 'q': case 'Q': letx = 17; break; case 'r': case 'R': letx = 18; break; case 's': case 'S': letx = 19; break; case 't': case 'T': letx = 20; break; case 'u': case 'U': letx = 21; break; case 'v': case 'V': letx = 22; break; case 'w': case 'W': letx = 23; break; case 'x': case 'X': letx = 24; break; case 'y': case 'Y': letx = 25; break; case 'z': case 'Z': letx = 26; break; default: letx = 0; break; } scorelist[scorelist_slot].name[i] = letx; } highscore_preen(); highscore_write(); } static void setlevel_set(const char *s) { int v; v = atoi(s); if (v > 0) scorelist[scorelist_slot].level = v; highscore_preen(); highscore_write(); } static void setscore_set(const char *s) { int v; v = atoi(s); if (v > 0) scorelist[scorelist_slot].score = v; highscore_preen(); highscore_write(); } static void setwhen_set(const char *s) { int vyr; int vmo; int vdy; int vhr; int vmn; int vsc; time_t now; struct tm *tm; time_t when; time(&now); tm = localtime(&now); if ( (sscanf(s,"%d-%d-%d %d:%d:%d",&vyr,&vmo,&vdy,&vhr,&vmn,&vsc) == 6) && ((vmo >= 1) && (vmo <= 12)) && ((vdy >= 1) && (vdy <= 31)) && ((vhr >= 0) && (vhr <= 24)) && ((vmn >= 0) && (vmn <= 60)) && ((vsc >= 0) && (vsc <= 60)) ) { if (vyr < 100) vyr += 100 * (tm->tm_year / 100); tm->tm_year = vyr - 1900; tm->tm_mon = vmo - 1; tm->tm_mday = vdy; tm->tm_hour = vhr; tm->tm_min = vmn; tm->tm_sec = vsc; } else if ( (sscanf(s,"%d-%d %d:%d:%d",&vmo,&vdy,&vhr,&vmn,&vsc) == 5) && ((vmo >= 1) && (vmo <= 12)) && ((vdy >= 1) && (vdy <= 31)) && ((vhr >= 0) && (vhr <= 24)) && ((vmn >= 0) && (vmn <= 60)) && ((vsc >= 0) && (vsc <= 60)) ) { tm->tm_mon = vmo - 1; tm->tm_mday = vdy; tm->tm_hour = vhr; tm->tm_min = vmn; tm->tm_sec = vsc; } else if ( (sscanf(s,"%d:%d:%d",&vhr,&vmn,&vsc) == 3) && ((vhr >= 0) && (vhr <= 24)) && ((vmn >= 0) && (vmn <= 60)) && ((vsc >= 0) && (vsc <= 60)) ) { tm->tm_hour = vhr; tm->tm_min = vmn; tm->tm_sec = vsc; } when = mktime(tm); if (when != (time_t)-1) scorelist[scorelist_slot].when = when; highscore_preen(); highscore_write(); } static void setup_wiz_scorelist_setwho(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (noscore_all || (highscore_fd < 0)) return; scorelist_setfn = setwho_set; scorelist_prompt = "New initials"; setup_getstr(scorelist_set_aux,""); } static void setup_wiz_scorelist_setlevel(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (noscore_all || (highscore_fd < 0)) return; scorelist_setfn = setlevel_set; scorelist_prompt = "New level number"; setup_getstr(scorelist_set_aux,""); } static void setup_wiz_scorelist_setscore(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (noscore_all || (highscore_fd < 0)) return; scorelist_setfn = setscore_set; scorelist_prompt = "New score"; setup_getstr(scorelist_set_aux,""); } static void setup_wiz_scorelist_setwhen(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (noscore_all || (highscore_fd < 0)) return; scorelist_setfn = setwhen_set; scorelist_prompt = "New time ([[yyyy]-mm-dd] hh:mm:ss)"; setup_getstr(scorelist_set_aux,""); } static void setup_wiz_scorelist_clear(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (noscore_all || (highscore_fd < 0)) return; scorelist[scorelist_slot].score = 0; highscore_preen(); highscore_write(); } static void enterpw_aux(int op) { int i; switch (op) { case SSE_DONE: iswiz = ! strcmp(wizpw,crypt(&setup_str[0],wizpw)); break; case SSE_DRAW: font_leftstring("Enter password: "); translate(16*ASPECT,0,0); for (i=strlen(&setup_str[0]);i>0;i--) { font_leftstring("\37"); translate(ASPECT,0,0); } font_leftstring("_"); break; case SSE_ABORT: break; default: abort(); break; } } static void setup_wiz_enterpw(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { setup_getstr(enterpw_aux,""); } static void setup_wiz_rescore(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { noscore_one = 0; } static void setup_wiz_exit_now(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { close_and_exit(); } static void setup_wiz_dont_exit(long int liarg __attribute__ ((unused)), void *varg __attribute__((__unused__))) { if (state != STATE_EXITING) return; enterstate(STATE_SHOWSCORE); } static void massage_helpscreens(void) { int sno; char *s; char *pct; char *end; char *mcopy; char *at; const char *kn; int knlen; int key; int len; int w; static void change_str(char *start, int size, const char *with, int withlen) { char *t; t = malloc((start-s)+withlen+strlen(start+size)+1); sprintf(t,"%.*s%.*s%s",(int)(start-s),s,withlen,with,start+size); free(mcopy); mcopy = t; s = t; helpscreens[sno] = t; } helpmaxmw = 0; for (sno=0;sno len) { change_str(pct,len,kn,knlen); at = pct + knlen; } else { bcopy(kn,pct,knlen); if (len > knlen) memset(pct+knlen,' ',len-knlen); at = pct + len; } break; case 'K': switch (pct[2]) { case 'E': key = ESCKEY; break; case '9': key = F9KEY; break; default: fprintf(stderr,"%s: %%K with unrecognized key character %c in help data\n",__progname,pct[2]); exit(1); break; } kn = keyname(key); knlen = strlen(kn); change_str(pct,3,kn,knlen); at = pct + knlen; break; default: fprintf(stderr,"%s: unrecognized %% escape %%%c in help data\n",__progname,pct[1]); exit(1); break; } } w = 0; for (kn=helpscreens[sno];*kn;kn++) { if (*kn == '\n') { if (w > helpmaxmw) helpmaxmw = w; w = 0; } else { w ++; } } } } int main(int, char **); int main(int ac, char **av) { #ifdef PROVIDE_PROGNAME __progname = av[0]; #endif parseargs(ac,av); if (! nosound) sound_start(audiodev); umask(0); highscore_open(); if (debugging) foreground(); prefposition(0,XMAXSCREEN-1,0,YMAXSCREEN-1); winopen("asteroids"); fullscrn(); RGBmode(); doublebuffer(); viewport(0,XMAXSCREEN,0,YMAXSCREEN); ortho2(0,XMAXSCREEN,0,YMAXSCREEN); gconfig(); massage_helpscreens(); qdevice(PIECECHANGE); qdevice(REDRAW); qdevice(TIMER2); qdevice(RIGHTMOUSE); queuebuttons(); noise(TIMER2,1); qreset(); scalesegs(); initrandom(); initships(); initgame(); getmatrix(noxfmat); while (1) { doticks(); if (dodraw) { dodraw = 0; cpack(0x00000000); clear(); cpack(0x00ffffff); drawit(); swapbuffers(); cursoff(); } qhandle(); } }