/*************************************************************************** extras.c - description ------------------- begin : Sun Sep 9 2001 copyright : (C) 2001 by Michael Speck email : kulkanie@gmx.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "../client/lbreakout.h" #include "levels.h" #include "paddle.h" #include "bricks.h" #include "balls.h" #include "mathfuncs.h" #include "extras.h" extern int ball_w, ball_dia; extern Game *cur_game; /* ==================================================================== Locals ==================================================================== */ /* ==================================================================== Public ==================================================================== */ /* ==================================================================== Create new extra at position ==================================================================== */ Extra *extra_create( int type, int x, int y, int dir ) { Extra *e = salloc( 1, sizeof( Extra ) ); e->type = type; e->offset = type * BRICK_WIDTH; e->x = x; e->y = y; e->dir = dir; e->alpha = 0; return e; } /* ==================================================================== Use extra when paddle collected it ==================================================================== */ void extra_use( Paddle *paddle, int extra_type ) { Ball *b; int i, j; if ( cur_game->diff->allow_maluses ) { while( extra_type == EX_RANDOM ) extra_type = rand() % (EX_NUMBER); } else { while ( extra_type == EX_RANDOM || extra_is_malus( extra_type ) ) extra_type = rand() % (EX_NUMBER); } /* store modification */ i = cur_game->paddles[0]==paddle?0:1; if ( cur_game->mod.collected_extra_count[i] < MAX_MODS ) cur_game->mod.collected_extras[i][cur_game->mod.collected_extra_count[i]++] = extra_type; /* statistics */ paddle->extras_collected++; switch (extra_type) { case EX_SCORE200: paddle->score += cur_game->diff->score_mod * 200 / 10; break; case EX_SCORE500: paddle->score += cur_game->diff->score_mod * 500 / 10; break; case EX_SCORE1000: paddle->score += cur_game->diff->score_mod * 1000 / 10; break; case EX_SCORE2000: paddle->score += cur_game->diff->score_mod * 2000 / 10; break; case EX_SCORE5000: paddle->score += cur_game->diff->score_mod * 5000 / 10; break; case EX_SCORE10000: paddle->score += cur_game->diff->score_mod * 10000 / 10; break; case EX_GOLDSHOWER: paddle->extra_time[EX_GOLDSHOWER] += TIME_GOLDSHOWER; paddle->extra_active[EX_GOLDSHOWER] = 1; break; case EX_LIFE: /* adding life is handled by client */ break; case EX_SHORTEN: paddle_init_resize( paddle, -1); break; case EX_LENGTHEN: paddle_init_resize( paddle, 1); break; case EX_BALL: b = ball_create( paddle->x + (paddle->w - ball_w) / 2, paddle->y + ((paddle->type == PADDLE_TOP)?paddle->h:-ball_dia) ); b->paddle = paddle; ball_set_random_angle( b, cur_game->ball_v ); b->get_target = 1; list_add( cur_game->balls, b ); break; case EX_WALL: paddle->extra_time[EX_WALL] += TIME_WALL; if ( paddle->extra_active[EX_WALL] ) break; paddle->extra_active[extra_type] = 1; if ( paddle->wall_y == 0 ) { for (i = 1; i < MAP_WIDTH - 1; i++) { cur_game->bricks[i][0].type = MAP_WALL; cur_game->bricks[i][0].id = 0; } } else for (i = 1; i < MAP_WIDTH - 1; i++) { cur_game->bricks[i][MAP_HEIGHT - 1].type = MAP_WALL; cur_game->bricks[i][MAP_HEIGHT - 1].id = 0; } paddle->wall_alpha = 0; balls_check_targets( -1, 0 ); break; case EX_METAL: cur_game->extra_time[EX_METAL] += TIME_METAL; cur_game->extra_active[extra_type] = 1; balls_set_type( BALL_METAL ); /* other ball extras are disabled */ if ( cur_game->extra_active[EX_EXPL_BALL] ) { cur_game->extra_active[EX_EXPL_BALL] = 0; cur_game->extra_time[EX_EXPL_BALL] = 0; } if ( cur_game->extra_active[EX_WEAK_BALL] ) { cur_game->extra_active[EX_WEAK_BALL] = 0; cur_game->extra_time[EX_WEAK_BALL] = 0; } break; case EX_FROZEN: paddle->extra_time[EX_FROZEN] = TIME_FROZEN; paddle->extra_active[extra_type] = 1; paddle_freeze( paddle, 1 ); break; case EX_WEAPON: paddle->extra_time[EX_WEAPON] += TIME_WEAPON; paddle->extra_active[extra_type] = 1; weapon_install( paddle, 1 ); break; case EX_SLIME: paddle->extra_time[EX_SLIME] += TIME_SLIME; paddle->extra_active[extra_type] = 1; paddle_set_slime( paddle, 1 ); break; case EX_FAST: if ( cur_game->extra_active[EX_SLOW] ) { cur_game->extra_time[EX_SLOW] = 0; cur_game->extra_active[EX_SLOW] = 0; } cur_game->extra_time[EX_FAST] += TIME_FAST; cur_game->extra_active[extra_type] = 1; cur_game->ball_v = cur_game->ball_v_max; balls_set_velocity( cur_game->balls, cur_game->ball_v ); break; case EX_SLOW: if ( cur_game->extra_active[EX_FAST] ) { cur_game->extra_time[EX_FAST] = 0; cur_game->extra_active[EX_FAST] = 0; } cur_game->extra_time[EX_SLOW] += TIME_SLOW; cur_game->extra_active[extra_type] = 1; cur_game->ball_v = cur_game->ball_v_min; balls_set_velocity( cur_game->balls, cur_game->ball_v ); break; case EX_CHAOS: cur_game->extra_time[EX_CHAOS] += TIME_CHAOS; cur_game->extra_active[extra_type] = 1; balls_set_chaos( 1 ); break; case EX_DARKNESS: cur_game->extra_time[EX_DARKNESS] += TIME_DARKNESS; cur_game->extra_active[extra_type] = 1; break; case EX_GHOST_PADDLE: paddle->extra_time[EX_GHOST_PADDLE] += TIME_GHOST_PADDLE; paddle->extra_active[extra_type] = 1; paddle_set_invis( paddle, 1 ); break; case EX_TIME_ADD: for ( i = 0; i < EX_NUMBER; i++ ) if ( cur_game->extra_time[i] ) cur_game->extra_time[i] += 7000; for ( i = 0; i < EX_NUMBER; i++ ) { for ( j = 0; j < cur_game->paddle_count; j++ ) if ( cur_game->paddles[j]->extra_time[i] ) cur_game->paddles[j]->extra_time[i] += 7000; } break; case EX_EXPL_BALL: balls_set_type( BALL_EXPL ); cur_game->extra_time[EX_EXPL_BALL] += TIME_EXPL_BALL; cur_game->extra_active[extra_type] = 1; /* other ball extras are disabled */ if ( cur_game->extra_active[EX_METAL] ) { cur_game->extra_active[EX_METAL] = 0; cur_game->extra_time[EX_METAL] = 0; } if ( cur_game->extra_active[EX_WEAK_BALL] ) { cur_game->extra_active[EX_WEAK_BALL] = 0; cur_game->extra_time[EX_WEAK_BALL] = 0; } break; case EX_WEAK_BALL: balls_set_type( BALL_WEAK ); cur_game->extra_time[EX_WEAK_BALL] += TIME_WEAK_BALL; cur_game->extra_active[extra_type] = 1; /* other ball extras are disabled */ if ( cur_game->extra_active[EX_METAL] ) { cur_game->extra_active[EX_METAL] = 0; cur_game->extra_time[EX_METAL] = 0; } if ( cur_game->extra_active[EX_EXPL_BALL] ) { cur_game->extra_active[EX_EXPL_BALL] = 0; cur_game->extra_time[EX_EXPL_BALL] = 0; } break; case EX_BONUS_MAGNET: paddle_set_attract( paddle, ATTRACT_BONUS ); paddle->extra_time[EX_BONUS_MAGNET] += TIME_BONUS_MAGNET; paddle->extra_active[extra_type] = 1; if ( paddle->extra_active[EX_MALUS_MAGNET] ) { paddle->extra_active[EX_MALUS_MAGNET] = 0; paddle->extra_time[EX_MALUS_MAGNET] = 0; } break; case EX_MALUS_MAGNET: paddle_set_attract( paddle, ATTRACT_MALUS ); paddle->extra_time[EX_MALUS_MAGNET] += TIME_MALUS_MAGNET; paddle->extra_active[extra_type] = 1; if ( paddle->extra_active[EX_BONUS_MAGNET] ) { paddle->extra_active[EX_BONUS_MAGNET] = 0; paddle->extra_time[EX_BONUS_MAGNET] = 0; } break; case EX_DISABLE: /* set all active extra times to 1 so they will expire next prog cycle */ for ( i = 0; i < EX_NUMBER; i++ ) if ( cur_game->extra_time[i] ) cur_game->extra_time[i] = 1; for ( i = 0; i < EX_NUMBER; i++ ) { for ( j = 0; j < cur_game->paddle_count; j++ ) if ( cur_game->paddles[j]->extra_time[i] ) cur_game->paddles[j]->extra_time[i] = 1; } break; default: /* it wasn't used so delete mod */ i = cur_game->paddles[0]==paddle?0:1; cur_game->mod.collected_extra_count[i]--; break; } } /* ==================================================================== Update extras ==================================================================== */ void extras_update( int ms ) { Extra *ex; int i, j; int magnets; Paddle *magnet; /* check extra_time of limited extras */ /* general extras */ for ( i = 0; i < EX_NUMBER; i++ ) if ( cur_game->extra_time[i] ) if ( (cur_game->extra_time[i] -= ms) <= 0 ) { cur_game->extra_time[i] = 0; /* expired */ switch ( i ) { case EX_EXPL_BALL: case EX_WEAK_BALL: case EX_METAL: balls_set_type( BALL_NORMAL ); break; case EX_SLOW: case EX_FAST: cur_game->ball_v = cur_game->diff->v_start + cur_game->diff->v_add * cur_game->speedup_level; balls_set_velocity( cur_game->balls, cur_game->ball_v ); break; case EX_CHAOS: balls_set_chaos( 0 ); break; } /* set deactivated */ cur_game->extra_active[i] = 0; } /* paddlized extras */ for ( j = 0; j < cur_game->paddle_count; j++ ) for ( i = 0; i < EX_NUMBER; i++ ) /* extra_time of wall is updated in wall_update() */ if ( cur_game->paddles[j]->extra_time[i] && i != EX_WALL ) if ( (cur_game->paddles[j]->extra_time[i] -= ms) <= 0 ) { cur_game->paddles[j]->extra_time[i] = 0; /* expired */ switch ( i ) { case EX_SLIME: paddle_set_slime( cur_game->paddles[j], 0 ); /* release all balls from paddle */ balls_detach_from_paddle( cur_game->paddles[j], ((rand()%2==1)?-1:1) ); break; case EX_WEAPON: weapon_install( cur_game->paddles[j], 0 ); break; case EX_FROZEN: paddle_freeze( cur_game->paddles[j], 0 ); break; case EX_GHOST_PADDLE: paddle_set_invis( cur_game->paddles[j], 0 ); break; case EX_BONUS_MAGNET: case EX_MALUS_MAGNET: paddle_set_attract( cur_game->paddles[j], ATTRACT_NONE ); break; } /* set deactivated */ cur_game->paddles[j]->extra_active[i] = 0; /* wall is handled in wall_...() */ } /* move extras and check if paddle was hit */ list_reset( cur_game->extras ); while ( ( ex = list_next( cur_game->extras ) ) ) { /* if only one paddle has a magnet active all extras will * be attracted by this paddle else the extras 'dir' is used */ magnets = 0; magnet = 0; for ( i = 0; i < cur_game->paddle_count; i++ ) if ( paddle_check_attract( cur_game->paddles[i], ex->type ) ) { magnets++; magnet = cur_game->paddles[i]; /* last magnet */ } if ( magnets != 1 ) { /* either no or more than one magnet so use default */ if ( ex->dir > 0 ) ex->y += 0.05 * ms; else ex->y -= 0.05 * ms; } else { /* 'magnet' is the paddle that will attract this extra */ if ( magnet->type == PADDLE_TOP ) ex->y -= 0.05 * ms; else ex->y += 0.05 * ms; if ( ex->x + ( BRICK_WIDTH >> 1 ) < magnet->x + ( magnet->w >> 1 ) ) { ex->x += 0.05 * ms; if ( ex->x + ( BRICK_WIDTH >> 1 ) > magnet->x + ( magnet->w >> 1 ) ) ex->x = magnet->x + ( magnet->w >> 1 ) - ( BRICK_WIDTH >> 1 ); } else { ex->x -= 0.05 * ms; if ( ex->x + ( BRICK_WIDTH >> 1 ) < magnet->x + ( magnet->w >> 1 ) ) ex->x = magnet->x + ( magnet->w >> 1 ) - ( BRICK_WIDTH >> 1 ); } } /* if out of screen, kill this extra */ if ( ex->y >= 480 || ex->y + BRICK_HEIGHT < 0 ) { list_delete_current( cur_game->extras ); continue; } for ( j = 0; j < cur_game->paddle_count; j++ ) { /* contact with paddle core ? */ if ( paddle_solid( cur_game->paddles[j] ) ) if ( ex->x + BRICK_WIDTH > cur_game->paddles[j]->x ) if ( ex->x < cur_game->paddles[j]->x + cur_game->paddles[j]->w - 1 ) if ( ex->y + BRICK_HEIGHT > cur_game->paddles[j]->y ) if ( ex->y < cur_game->paddles[j]->y + cur_game->paddles[j]->h ) { /* any extra except EX_JOKER is simply used */ if ( ex->type != EX_JOKER ) { extra_use( cur_game->paddles[j], ex->type ); list_delete_current( cur_game->extras ); break; } /* use EX_JOKER and work through all active extras */ /* the mod is only stored to play the sound */ if ( cur_game->mod.collected_extra_count[j] < MAX_MODS ) cur_game->mod.collected_extras[j][cur_game->mod.collected_extra_count[j]++] = EX_JOKER; list_reset( cur_game->extras ); while ( ( ex = list_next( cur_game->extras ) ) ) { if ( ex->type != EX_JOKER ) if ( ex->type != EX_SHORTEN ) if ( ex->type != EX_FROZEN ) if ( ex->type != EX_FAST ) if ( ex->type != EX_RANDOM ) if ( ex->type != EX_DARKNESS ) if ( ex->type != EX_GHOST_PADDLE ) if ( ex->type != EX_CHAOS ) if ( ex->type != EX_DISABLE ) if ( ex->type != EX_MALUS_MAGNET ) if ( ex->type != EX_WEAK_BALL ) { extra_use( cur_game->paddles[j], ex->type ); extra_use( cur_game->paddles[j], ex->type ); } list_delete_current( cur_game->extras ); } break; } } } } /* wall */ void walls_update( int ms ) { int i, j; for ( j = 0; j < cur_game->paddle_count; j++ ) if ( cur_game->paddles[j]->extra_active[EX_WALL] ) { if ( cur_game->paddles[j]->extra_time[EX_WALL] > 0 ) { if ( (cur_game->paddles[j]->extra_time[EX_WALL] -= ms) < 0 ) cur_game->paddles[j]->extra_time[EX_WALL] = 0; /* still appearing? */ if (cur_game->paddles[j]->wall_alpha < 255) if ( (cur_game->paddles[j]->wall_alpha += 0.25 * ms) > 255 ) cur_game->paddles[j]->wall_alpha = 255; } else if ( (cur_game->paddles[j]->wall_alpha -= 0.25 * ms) < 0 ) { cur_game->paddles[j]->wall_alpha = 0; cur_game->paddles[j]->extra_active[EX_WALL] = 0; if ( cur_game->paddles[j]->wall_y == 0 ) for (i = 1; i < MAP_WIDTH - 1; i++) cur_game->bricks[i][0].type = MAP_EMPTY; else for (i = 1; i < MAP_WIDTH - 1; i++) cur_game->bricks[i][MAP_HEIGHT - 1].type = MAP_EMPTY; balls_check_targets( -1, 0 ); } } } int extra_is_malus( int type ) { if ( type == EX_SHORTEN ) return 1; if ( type == EX_FROZEN ) return 1; if ( type == EX_FAST ) return 1; if ( type == EX_DARKNESS ) return 1; if ( type == EX_GHOST_PADDLE ) return 1; if ( type == EX_CHAOS ) return 1; if ( type == EX_DISABLE ) return 1; if ( type == EX_MALUS_MAGNET ) return 1; if ( type == EX_WEAK_BALL ) return 1; return 0; }