Main Page | Alphabetical List | Class List | File List | Class Members | File Members

engine/game.c

Go to the documentation of this file.
00001 #include "engine.h"
00002 
00003 #define MONSTMOVETIME ((current_map.gamerules==RULESET_KYE92)?(3):(6))
00004 engine_map current_map;
00005 int player_x, player_y;
00006 int scroll_x=0, scroll_y=0;
00007 int current_level_num = 0;
00008 int advance_to_level = 0;
00009 int round;
00010 int num_diamonds;
00011 int have_moved = 0;
00012 
00013 #if 0
00014 //{{{
00015 void game_loop(void)
00016 {
00017     draw_current_map();
00018     
00019     OSFreeTimer(USER_TIMER);
00020     OSRegisterTimer(USER_TIMER, DELAY_TIME);
00021     
00022     do
00023     {
00024         if(!delay_and_input())
00025             return;
00026         clear_effects();
00027         if(current_map.gamerules == RULESET_DCKYE)
00028             turn_sliders();
00029         set_magnet_skips();
00030         
00031         advance();
00032         round++;
00033         
00034         if(advance_to_level != current_level_num)
00035         {
00036             if(!level_advance(advance_to_level))
00037                 return;
00038         }
00039     } while(1);
00040 }
00041 //}}}
00042 //{{{
00043 int delay_and_input(void)
00044 {
00045     short input;
00046     short early_exit = 0;
00047     int have_received_input = 0;
00048     static int previous_move = 0;
00049     static int block_move = 0;
00050     
00051     have_moved = 0;
00052     
00053     if(OSTimerCurVal(USER_TIMER) > 5) {
00054         OSFreeTimer(USER_TIMER);
00055         OSRegisterTimer(USER_TIMER, DELAY_TIME);
00056     }
00057     
00058     do {
00059         GrayIdle();
00060         
00061         if(have_moved==0) {
00062             input = poll_char() & ~0x800;
00063             if(!input) continue;
00064             have_received_input = 1;
00065             if(input != previous_move) {
00066                 previous_move = input;
00067                 block_move = MOVE_DELAY;
00068                 if(!make_move(input))
00069                     return 0;
00070                 else
00071                     early_exit = 1;
00072             }
00073             else if(!block_move) {
00074                 if(!make_move(input)) return 0;
00075                 else early_exit = 1;
00076             }
00077         }
00078         if(early_exit && OSTimerCurVal(USER_TIMER)+1 < DELAY_TIME)
00079             break;
00080     } while(!OSTimerExpired(USER_TIMER));
00081     OSFreeTimer(USER_TIMER);
00082     
00083     if(have_moved == 0 && block_move==0) previous_move = 0;
00084     if(have_received_input == 0) {
00085         previous_move = 0;
00086         block_move = 0;
00087     }
00088     if(block_move) block_move--;
00089 
00090     flush_input();
00091     
00092     // Register timer for *next* cycle
00093     OSRegisterTimer(USER_TIMER, DELAY_TIME);
00094     
00095     return 1;
00096 }
00097 //}}}
00098 #else
00099 //{{{
00100 void game_loop(void)
00101 {
00102     int input;
00103     
00104     draw_current_map();
00105     
00106     OSFreeTimer(USER_TIMER);
00107     OSRegisterTimer(USER_TIMER, DELAY_TIME);
00108     
00109     do
00110     {
00111         have_moved = 0;
00112         input = read_char_with_timeout() & ~0x800;
00113         if(input) {
00114             if( make_move(input) == 0 )
00115                 return;
00116         }
00117         
00118         OSTimerRestart(USER_TIMER);
00119         
00120         clear_effects();
00121         set_magnet_skips();
00122         advance();
00123         round++;
00124         
00125         if(advance_to_level != current_level_num) {
00126             if(!level_advance(advance_to_level))
00127                 return;
00128         }
00129     } while(1);
00130 }
00131 //}}}
00132 #endif
00133 
00134 //{{{
00135 int level_advance(int level)
00136 {
00137     int input;
00138     do {
00139         current_level_num = level;
00140         if(!load_kyelib(current_level_num))
00141             return 0;
00142         
00143         input = show_splash();
00144         if(input=='+') {
00145             level++;
00146         } else if(input=='-') {
00147             if(current_level_num==0)
00148                 break;
00149             level = current_level_num-1;
00150         } else {
00151             break;
00152         }
00153     } while(1);
00154     advance_to_level = level;
00155     begin_level();
00156     draw_current_map();
00157     return 1;
00158 }
00159 //}}}
00160 
00161 //{{{
00162 int make_move(int key)
00163 {
00164     if(have_moved) return 1;
00165     have_moved = 1;
00166     if     (key == KEY_RIGHT) kye_move(1, 0);
00167     else if(key == KEY_DOWN) kye_move(0, 1);
00168     else if(key == KEY_LEFT) kye_move(-1, 0);
00169     else if(key == KEY_UP)   kye_move(0, -1);
00170     else if(key == KEY_ESC)  return 0;
00171     else if(key == KEY_F1 || key == KEY_CLEAR) restart_level();
00172     else if(key == '+')      advance_to_level = current_level_num + 1;
00173     else if(key == '-') {
00174         if(current_level_num > 0)
00175             advance_to_level = current_level_num - 1;
00176         else restart_level();
00177     } else if(key == KEY_F2) {
00178         if( strlen(current_map.hint)>0 )
00179             message(current_map.hint);
00180         draw_current_map();
00181     }
00182     
00183     return 1;
00184 }
00185 //}}}
00186 
00187 //{{{
00188 void kye_move(int dx, int dy)
00189 {
00190     int x = player_x+dx, y = player_y+dy;
00191     int block;
00192     if(!is_in_bounds(x, y))
00193         return;
00194     block = current_map.t[y][x].v;
00195     if(block==TILE_DIAMOND) {
00196         current_map.t[y][x].v = TILE_EMPTY;
00197         check_diamonds();
00198     } else if(block==TILE_EMPTY) {
00199         ;
00200     } else if(block==TILE_FUZZY) {
00201         current_map.t[y][x].v = TILE_EMPTY;
00202     } else if(can_shove(x, y, dx, dy)) {
00203         shove(x, y, dx, dy);
00204     } else if(block==DOOR_EW) {
00205         if(dx!=1) return;
00206     } else if(block==DOOR_WE) {
00207         if(dx!=-1) return;
00208     } else if(block==DOOR_NS) {
00209         if(dy!=1) return;
00210     } else if(block==DOOR_SN) {
00211         if(dy!=-1) return;
00212     } else {
00213         return;
00214     }
00215     
00216     if(current_map.t[player_y][player_x].v != TILE_KYE)
00217     {
00218         ;
00219     }
00220     else if(dx && is_in_bounds(player_x-dx, player_y) &&
00221        (current_map.t[player_y][player_x-dx].v == TILE_STICKY_EW ||
00222         current_map.t[player_y][player_x-dx].v == TILE_MAGNET_EW) )
00223     {
00224         current_map.t[player_y][player_x].v =
00225             current_map.t[player_y][player_x-dx].v;
00226         current_map.t[player_y][player_x-dx].v = TILE_EMPTY;
00227         draw_map_tile(player_x-dx, player_y);
00228         if(can_shove(player_x-dx-dx, player_y, dx, 0))
00229             shove(player_x-dx-dx, player_y, dx, 0);
00230     }
00231     else if(dy && is_in_bounds(player_x, player_y-dy) &&
00232             (current_map.t[player_y-dy][player_x].v == TILE_STICKY_NS ||
00233              current_map.t[player_y-dy][player_x].v == TILE_MAGNET_NS) )
00234     {
00235         current_map.t[player_y][player_x].v =
00236             current_map.t[player_y-dy][player_x].v;
00237         current_map.t[player_y-dy][player_x].v = TILE_EMPTY;
00238         draw_map_tile(player_x, player_y-dy);
00239         if(can_shove(player_x, player_y-dy-dy, 0, dy))
00240             shove(player_x, player_y-dy-dy, 0, dy);
00241     } else {
00242         current_map.t[player_y][player_x].v = TILE_EMPTY;
00243     }
00244     player_x += dx;
00245     player_y += dy;
00246     draw_map_tile(player_x-dx, player_y-dy);
00247     if(current_map.t[player_y][player_x].v==TILE_EMPTY)
00248         current_map.t[player_y][player_x].v = TILE_KYE;
00249     if(current_map.t[player_y][player_x].effect.duration > 0) {
00250         current_map.t[player_y][player_x].effect.sprite = EFFECT_NONE;
00251         current_map.t[player_y][player_x].effect.duration = 0;
00252     }
00253     draw_map_tile(player_x, player_y);
00254     scroll_viewport();
00255 }
00256 //}}}
00257 //{{{
00258 void scroll_viewport(void)
00259 {
00260     int new_x=scroll_x, new_y=scroll_y;
00261     
00262     if(player_x < scroll_x+EDGE_X)
00263         new_x = max(scroll_x-SCROLL_INCR_X, 0);
00264     if(player_y < scroll_y+EDGE_Y)
00265         new_y = max(scroll_y-SCROLL_INCR_Y, 0);
00266     if(player_x+EDGE_X >= scroll_x+VIEWPORT_WIDTH)
00267         new_x = min(scroll_x+SCROLL_INCR_X, FIELD_WIDTH-VIEWPORT_WIDTH);
00268     if(player_y+EDGE_Y >= scroll_y+VIEWPORT_HEIGHT)
00269         new_y = min(scroll_y+SCROLL_INCR_Y, FIELD_HEIGHT-VIEWPORT_HEIGHT);
00270     
00271     if(new_x != scroll_x || new_y != scroll_y) {
00272         scroll_x = new_x;
00273         scroll_y = new_y;
00274         draw_current_map();
00275     }
00276 }
00277 //}}}
00278 //{{{
00279 void center_viewport(void)
00280 {
00281     int new_x, new_y;
00282     
00283     new_x = player_x - VIEWPORT_WIDTH/2;
00284     new_y = player_y - VIEWPORT_HEIGHT/2;
00285     if(new_x+VIEWPORT_WIDTH>=FIELD_WIDTH) new_x=FIELD_WIDTH-VIEWPORT_WIDTH;
00286     if(new_y+VIEWPORT_HEIGHT>=FIELD_HEIGHT) new_y=FIELD_HEIGHT-VIEWPORT_HEIGHT;
00287     if(new_x < 0) new_x = 0;
00288     if(new_y < 0) new_y = 0;
00289     
00290     scroll_x = new_x;
00291     scroll_y = new_y;
00292 }
00293 //}}}
00294 
00295 //{{{
00296 static inline void _drawtile(int x, int y)
00297 {
00298     int xpos = (x-scroll_x)<<3;
00299     int ypos = (y-scroll_y)<<3;
00300     
00301     if(x==player_x && y==player_y)
00302         draw_sprite(tile_sprites[ TILE_KYE ], xpos, ypos);
00303     else if(current_map.t[y][x].effect.sprite == EFFECT_NONE)
00304         draw_sprite(tile_sprites[ current_map.t[y][x].v ], xpos, ypos);
00305     else
00306         draw_sprite(effects[ current_map.t[y][x].effect.sprite ], xpos, ypos);
00307 
00308 }
00309 //}}}
00310 //{{{
00311 void draw_current_map(void)
00312 {
00313     int xi, yi;
00314     int xmax, ymax;
00315     
00316     clear_screen();
00317     
00318     xmax = scroll_x + VIEWPORT_WIDTH;
00319     ymax = scroll_y + VIEWPORT_HEIGHT;
00320     if(xmax > FIELD_WIDTH) xmax = FIELD_WIDTH;
00321     if(ymax > FIELD_HEIGHT) ymax = FIELD_HEIGHT;
00322     
00323     // If the field is *smaller*, then it needs a border around it
00324     if(FIELD_WIDTH < VIEWPORT_WIDTH) {
00325         draw_line(FIELD_WIDTH*8+1, 0, FIELD_WIDTH*8+1,
00326                   ymax*8, A_NORMAL);
00327     }
00328     if(FIELD_HEIGHT < VIEWPORT_HEIGHT) {
00329         draw_line(0, ymax*8+1, FIELD_WIDTH*8+1,
00330                   ymax*8+1, A_NORMAL);
00331     }
00332     
00333     for(xi=scroll_x; xi<xmax; xi++)
00334     for(yi=scroll_y; yi<ymax; yi++)
00335     {
00336         _drawtile(xi, yi);
00337     }
00338     
00339     draw_line(0, LCD_HEIGHT-11, (LCD_WIDTH-1), LCD_HEIGHT-11, A_NORMAL);
00340     redraw_status_bar();
00341 }
00342 //}}}
00343 //{{{
00344 void draw_map_tile(int x, int y)
00345 {
00346     if( x<scroll_x || y<scroll_y || x>=scroll_x+VIEWPORT_WIDTH ||
00347         y>=scroll_y+VIEWPORT_HEIGHT) // Off-screen tile
00348         return;
00349     
00350     _drawtile(x, y);
00351 }
00352 //}}}
00353 //{{{
00354 void redraw_status_bar(void)
00355 {
00356     char buf[32];
00357     clear_line_range(LCD_HEIGHT-10, LCD_HEIGHT-1);
00358     draw_string("Diamonds: ", 2, LCD_HEIGHT-7, F_4x6);
00359     sprintf(buf, "%i", (int)num_diamonds);
00360     draw_string(buf, 40, LCD_HEIGHT-7, F_4x6);
00361     draw_string("Level: ", 100, LCD_HEIGHT-7, F_4x6);
00362     sprintf(buf, "%i", (int)(current_level_num+1));
00363     draw_string(buf, 125, LCD_HEIGHT-7, F_4x6);
00364 }
00365 //}}}
00366 
00367 //{{{
00368 void advance(void)
00369 {
00370     int xi, yi;
00371     int xmax = FIELD_WIDTH;
00372     int ymax = FIELD_HEIGHT;
00373     
00374     // Move sliders, monsters, etc.
00375     for(yi=0; yi<ymax; yi++)
00376     for(xi=0; xi<xmax; xi++)
00377     {
00378         switch(current_map.t[yi][xi].v)
00379         {
00380             case TILE_SLIDER_S: move_slider(xi, yi, 0, 0, 1); break;
00381             case TILE_SLIDER_E: move_slider(xi, yi, 0, 1, 0); break;
00382             case TILE_SLIDER_N: move_slider(xi, yi, 0, 0, -1); break;
00383             case TILE_SLIDER_W: move_slider(xi, yi, 0, -1, 0); break;
00384             case TILE_ROCKY_S:  move_slider(xi, yi, 1, 0, 1); break;
00385             case TILE_ROCKY_E:  move_slider(xi, yi, 1, 1, 0); break;
00386             case TILE_ROCKY_N:  move_slider(xi, yi, 1, 0, -1); break;
00387             case TILE_ROCKY_W:  move_slider(xi, yi, 1, -1, 0); break;
00388             
00389             case TILE_PUSHER_S: move_pusher(xi, yi, 0, 1); break;
00390             case TILE_PUSHER_E: move_pusher(xi, yi, 1, 0); break;
00391             case TILE_PUSHER_N: move_pusher(xi, yi, 0, -1); break;
00392             case TILE_PUSHER_W: move_pusher(xi, yi, -1, 0); break;
00393             
00394             case TILE_MAGNET_NS:
00395                 if(can_shove(xi, yi-2, 0, 1))
00396                     shove(xi, yi-2, 0, 1);
00397                 if(current_map.t[yi-2][xi].v==TILE_KYE && can_shove(xi,yi,0,-1))
00398                     shove(xi, yi, 0, -1);
00399                 if(can_shove(xi, yi+2, 0, -1))
00400                     shove(xi, yi+2, 0, -1);
00401                 if(current_map.t[yi+2][xi].v==TILE_KYE && can_shove(xi,yi,0,1))
00402                     shove(xi, yi, 0, 1);
00403                 break;
00404             case TILE_MAGNET_EW:
00405                 if(can_shove(xi-2, yi, 1, 0))
00406                     shove(xi-2, yi, 1, 0);
00407                 if(current_map.t[yi][xi-2].v==TILE_KYE && can_shove(xi,yi,-1,0))
00408                     shove(xi, yi, -1, 0);
00409                 if(can_shove(xi+2, yi, -1, 0))
00410                     shove(xi+2, yi, -1, 0);
00411                 if(current_map.t[yi][xi+2].v==TILE_KYE && can_shove(xi,yi,1,0))
00412                     shove(xi, yi, 1, 0);
00413                 break;
00414             
00415             case TILE_BLOB:    // Monsters
00416             case TILE_TWISTER:
00417             case TILE_GNASHER:
00418             case TILE_VIRUS:
00419             case TILE_SPIKE:
00420                 if( round % MONSTMOVETIME == 0 )
00421                     move_monster(xi, yi);
00422                 break;
00423             
00424 #ifdef ANIMATE_DIAMONDS
00425             case TILE_DIAMOND:
00426                 if( current_map.t[yi][xi].effect.duration <= 1)
00427                     //FIXMEFIXMEFIXME: This breaks RNG-linked maps (Roulette)!
00428                 {
00429                     if(current_map.t[yi][xi].effect.sprite == EFFECT_NONE) {
00430                         current_map.t[yi][xi].effect.sprite = EFFECT_DIAMOND;
00431                         current_map.t[yi][xi].effect.duration = 25+rand()%75;
00432                     } else {
00433                         current_map.t[yi][xi].effect.sprite = EFFECT_NONE;
00434                         current_map.t[yi][xi].effect.duration = 25+rand()%75;
00435                     }
00436                     draw_map_tile(xi, yi);
00437                 }
00438                 break;
00439 #endif
00440         }
00441     }
00442     
00443     // Check for monsters around the player
00444     if(   tile_is_monster(player_x-1, player_y)
00445        || tile_is_monster(player_x+1, player_y)
00446        || tile_is_monster(player_x, player_y-1)
00447        || tile_is_monster(player_x, player_y+1) )
00448     {
00449         message("Ouch!");
00450         restart_level();
00451     }
00452 }
00453 //}}}
00454 //{{{
00455 int tile_is_monster(int x, int y)
00456 {
00457     int tile = current_map.t[y][x].v;
00458     if(tile==TILE_BLOB    || tile==TILE_VIRUS ||
00459        tile==TILE_TWISTER || tile==TILE_GNASHER ||
00460        tile==TILE_SPIKE )
00461         return 1;
00462     else
00463         return 0;
00464 }
00465 //}}}
00466 
00467 //{{{
00468 void turn_sliders(void)
00469 {
00470     int xi, yi;
00471     for(yi=0; yi<FIELD_HEIGHT; yi++)
00472     for(xi=0; xi<FIELD_WIDTH; xi++)
00473     {
00474         switch(current_map.t[yi][xi].v)
00475         {
00476             case TILE_SLIDER_S:
00477                 try_rotate(xi, yi, 0, 1, TILE_SLIDER_W, TILE_SLIDER_E);
00478                 break;
00479             case TILE_SLIDER_E:
00480                 try_rotate(xi, yi, 1, 0, TILE_SLIDER_S, TILE_SLIDER_N);
00481                 break;
00482             case TILE_SLIDER_N:
00483                 try_rotate(xi, yi, 0, -1, TILE_SLIDER_E, TILE_SLIDER_W);
00484                 break;
00485             case TILE_SLIDER_W:
00486                 try_rotate(xi, yi, -1, 0, TILE_SLIDER_N, TILE_SLIDER_S);
00487                 break;
00488             case TILE_ROCKY_S:
00489                 try_rotate(xi, yi, 0, 1, TILE_ROCKY_W, TILE_ROCKY_E);
00490                 break;
00491             case TILE_ROCKY_E:
00492                 try_rotate(xi, yi, 1, 0, TILE_ROCKY_S, TILE_ROCKY_N);
00493                 break;
00494             case TILE_ROCKY_N:
00495                 try_rotate(xi, yi, 0, -1, TILE_ROCKY_E, TILE_ROCKY_W);
00496                 break;
00497             case TILE_ROCKY_W:
00498                 try_rotate(xi, yi, -1, 0, TILE_ROCKY_N, TILE_ROCKY_S);
00499                 break;
00500         }
00501     }
00502 }
00503 //}}}
00504 //{{{
00505 void try_rotate(int x, int y, int dx, int dy, int cw, int ccw)
00506 {
00507     if(current_map.t[y+dy][x+dx].v == TILE_TWIST_CW) {
00508         current_map.t[y][x].v = cw;
00509         draw_map_tile(x, y);
00510     }
00511     else if(current_map.t[y+dy][x+dx].v == TILE_TWIST_CCW) {
00512         current_map.t[y][x].v = ccw;
00513         draw_map_tile(x, y);
00514     }
00515 }
00516 //}}}
00517 //{{{
00518 void move_slider(int x, int y, int slippery, int dx, int dy)
00519 {
00520     int block;
00521     int movedir_1=0, movedir_2=0;
00522     
00523     if(current_map.t[y][x].skip)
00524         return;
00525     
00526     if(!is_in_bounds(x+dx, y+dy))
00527         return;
00528     
00529     block = current_map.t[y+dy][x+dx].v;
00530     
00531     if(block == TILE_EMPTY)
00532     {
00533         current_map.t[y+dy][x+dx].v = current_map.t[y][x].v;
00534         current_map.t[y+dy][x+dx].skip = 1;
00535         draw_map_tile(x+dx, y+dy);
00536         current_map.t[y][x].v = TILE_EMPTY;
00537         draw_map_tile(x, y);
00538         return;
00539     }
00540     else if(block == TILE_EXPLODER) {
00541         current_map.t[y][x].v = TILE_EMPTY;
00542         draw_map_tile(x, y);
00543         detonate(x+dx, y+dy);
00544         return;
00545     }
00546     else if(block == TILE_PIT) {
00547         current_map.t[y][x].v = TILE_EMPTY;
00548         draw_map_tile(x, y);
00549         current_map.t[y+dy][x+dx].v = TILE_EMPTY;
00550         draw_map_tile(x+dx, y+dy);
00551         return;
00552     }
00553     else if(block == TILE_TWIST_CW || block == TILE_TWIST_CCW)
00554     {
00555         try_rotate(x, y, dx, dy,
00556             rotate_cw(current_map.t[y][x].v),
00557             rotate_ccw(current_map.t[y][x].v));
00558         return;
00559     }
00560     
00561     else if(!slippery) return;
00562     
00563     switch(block)
00564     {
00565         case TILE_ROCKY_S:
00566         case TILE_ROCKY_E:
00567         case TILE_ROCKY_N:
00568         case TILE_ROCKY_W:
00569         case TILE_BOULDER:
00570             goto random_falloff;
00571         case TILE_WALL_3:
00572             if(dy==-1) goto negative_falloff;
00573             else if(dx==-1) goto negative_falloff;
00574             break;
00575         case TILE_WALL_9:
00576             if(dy==1) goto positive_falloff;
00577             else if(dx==-1) goto positive_falloff;
00578             break;
00579         case TILE_WALL_7:
00580             if(dy==1) goto negative_falloff;
00581             else if(dx==1) goto negative_falloff;
00582             break;
00583         case TILE_WALL_1:
00584             if(dy==-1) goto positive_falloff;
00585             else if(dx==1) goto positive_falloff;
00586             break;
00587         case TILE_WALL_2:
00588             if(dy==-1) goto random_falloff;
00589             else if(dx==1) goto positive_falloff;
00590             else if(dx==-1) goto negative_falloff;
00591             break;
00592         case TILE_WALL_6:
00593             if(dx==-1) goto random_falloff;
00594             else if(dy==1) goto positive_falloff;
00595             else if(dy==-1) goto negative_falloff;
00596             break;
00597         case TILE_WALL_8:
00598             if(dy==1) goto random_falloff;
00599             else if(dx==1) goto negative_falloff;
00600             else if(dx==-1) goto positive_falloff;
00601             break;
00602         case TILE_WALL_4:
00603             if(dx==1) goto random_falloff;
00604             else if(dy==1) goto negative_falloff;
00605             else if(dy==-1) goto positive_falloff;
00606             break;
00607     }
00608     return;
00609     
00610     // All of the wall cases reduce to four:
00611     //       v
00612     //    #B<--
00613     //        |s
00614     //        /
00615     // Vector v is (dx,dy)
00616     // Vector s is (dy,dx)
00617     // Either the block can fall to v+s, it can fall to v-s, it can fall
00618     // to either, or it can fall to neither. If it can fall to either, which
00619     // direction it falls is chosen randomly.
00620 random_falloff:
00621     movedir_1 = movedir_2 = 1;
00622     goto evaluate_falloff;
00623 positive_falloff:
00624     movedir_1 = 1;
00625     goto evaluate_falloff;
00626 negative_falloff:
00627     movedir_2 = 1;
00628     goto evaluate_falloff;
00629     
00630     // And *these* cases are just the state of two binary values
00631     
00632 evaluate_falloff:
00633     if(movedir_1 && is_in_bounds(x+dx+dy, y+dx+dy) &&
00634        current_map.t[y+dx][x+dy].v==TILE_EMPTY &&
00635        current_map.t[y+dx+dy][x+dx+dy].v==TILE_EMPTY)
00636         movedir_1 = 1;
00637     else
00638         movedir_1 = 0;
00639     if(movedir_2 && is_in_bounds(x+dx-dy, y-dx+dy) &&
00640        current_map.t[y-dx][x-dy].v==TILE_EMPTY &&
00641        current_map.t[y-dx+dy][x+dx-dy].v==TILE_EMPTY)
00642         movedir_2 = 1;
00643     else
00644         movedir_2 = 0;
00645     
00646     if(movedir_1 && movedir_2) {
00647         if(rand()%2) movedir_1 = 0;
00648         else         movedir_2 = 0;
00649     }
00650     if(movedir_1) {
00651         current_map.t[y+dx+dy][x+dx+dy].v = current_map.t[y][x].v;
00652         current_map.t[y+dx+dy][x+dx+dy].skip = 1;
00653         current_map.t[y][x].v = TILE_EMPTY;
00654         draw_map_tile(x+dx+dy, y+dx+dy);
00655         draw_map_tile(x, y);
00656     }
00657     else if(movedir_2) {
00658         current_map.t[y-dx+dy][x+dx-dy].v = current_map.t[y][x].v;
00659         current_map.t[y-dx+dy][x+dx-dy].skip = 1;
00660         current_map.t[y][x].v = TILE_EMPTY;
00661         draw_map_tile(x+dx-dy, y-dx+dy);
00662         draw_map_tile(x, y);
00663     }
00664 
00665 }
00666 //}}}
00667 //{{{
00668 int rotate_cw(int tile)
00669 {
00670     switch(tile)
00671     {
00672         case TILE_SLIDER_N: return TILE_SLIDER_E;
00673         case TILE_SLIDER_E: return TILE_SLIDER_S;
00674         case TILE_SLIDER_S: return TILE_SLIDER_W;
00675         case TILE_SLIDER_W: return TILE_SLIDER_N;
00676         case TILE_ROCKY_N:  return TILE_ROCKY_E;
00677         case TILE_ROCKY_E:  return TILE_ROCKY_S;
00678         case TILE_ROCKY_S:  return TILE_ROCKY_W;
00679         case TILE_ROCKY_W:  return TILE_ROCKY_N;
00680         default: return tile;
00681     }
00682 }
00683 //}}}
00684 //{{{
00685 int rotate_ccw(int tile)
00686 {
00687     switch(tile)
00688     {
00689         case TILE_SLIDER_N: return TILE_SLIDER_W;
00690         case TILE_SLIDER_E: return TILE_SLIDER_N;
00691         case TILE_SLIDER_S: return TILE_SLIDER_E;
00692         case TILE_SLIDER_W: return TILE_SLIDER_S;
00693         case TILE_ROCKY_N:  return TILE_ROCKY_W;
00694         case TILE_ROCKY_E:  return TILE_ROCKY_N;
00695         case TILE_ROCKY_S:  return TILE_ROCKY_E;
00696         case TILE_ROCKY_W:  return TILE_ROCKY_S;
00697         default: return tile;
00698     }
00699 }
00700 //}}}
00701 //{{{
00702 void move_pusher(int x, int y, int dx, int dy)
00703 {
00704     if(current_map.t[y][x].skip)
00705         return;
00706     
00707     if(current_map.gamerules == RULESET_DCKYE) {
00708         if(round%10 != 0) return;
00709     } else {
00710         if(round%5 != 0) return;
00711     }
00712     
00713     if(!is_in_bounds(x+dx, y+dy) || current_map.t[y+dy][x+dx].v != TILE_EMPTY)
00714     {
00715         if(current_map.t[y+dy][x+dx].v == TILE_PIT) {
00716             current_map.t[y][x].v = TILE_EMPTY;
00717             draw_map_tile(x, y);
00718             current_map.t[y+dy][x+dx].v = TILE_EMPTY;
00719             draw_map_tile(x+dx, y+dy);
00720             return;
00721         }
00722         
00723         // Reverse direction
00724         switch(current_map.t[y][x].v)
00725         {
00726             case TILE_PUSHER_W: current_map.t[y][x].v = TILE_PUSHER_E; break;
00727             case TILE_PUSHER_E: current_map.t[y][x].v = TILE_PUSHER_W; break;
00728             case TILE_PUSHER_N: current_map.t[y][x].v = TILE_PUSHER_S; break;
00729             case TILE_PUSHER_S: current_map.t[y][x].v = TILE_PUSHER_N; break;
00730         }
00731         draw_map_tile(x, y);
00732         // Shove
00733         if(can_shove(x+dx, y+dy, dx, dy))
00734             shove(x+dx, y+dy, dx, dy);
00735     } else {
00736         shove(x, y, dx, dy);
00737         current_map.t[y+dy][x+dx].skip = 1;
00738     }
00739 }
00740 //}}}
00741 //{{{
00742 void move_monster(int x, int y)
00743 {
00744     int dx, dy;
00745     dx=dy=0;
00746     // 50%: move towards player
00747     // 50%: move randomly
00748     
00749     if(current_map.t[y][x].skip) return;
00750     
00751     if(rand()%2 == 0) {
00752         // Move randomly
00753         switch(rand()%5) {
00754             case 0: dx=dy=0; break;
00755             case 1: dx=1,  dy=0; break;
00756             case 2: dx=-1, dy=0; break;
00757             case 3: dx=0,  dy=1; break;
00758             case 4: dx=0,  dy=-1; break;
00759         }
00760     } else {
00761         // Move towards player
00762         if(player_x>x) dx=1;
00763         else if(player_x<x) dx=-1;
00764         if(player_y>y) dy=1;
00765         if(player_y<y) dy=-1;
00766         if(!is_in_bounds(x+dx, y) || current_map.t[y][x+dx].v!=TILE_EMPTY) dx=0;
00767         if(!is_in_bounds(x, y+dy) || current_map.t[y+dy][x].v!=TILE_EMPTY) dy=0;
00768         if(dx && dy) {
00769             if( abs(player_y-y) > abs(player_x-x) )
00770                 dx=0;
00771             else
00772                 dy=0;
00773         }
00774     }
00775     
00776     current_map.t[y][x].effect = animate_monster(x, y);
00777     
00778     if((dx==0 && dy==0) || !is_in_bounds(x+dx, y+dy)
00779        || current_map.t[y+dy][x+dx].v != TILE_EMPTY) {
00780         draw_map_tile(x, y);
00781         return;
00782     }
00783     current_map.t[y+dy][x+dx].v = current_map.t[y][x].v;
00784     current_map.t[y+dy][x+dx].effect = current_map.t[y][x].effect;
00785     current_map.t[y+dy][x+dx].skip = 1;
00786     current_map.t[y][x].v = TILE_EMPTY;
00787     current_map.t[y][x].effect.sprite = EFFECT_NONE;
00788     current_map.t[y][x].effect.duration = 0;
00789     draw_map_tile(x+dx, y+dy);
00790     draw_map_tile(x, y);
00791 }
00792 //}}}
00793 //{{{
00794 effect_info animate_monster(int x, int y)
00795 {
00796     effect_info ret;
00797     
00798     if((round/MONSTMOVETIME)%2 == 0) {
00799         ret.duration=0;
00800         ret.sprite = EFFECT_NONE;
00801         return ret;
00802     }
00803     
00804     ret.duration = -1;
00805     switch(current_map.t[y][x].v)
00806     {
00807         case TILE_BLOB:    ret.sprite=EFFECT_BLOB; break;
00808         case TILE_VIRUS:   ret.sprite=EFFECT_VIRUS; break;
00809         case TILE_TWISTER: ret.sprite=EFFECT_TWISTER; break;
00810         case TILE_GNASHER: ret.sprite=EFFECT_GNASHER; break;
00811         case TILE_SPIKE:   ret.sprite=EFFECT_SPIKE; break;
00812     }
00813     return ret;
00814 }
00815 //}}}
00816 //{{{
00817 void detonate(int x, int y)
00818 {
00819     int xi, yi;
00820     
00821     // Yuck, I know. But since the effect tiles have to map into a linear
00822     // array for efficient rendering, this is pretty much forced.
00823     if(is_in_bounds(x-1, y-1)) {
00824         current_map.t[y-1][x-1].effect.sprite = EFFECT_EXPLODE_TOPLEFT;
00825         current_map.t[y-1][x-1].effect.duration = EXPLODE_DURATION;
00826     }
00827     if(is_in_bounds(x, y-1)) {
00828         current_map.t[y-1][x].effect.sprite = EFFECT_EXPLODE_TOP;
00829         current_map.t[y-1][x].effect.duration = EXPLODE_DURATION;
00830     }
00831     if(is_in_bounds(x+1, y-1)) {
00832         current_map.t[y-1][x+1].effect.sprite = EFFECT_EXPLODE_TOPRIGHT;
00833         current_map.t[y-1][x+1].effect.duration = EXPLODE_DURATION;
00834     }
00835     if(is_in_bounds(x-1, y)) {
00836         current_map.t[y][x-1].effect.sprite = EFFECT_EXPLODE_LEFT;
00837         current_map.t[y][x-1].effect.duration = EXPLODE_DURATION;
00838     }
00839     if(is_in_bounds(x, y)) {
00840         current_map.t[y][x].effect.sprite = EFFECT_EXPLODE_CENTER;
00841         current_map.t[y][x].effect.duration = EXPLODE_DURATION;
00842     }
00843     if(is_in_bounds(x+1, y)) {
00844         current_map.t[y][x+1].effect.sprite = EFFECT_EXPLODE_RIGHT;
00845         current_map.t[y][x+1].effect.duration = EXPLODE_DURATION;
00846     }
00847     if(is_in_bounds(x-1, y+1)) {
00848         current_map.t[y+1][x-1].effect.sprite = EFFECT_EXPLODE_BOTLEFT;
00849         current_map.t[y+1][x-1].effect.duration = EXPLODE_DURATION;
00850     }
00851     if(is_in_bounds(x, y+1)) {
00852         current_map.t[y+1][x].effect.sprite = EFFECT_EXPLODE_BOTTOM;
00853         current_map.t[y+1][x].effect.duration = EXPLODE_DURATION;
00854     }
00855     if(is_in_bounds(x+1, y+1)) {
00856         current_map.t[y+1][x+1].effect.sprite = EFFECT_EXPLODE_BOTRIGHT;
00857         current_map.t[y+1][x+1].effect.duration = EXPLODE_DURATION;
00858     }
00859     current_map.t[y][x].v = TILE_PIT;
00860     for(yi=y-1; yi<=y+1; yi++)
00861     for(xi=x-1; xi<=x+1; xi++)
00862     {
00863         if(!is_in_bounds(xi, yi)) continue;
00864         if(current_map.t[yi][xi].v!=TILE_DIAMOND // Diamonds can't be destroyed
00865            || current_map.t[yi][xi].v == TILE_KYE  // Kye survives explosions
00866            || current_map.t[yi][xi].v == TILE_PIT) // So do pits
00867               current_map.t[yi][xi].v = TILE_EMPTY;
00868         draw_map_tile(xi, yi);
00869     }
00870 }
00871 //}}}
00872 //{{{
00873 void clear_effects(void)
00874 {
00875     int xi, yi;
00876     int xmax = FIELD_WIDTH;
00877     int ymax = FIELD_HEIGHT;
00878     for(yi=0; yi<ymax; yi++)
00879     for(xi=0; xi<xmax; xi++)
00880     {
00881         current_map.t[yi][xi].skip = 0;
00882         
00883         if(current_map.t[yi][xi].effect.duration > 0)
00884         {
00885             if(current_map.t[yi][xi].effect.duration == 1) {
00886                 current_map.t[yi][xi].effect.duration = 0;
00887                 current_map.t[yi][xi].effect.sprite = EFFECT_NONE;
00888                 draw_map_tile(xi, yi);
00889             } else {
00890                 current_map.t[yi][xi].effect.duration--;
00891             }
00892         }
00893     }
00894 }
00895 //}}}
00896 
00897 //{{{
00898 int is_in_bounds(int x, int y)
00899 {
00900     if(x<0 || y<0 || x>=FIELD_WIDTH || y>=FIELD_HEIGHT)
00901         return 0;
00902     else
00903         return 1;
00904 }
00905 //}}}
00906 //{{{
00907 int can_shove(int x, int y, int dx, int dy)
00908 {
00909     if(!is_in_bounds(x, y))
00910         return 0;
00911     if(!is_in_bounds(x+dx, y+dy))
00912         return 0;
00913     
00914     switch(current_map.t[y+dy][x+dx].v)
00915     {
00916         case TILE_EMPTY:
00917         case TILE_PIT:
00918             break;
00919         default:
00920             return 0;
00921     }
00922     
00923     switch(current_map.t[y][x].v)
00924     {
00925         case TILE_SLIDER_S: case TILE_SLIDER_E:  // Pushable tiles (*might* be
00926         case TILE_SLIDER_N: case TILE_SLIDER_W:  // passable, if there's room
00927         case TILE_ROCKY_S:   case TILE_ROCKY_E:  // to push them into.)
00928         case TILE_ROCKY_N:   case TILE_ROCKY_W:
00929         case TILE_BLOCK:    case TILE_BOULDER:
00930         case TILE_EXPLODER:
00931         case TILE_TWIST_CW: case TILE_TWIST_CCW:
00932         case TILE_STICKY_EW: case TILE_STICKY_NS:
00933         case TILE_MAGNET_EW: case TILE_MAGNET_NS:
00934         case TILE_PUSHER_W: case TILE_PUSHER_E:
00935         case TILE_PUSHER_N: case TILE_PUSHER_S:
00936         case TILE_VIRUS:    case TILE_BLOB:
00937         case TILE_TWISTER:  case TILE_GNASHER:
00938         case TILE_SPIKE:
00939             break;
00940         default:
00941             return 0;
00942     }
00943 
00944     return 1;
00945 }
00946 //}}}
00947 //{{{
00948 void shove(int x, int y, int dx, int dy)
00949 {
00950     if(current_map.t[y+dy][x+dx].v == TILE_PIT)
00951     {
00952         current_map.t[y][x].v = TILE_EMPTY;
00953         current_map.t[y+dy][x+dx].v = TILE_EMPTY;
00954     } else {
00955         current_map.t[y+dy][x+dx].v = current_map.t[y][x].v;
00956         current_map.t[y][x].v = TILE_EMPTY;
00957     }
00958     if(current_map.t[y][x].effect.duration == -1) {
00959         current_map.t[y][x].effect.duration = 0;
00960         current_map.t[y][x].effect.sprite = 0;
00961     }
00962     draw_map_tile(x, y);
00963     draw_map_tile(x+dx, y+dy);
00964 }
00965 //}}}
00966 
00967 //{{{
00968 int check_diamonds(void)
00969 {
00970     int xi, yi;
00971     int prev_diamonds = num_diamonds;
00972     num_diamonds = 0;
00973     for(yi=0; yi<FIELD_HEIGHT; yi++)
00974     for(xi=0; xi<FIELD_WIDTH; xi++)
00975     {
00976         if(current_map.t[yi][xi].v == TILE_DIAMOND)
00977             num_diamonds++;
00978     }
00979     if(num_diamonds==0) {
00980         advance_to_level = current_level_num+1;
00981         if(strlen(current_map.congrat) > 0) {
00982             message(current_map.congrat);
00983         }
00984         return 0;
00985     }
00986     if(num_diamonds != prev_diamonds)
00987         redraw_status_bar();
00988     return 1;
00989 }
00990 //}}}
00991 
00992 //{{{
00993 void set_magnet_skips(void)
00994 {
00995     int xi, yi;
00996     int xmax = FIELD_WIDTH;
00997     int ymax = FIELD_HEIGHT;
00998     for(yi=0; yi<ymax; yi++)
00999     for(xi=0; xi<xmax; xi++)
01000     {
01001         if( current_map.t[yi][xi].v == TILE_STICKY_NS ||
01002                 current_map.t[yi][xi].v == TILE_MAGNET_NS) {
01003             if(yi>0) current_map.t[yi-1][xi].skip = 1;
01004             if(yi+1<ymax) current_map.t[yi+1][xi].skip = 1;
01005         } else if( current_map.t[yi][xi].v == TILE_STICKY_EW ||
01006                 current_map.t[yi][xi].v == TILE_MAGNET_EW) {
01007             if(xi>0) current_map.t[yi][xi-1].skip = 1;
01008             if(xi+1<xmax) current_map.t[yi][xi+1].skip = 1;
01009         }
01010     }
01011 }
01012 //}}}
01013 
01014 //{{{
01015 void restart_level(void)
01016 {
01017     load_kyelib(current_level_num);
01018     begin_level();
01019     draw_current_map();
01020 }
01021 //}}}
01022 //{{{
01023 void begin_level(void)
01024 {
01025     int xi, yi;
01026     
01027     round = 1;
01028     
01029     for(yi=0; yi<FIELD_HEIGHT; yi++)
01030     for(xi=0; xi<FIELD_WIDTH; xi++)
01031     {
01032         current_map.t[yi][xi].effect.duration = 0;
01033         current_map.t[yi][xi].effect.sprite = EFFECT_NONE;
01034         if(current_map.t[yi][xi].v == TILE_KYE) {
01035             player_x = xi;
01036             player_y = yi;
01037         }
01038     }
01039     
01040     center_viewport();
01041     check_diamonds();
01042 }
01043 //}}}
01044 

Generated on Thu Apr 22 14:06:33 2004 for SKye by doxygen 1.3.6