X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=f359a2cbdfb406c6bf018873c7b14c5901397dfc;hp=d3c403fe75b8ad7050dfcec0a4f91d07e53f581f;hb=4848f0bcd25bee8a7a4e48ae7d2a670bbc4e278d;hpb=d0409bd76aa84a8745ec2ea6d8a5480c8bea0bcd diff --git a/src/game.c b/src/game.c index d3c403fe..f359a2cb 100644 --- a/src/game.c +++ b/src/game.c @@ -19,6 +19,7 @@ #include "files.h" #include "tape.h" #include "network.h" +#include "anim.h" /* DEBUG SETTINGS */ @@ -80,7 +81,7 @@ #define EX_TYPE_DYNA (1 << 4) #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER) -#define PANEL_OFF() (local_player->LevelSolved_PanelOff) +#define PANEL_OFF() (game.panel.active == FALSE) #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF()) #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p)) #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p)) @@ -121,87 +122,90 @@ #define GAME_PANEL_TIME_HH 32 #define GAME_PANEL_TIME_MM 33 #define GAME_PANEL_TIME_SS 34 -#define GAME_PANEL_FRAME 35 -#define GAME_PANEL_SHIELD_NORMAL 36 -#define GAME_PANEL_SHIELD_NORMAL_TIME 37 -#define GAME_PANEL_SHIELD_DEADLY 38 -#define GAME_PANEL_SHIELD_DEADLY_TIME 39 -#define GAME_PANEL_EXIT 40 -#define GAME_PANEL_EMC_MAGIC_BALL 41 -#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42 -#define GAME_PANEL_LIGHT_SWITCH 43 -#define GAME_PANEL_LIGHT_SWITCH_TIME 44 -#define GAME_PANEL_TIMEGATE_SWITCH 45 -#define GAME_PANEL_TIMEGATE_SWITCH_TIME 46 -#define GAME_PANEL_SWITCHGATE_SWITCH 47 -#define GAME_PANEL_EMC_LENSES 48 -#define GAME_PANEL_EMC_LENSES_TIME 49 -#define GAME_PANEL_EMC_MAGNIFIER 50 -#define GAME_PANEL_EMC_MAGNIFIER_TIME 51 -#define GAME_PANEL_BALLOON_SWITCH 52 -#define GAME_PANEL_DYNABOMB_NUMBER 53 -#define GAME_PANEL_DYNABOMB_SIZE 54 -#define GAME_PANEL_DYNABOMB_POWER 55 -#define GAME_PANEL_PENGUINS 56 -#define GAME_PANEL_SOKOBAN_OBJECTS 57 -#define GAME_PANEL_SOKOBAN_FIELDS 58 -#define GAME_PANEL_ROBOT_WHEEL 59 -#define GAME_PANEL_CONVEYOR_BELT_1 60 -#define GAME_PANEL_CONVEYOR_BELT_2 61 -#define GAME_PANEL_CONVEYOR_BELT_3 62 -#define GAME_PANEL_CONVEYOR_BELT_4 63 -#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64 -#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65 -#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66 -#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67 -#define GAME_PANEL_MAGIC_WALL 68 -#define GAME_PANEL_MAGIC_WALL_TIME 69 -#define GAME_PANEL_GRAVITY_STATE 70 -#define GAME_PANEL_GRAPHIC_1 71 -#define GAME_PANEL_GRAPHIC_2 72 -#define GAME_PANEL_GRAPHIC_3 73 -#define GAME_PANEL_GRAPHIC_4 74 -#define GAME_PANEL_GRAPHIC_5 75 -#define GAME_PANEL_GRAPHIC_6 76 -#define GAME_PANEL_GRAPHIC_7 77 -#define GAME_PANEL_GRAPHIC_8 78 -#define GAME_PANEL_ELEMENT_1 79 -#define GAME_PANEL_ELEMENT_2 80 -#define GAME_PANEL_ELEMENT_3 81 -#define GAME_PANEL_ELEMENT_4 82 -#define GAME_PANEL_ELEMENT_5 83 -#define GAME_PANEL_ELEMENT_6 84 -#define GAME_PANEL_ELEMENT_7 85 -#define GAME_PANEL_ELEMENT_8 86 -#define GAME_PANEL_ELEMENT_COUNT_1 87 -#define GAME_PANEL_ELEMENT_COUNT_2 88 -#define GAME_PANEL_ELEMENT_COUNT_3 89 -#define GAME_PANEL_ELEMENT_COUNT_4 90 -#define GAME_PANEL_ELEMENT_COUNT_5 91 -#define GAME_PANEL_ELEMENT_COUNT_6 92 -#define GAME_PANEL_ELEMENT_COUNT_7 93 -#define GAME_PANEL_ELEMENT_COUNT_8 94 -#define GAME_PANEL_CE_SCORE_1 95 -#define GAME_PANEL_CE_SCORE_2 96 -#define GAME_PANEL_CE_SCORE_3 97 -#define GAME_PANEL_CE_SCORE_4 98 -#define GAME_PANEL_CE_SCORE_5 99 -#define GAME_PANEL_CE_SCORE_6 100 -#define GAME_PANEL_CE_SCORE_7 101 -#define GAME_PANEL_CE_SCORE_8 102 -#define GAME_PANEL_CE_SCORE_1_ELEMENT 103 -#define GAME_PANEL_CE_SCORE_2_ELEMENT 104 -#define GAME_PANEL_CE_SCORE_3_ELEMENT 105 -#define GAME_PANEL_CE_SCORE_4_ELEMENT 106 -#define GAME_PANEL_CE_SCORE_5_ELEMENT 107 -#define GAME_PANEL_CE_SCORE_6_ELEMENT 108 -#define GAME_PANEL_CE_SCORE_7_ELEMENT 109 -#define GAME_PANEL_CE_SCORE_8_ELEMENT 110 -#define GAME_PANEL_PLAYER_NAME 111 -#define GAME_PANEL_LEVEL_NAME 112 -#define GAME_PANEL_LEVEL_AUTHOR 113 - -#define NUM_GAME_PANEL_CONTROLS 114 +#define GAME_PANEL_TIME_ANIM 35 +#define GAME_PANEL_HEALTH 36 +#define GAME_PANEL_HEALTH_ANIM 37 +#define GAME_PANEL_FRAME 38 +#define GAME_PANEL_SHIELD_NORMAL 39 +#define GAME_PANEL_SHIELD_NORMAL_TIME 40 +#define GAME_PANEL_SHIELD_DEADLY 41 +#define GAME_PANEL_SHIELD_DEADLY_TIME 42 +#define GAME_PANEL_EXIT 43 +#define GAME_PANEL_EMC_MAGIC_BALL 44 +#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45 +#define GAME_PANEL_LIGHT_SWITCH 46 +#define GAME_PANEL_LIGHT_SWITCH_TIME 47 +#define GAME_PANEL_TIMEGATE_SWITCH 48 +#define GAME_PANEL_TIMEGATE_SWITCH_TIME 49 +#define GAME_PANEL_SWITCHGATE_SWITCH 50 +#define GAME_PANEL_EMC_LENSES 51 +#define GAME_PANEL_EMC_LENSES_TIME 52 +#define GAME_PANEL_EMC_MAGNIFIER 53 +#define GAME_PANEL_EMC_MAGNIFIER_TIME 54 +#define GAME_PANEL_BALLOON_SWITCH 55 +#define GAME_PANEL_DYNABOMB_NUMBER 56 +#define GAME_PANEL_DYNABOMB_SIZE 57 +#define GAME_PANEL_DYNABOMB_POWER 58 +#define GAME_PANEL_PENGUINS 59 +#define GAME_PANEL_SOKOBAN_OBJECTS 60 +#define GAME_PANEL_SOKOBAN_FIELDS 61 +#define GAME_PANEL_ROBOT_WHEEL 62 +#define GAME_PANEL_CONVEYOR_BELT_1 63 +#define GAME_PANEL_CONVEYOR_BELT_2 64 +#define GAME_PANEL_CONVEYOR_BELT_3 65 +#define GAME_PANEL_CONVEYOR_BELT_4 66 +#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67 +#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68 +#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69 +#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70 +#define GAME_PANEL_MAGIC_WALL 71 +#define GAME_PANEL_MAGIC_WALL_TIME 72 +#define GAME_PANEL_GRAVITY_STATE 73 +#define GAME_PANEL_GRAPHIC_1 74 +#define GAME_PANEL_GRAPHIC_2 75 +#define GAME_PANEL_GRAPHIC_3 76 +#define GAME_PANEL_GRAPHIC_4 77 +#define GAME_PANEL_GRAPHIC_5 78 +#define GAME_PANEL_GRAPHIC_6 79 +#define GAME_PANEL_GRAPHIC_7 80 +#define GAME_PANEL_GRAPHIC_8 81 +#define GAME_PANEL_ELEMENT_1 82 +#define GAME_PANEL_ELEMENT_2 83 +#define GAME_PANEL_ELEMENT_3 84 +#define GAME_PANEL_ELEMENT_4 85 +#define GAME_PANEL_ELEMENT_5 86 +#define GAME_PANEL_ELEMENT_6 87 +#define GAME_PANEL_ELEMENT_7 88 +#define GAME_PANEL_ELEMENT_8 89 +#define GAME_PANEL_ELEMENT_COUNT_1 90 +#define GAME_PANEL_ELEMENT_COUNT_2 91 +#define GAME_PANEL_ELEMENT_COUNT_3 92 +#define GAME_PANEL_ELEMENT_COUNT_4 93 +#define GAME_PANEL_ELEMENT_COUNT_5 94 +#define GAME_PANEL_ELEMENT_COUNT_6 95 +#define GAME_PANEL_ELEMENT_COUNT_7 96 +#define GAME_PANEL_ELEMENT_COUNT_8 97 +#define GAME_PANEL_CE_SCORE_1 98 +#define GAME_PANEL_CE_SCORE_2 99 +#define GAME_PANEL_CE_SCORE_3 100 +#define GAME_PANEL_CE_SCORE_4 101 +#define GAME_PANEL_CE_SCORE_5 102 +#define GAME_PANEL_CE_SCORE_6 103 +#define GAME_PANEL_CE_SCORE_7 104 +#define GAME_PANEL_CE_SCORE_8 105 +#define GAME_PANEL_CE_SCORE_1_ELEMENT 106 +#define GAME_PANEL_CE_SCORE_2_ELEMENT 107 +#define GAME_PANEL_CE_SCORE_3_ELEMENT 108 +#define GAME_PANEL_CE_SCORE_4_ELEMENT 109 +#define GAME_PANEL_CE_SCORE_5_ELEMENT 110 +#define GAME_PANEL_CE_SCORE_6_ELEMENT 111 +#define GAME_PANEL_CE_SCORE_7_ELEMENT 112 +#define GAME_PANEL_CE_SCORE_8_ELEMENT 113 +#define GAME_PANEL_PLAYER_NAME 114 +#define GAME_PANEL_LEVEL_NAME 115 +#define GAME_PANEL_LEVEL_AUTHOR 116 + +#define NUM_GAME_PANEL_CONTROLS 117 struct GamePanelOrderInfo { @@ -218,6 +222,8 @@ struct GamePanelControlInfo struct TextPosInfo *pos; int type; + int graphic, graphic_active; + int value, last_value; int frame, last_frame; int gfx_frame; @@ -401,6 +407,27 @@ static struct GamePanelControlInfo game_panel_controls[] = &game.panel.time_ss, TYPE_INTEGER, }, + { + GAME_PANEL_TIME_ANIM, + &game.panel.time_anim, + TYPE_GRAPHIC, + + IMG_GFX_GAME_PANEL_TIME_ANIM, + IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE + }, + { + GAME_PANEL_HEALTH, + &game.panel.health, + TYPE_INTEGER, + }, + { + GAME_PANEL_HEALTH_ANIM, + &game.panel.health_anim, + TYPE_GRAPHIC, + + IMG_GFX_GAME_PANEL_HEALTH_ANIM, + IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE + }, { GAME_PANEL_FRAME, &game.panel.frame, @@ -826,6 +853,14 @@ static struct GamePanelControlInfo game_panel_controls[] = #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value)) #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value)) +/* values for scroll positions */ +#define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \ + (x) > SBX_Right + MIDPOSX ? SBX_Right :\ + (x) - MIDPOSX) +#define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\ + (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\ + (y) - MIDPOSY) + /* values for other actions */ #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED) #define MOVE_STEPSIZE_MIN (1) @@ -967,6 +1002,8 @@ static struct GamePanelControlInfo game_panel_controls[] = #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d)) #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d)) +#define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH)) + /* game button identifiers */ #define GAME_CTRL_ID_STOP 0 #define GAME_CTRL_ID_PAUSE 1 @@ -976,11 +1013,17 @@ static struct GamePanelControlInfo game_panel_controls[] = #define GAME_CTRL_ID_SAVE 5 #define GAME_CTRL_ID_PAUSE2 6 #define GAME_CTRL_ID_LOAD 7 -#define SOUND_CTRL_ID_MUSIC 8 -#define SOUND_CTRL_ID_LOOPS 9 -#define SOUND_CTRL_ID_SIMPLE 10 +#define GAME_CTRL_ID_PANEL_STOP 8 +#define GAME_CTRL_ID_PANEL_PAUSE 9 +#define GAME_CTRL_ID_PANEL_PLAY 10 +#define SOUND_CTRL_ID_MUSIC 11 +#define SOUND_CTRL_ID_LOOPS 12 +#define SOUND_CTRL_ID_SIMPLE 13 +#define SOUND_CTRL_ID_PANEL_MUSIC 14 +#define SOUND_CTRL_ID_PANEL_LOOPS 15 +#define SOUND_CTRL_ID_PANEL_SIMPLE 16 -#define NUM_GAME_BUTTONS 11 +#define NUM_GAME_BUTTONS 17 /* forward declaration for internal use */ @@ -1042,7 +1085,8 @@ static void PlayLevelSoundElementAction(int, int, int, int); static void PlayLevelSoundElementActionIfLoop(int, int, int, int); static void PlayLevelSoundActionIfLoop(int, int, int); static void StopLevelSoundActionIfLoop(int, int, int); -static void PlayLevelMusic(); +static void PlayLevelMusic(void); +static void FadeLevelSoundsAndMusic(void); static void HandleGameButtons(struct GadgetInfo *); @@ -1052,7 +1096,7 @@ void ContinueMoving(int, int); void Bang(int, int); void InitMovDir(int, int); void InitAmoebaNr(int, int); -int NewHiScore(void); +int NewHiScore(int); void TestIfGoodThingHitsBadThing(int, int, int); void TestIfBadThingHitsGoodThing(int, int, int); @@ -1068,6 +1112,7 @@ void TestIfGoodThingGetsHitByBadThing(int, int, int); void KillPlayer(struct PlayerInfo *); void BuryPlayer(struct PlayerInfo *); void RemovePlayer(struct PlayerInfo *); +void ExitPlayer(struct PlayerInfo *); static int getInvisibleActiveFromInvisibleElement(int); static int getInvisibleFromInvisibleActiveElement(int); @@ -1517,7 +1562,7 @@ static int playfield_scan_delta_y = 1; (x) += playfield_scan_delta_x) #ifdef DEBUG -void DEBUG_SetMaximumDynamite() +void DEBUG_SetMaximumDynamite(void) { int i; @@ -1528,7 +1573,7 @@ void DEBUG_SetMaximumDynamite() } #endif -static void InitPlayfieldScanModeVars() +static void InitPlayfieldScanModeVars(void) { if (game.use_reverse_scan_direction) { @@ -1591,7 +1636,7 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize, } } -void GetPlayerConfig() +void GetPlayerConfig(void) { GameFrameDelay = setup.game_frame_delay; @@ -1610,7 +1655,6 @@ void GetPlayerConfig() setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music); SetAudioMode(setup.sound); - InitJoysticks(); } int GetElementFromGroupElement(int element) @@ -1688,7 +1732,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) if (game.use_block_last_field_bug) player->block_delay_adjustment = (player->block_last_field ? -1 : 1); - if (!options.network || player->connected) + if (!network.enabled || player->connected_network) { player->active = TRUE; @@ -2049,7 +2093,17 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2) return compare_result; } -void InitGameControlValues() +int getPlayerInventorySize(int player_nr) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return level.native_em_level->ply[player_nr]->dynamite; + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + return level.native_sp_level->game_sp->red_disk_count; + else + return stored_player[player_nr].inventory_size; +} + +static void InitGameControlValues(void) { int i; @@ -2094,7 +2148,7 @@ void InitGameControlValues() sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo); } -void UpdatePlayfieldElementCount() +static void UpdatePlayfieldElementCount(void) { boolean use_element_count = FALSE; int i, j, x, y; @@ -2122,7 +2176,7 @@ void UpdatePlayfieldElementCount() element_info[j].element_count; } -void UpdateGameControlValues() +static void UpdateGameControlValues(void) { int i, k; int time = (local_player->LevelSolved ? @@ -2131,6 +2185,8 @@ void UpdateGameControlValues() level.native_em_level->lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->time_played : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.energy_left : game.no_time_limit ? TimePlayed : TimeLeft); int score = (local_player->LevelSolved ? local_player->LevelSolved_CountingScore : @@ -2138,25 +2194,38 @@ void UpdateGameControlValues() level.native_em_level->lev->score : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->score : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.score : local_player->score); int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->infotrons_still_needed : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.kettles_still_needed : local_player->gems_still_needed); int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->infotrons_still_needed > 0 : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.kettles_still_needed > 0 || + game_mm.lights_still_needed > 0 : local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || local_player->lights_still_needed > 0); + int health = (local_player->LevelSolved ? + local_player->LevelSolved_CountingHealth : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + MM_HEALTH(game_mm.laser_overload_value) : + local_player->health); UpdatePlayfieldElementCount(); /* update game panel control values */ - game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr; + /* used instead of "level_nr" (for network games) */ + game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr; game_panel_controls[GAME_PANEL_GEMS].value = gems; game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0; @@ -2186,15 +2255,8 @@ void UpdateGameControlValues() get_key_element_from_nr(k); } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_em_level->ply[i]->dynamite; - else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_sp_level->game_sp->red_disk_count; - else - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - stored_player[i].inventory_size; + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + getPlayerInventorySize(i); if (stored_player[i].num_white_keys > 0) game_panel_controls[GAME_PANEL_KEY_WHITE].value = @@ -2221,15 +2283,8 @@ void UpdateGameControlValues() get_key_element_from_nr(k); } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_em_level->ply[player_nr]->dynamite; - else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_sp_level->game_sp->red_disk_count; - else - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - stored_player[player_nr].inventory_size; + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + getPlayerInventorySize(player_nr); if (stored_player[player_nr].num_white_keys > 0) game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE; @@ -2255,6 +2310,14 @@ void UpdateGameControlValues() game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60; game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60; + if (level.time == 0) + game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100; + else + game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time; + + game_panel_controls[GAME_PANEL_HEALTH].value = health; + game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health; + game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter; game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value = @@ -2407,6 +2470,36 @@ void UpdateGameControlValues() gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value), gpc->gfx_frame); + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; + } + } + else if (gpc->type == TYPE_GRAPHIC) + { + if (gpc->graphic != IMG_UNDEFINED) + { + int last_anim_random_frame = gfx.anim_random_frame; + int graphic = gpc->graphic; + + if (gpc->value != gpc->last_value) + { + gpc->gfx_frame = 0; + gpc->gfx_random = INIT_GFX_RANDOM(); + } + else + { + gpc->gfx_frame++; + + if (ANIM_MODE(graphic) == ANIM_RANDOM && + IS_NEXT_FRAME(gpc->gfx_frame, graphic)) + gpc->gfx_random = INIT_GFX_RANDOM(); + } + + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = gpc->gfx_random; + + gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame); + if (ANIM_MODE(graphic) == ANIM_RANDOM) gfx.anim_random_frame = last_anim_random_frame; } @@ -2414,7 +2507,7 @@ void UpdateGameControlValues() } } -void DisplayGameControlValues() +static void DisplayGameControlValues(void) { boolean redraw_panel = FALSE; int i; @@ -2530,6 +2623,94 @@ void DisplayGameControlValues() dst_x, dst_y); } } + else if (type == TYPE_GRAPHIC) + { + int graphic = gpc->graphic; + int graphic_active = gpc->graphic_active; + Bitmap *src_bitmap; + int src_x, src_y; + int width, height; + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); + boolean skip = (pos->class == get_hash_from_key("mm_engine_only") && + level.game_engine_type != GAME_ENGINE_TYPE_MM); + + if (graphic != IMG_UNDEFINED && !skip) + { + if (pos->style == STYLE_REVERSE) + value = 100 - value; + + getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y); + + if (pos->direction & MV_HORIZONTAL) + { + width = graphic_info[graphic_active].width * value / 100; + height = graphic_info[graphic_active].height; + + if (pos->direction == MV_LEFT) + { + src_x += graphic_info[graphic_active].width - width; + dst_x += graphic_info[graphic_active].width - width; + } + } + else + { + width = graphic_info[graphic_active].width; + height = graphic_info[graphic_active].height * value / 100; + + if (pos->direction == MV_UP) + { + src_y += graphic_info[graphic_active].height - height; + dst_y += graphic_info[graphic_active].height - height; + } + } + + if (draw_masked) + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + else + BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + + getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y); + + if (pos->direction & MV_HORIZONTAL) + { + if (pos->direction == MV_RIGHT) + { + src_x += width; + dst_x += width; + } + else + { + dst_x = PANEL_XPOS(pos); + } + + width = graphic_info[graphic].width - width; + } + else + { + if (pos->direction == MV_DOWN) + { + src_y += height; + dst_y += height; + } + else + { + dst_y = PANEL_YPOS(pos); + } + + height = graphic_info[graphic].height - height; + } + + if (draw_masked) + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + else + BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + } + } else if (type == TYPE_STRING) { boolean active = (value != 0); @@ -2577,7 +2758,7 @@ void DisplayGameControlValues() SetGameStatus(GAME_MODE_PLAYING); } -void UpdateAndDisplayGameControlValues() +void UpdateAndDisplayGameControlValues(void) { if (tape.deactivate_display) return; @@ -2586,12 +2767,14 @@ void UpdateAndDisplayGameControlValues() DisplayGameControlValues(); } -void UpdateGameDoorValues() +#if 0 +static void UpdateGameDoorValues(void) { UpdateGameControlValues(); } +#endif -void DrawGameDoorValues() +void DrawGameDoorValues(void) { DisplayGameControlValues(); } @@ -2605,7 +2788,7 @@ void DrawGameDoorValues() ============================================================================= */ -static void InitGameEngine() +static void InitGameEngine(void) { int i, j, k, l, x, y; @@ -2694,6 +2877,12 @@ static void InitGameEngine() game.use_block_last_field_bug = (game.engine_version < VERSION_IDENT(3,1,1,0)); + game_em.use_single_button = + (game.engine_version > VERSION_IDENT(4,0,0,2)); + + game_em.use_snap_key_bug = + (game.engine_version < VERSION_IDENT(4,0,1,0)); + /* ---------------------------------------------------------------------- */ /* set maximal allowed number of custom element changes per game frame */ @@ -3048,9 +3237,16 @@ static void InitGameEngine() SNAPSHOT_MODE_EVERY_MOVE : strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ? SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF); + game.snapshot.save_snapshot = FALSE; + + /* ---------- initialize level time for Supaplex engine ------------------- */ + /* Supaplex levels with time limit currently unsupported -- should be added */ + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + level.time = 0; } -int get_num_special_action(int element, int action_first, int action_last) +static int get_num_special_action(int element, int action_first, + int action_last) { int num_special_action = 0; int i, j; @@ -3082,7 +3278,37 @@ int get_num_special_action(int element, int action_first, int action_last) ============================================================================= */ -void InitGame() +#if DEBUG_INIT_PLAYER +static void DebugPrintPlayerStatus(char *message) +{ + int i; + + if (!options.debug) + return; + + printf("%s:\n", message); + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d [%d/%d], active == %d", + i + 1, + player->present, + player->connected, + player->connected_locally, + player->connected_network, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } +} +#endif + +void InitGame(void) { int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0); int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0); @@ -3102,34 +3328,34 @@ void InitGame() SetGameStatus(GAME_MODE_PLAYING); - /* needed if different viewport properties defined for playing */ - ChangeViewportPropertiesIfNeeded(); - if (level_editor_test_game) FadeSkipNextFadeIn(); else FadeSetEnterScreen(); - if (CheckIfGlobalBorderHasChanged()) + if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) fade_mask = REDRAW_ALL; - FadeSoundsAndMusic(); + FadeLevelSoundsAndMusic(); ExpireSoundLoops(TRUE); FadeOut(fade_mask); - ClearField(); + /* needed if different viewport properties defined for playing */ + ChangeViewportPropertiesIfNeeded(); - OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + ClearField(); DrawCompleteVideoDisplay(); + OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + InitGameEngine(); InitGameControlValues(); /* don't play tapes over network */ - network_playing = (options.network && !tape.playing); + network_playing = (network.enabled && !tape.playing); for (i = 0; i < MAX_PLAYERS; i++) { @@ -3150,12 +3376,26 @@ void InitGame() player->effective_action = 0; player->programmed_action = 0; + player->mouse_action.lx = 0; + player->mouse_action.ly = 0; + player->mouse_action.button = 0; + player->mouse_action.button_hint = 0; + + player->effective_mouse_action.lx = 0; + player->effective_mouse_action.ly = 0; + player->effective_mouse_action.button = 0; + player->effective_mouse_action.button_hint = 0; + player->score = 0; player->score_final = 0; + player->health = MAX_HEALTH; + player->health_final = MAX_HEALTH; + player->gems_still_needed = level.gems_needed; player->sokobanfields_still_needed = 0; player->lights_still_needed = 0; + player->players_still_needed = 0; player->friends_still_needed = 0; for (j = 0; j < MAX_NUM_KEYS; j++) @@ -3211,6 +3451,13 @@ void InitGame() player->is_bored = FALSE; player->is_sleeping = FALSE; + player->was_waiting = TRUE; + player->was_moving = FALSE; + player->was_snapping = FALSE; + player->was_dropping = FALSE; + + player->force_dropping = FALSE; + player->frame_counter_bored = -1; player->frame_counter_sleeping = -1; @@ -3278,22 +3525,21 @@ void InitGame() player->LevelSolved_GameWon = FALSE; player->LevelSolved_GameEnd = FALSE; - player->LevelSolved_PanelOff = FALSE; player->LevelSolved_SaveTape = FALSE; player->LevelSolved_SaveScore = FALSE; + player->LevelSolved_CountingTime = 0; player->LevelSolved_CountingScore = 0; + player->LevelSolved_CountingHealth = 0; map_player_action[i] = i; } network_player_action_received = FALSE; -#if defined(NETWORK_AVALIABLE) /* initial null action */ if (network_playing) SendToServer_MovePlayer(MV_NONE); -#endif ZX = ZY = -1; ExitX = ExitY = -1; @@ -3312,6 +3558,8 @@ void InitGame() AllPlayersGone = FALSE; + game.panel.active = TRUE; + game.no_time_limit = (level.time == 0); game.yamyam_content_nr = 0; @@ -3329,19 +3577,9 @@ void InitGame() game.ball_state = level.ball_state_initial; game.ball_content_nr = 0; - game.envelope_active = FALSE; - - /* set focus to local player for network games, else to all players */ - game.centered_player_nr = (network_playing ? local_player->index_nr : -1); - game.centered_player_nr_next = game.centered_player_nr; - game.set_centered_player = FALSE; + game.explosions_delayed = TRUE; - if (network_playing && tape.recording) - { - /* store client dependent player focus when recording network games */ - tape.centered_player_nr_next = game.centered_player_nr_next; - tape.set_centered_player = TRUE; - } + game.envelope_active = FALSE; for (i = 0; i < NUM_BELTS; i++) { @@ -3353,15 +3591,12 @@ void InitGame() AmoebaCnt[i] = AmoebaCnt2[i] = 0; #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("Player status at level initialization:\n"); - } + DebugPrintPlayerStatus("Player status at level initialization"); #endif SCAN_PLAYFIELD(x, y) { - Feld[x][y] = level.field[x][y]; + Feld[x][y] = Last[x][y] = level.field[x][y]; MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; @@ -3470,22 +3705,33 @@ void InitGame() game.belt_dir_nr[i] = 3; /* not moving, next moving left */ #if USE_NEW_PLAYER_ASSIGNMENTS - /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ - /* choose default local player */ - local_player = &stored_player[0]; - for (i = 0; i < MAX_PLAYERS; i++) + { stored_player[i].connected = FALSE; - local_player->connected = TRUE; - /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ + /* in network game mode, the local player might not be the first player */ + if (stored_player[i].connected_locally) + local_player = &stored_player[i]; + } + + if (!network.enabled) + local_player->connected = TRUE; if (tape.playing) { for (i = 0; i < MAX_PLAYERS; i++) stored_player[i].connected = tape.player_participates[i]; } - else if (game.team_mode && !options.network) + else if (network.enabled) + { + /* add team mode players connected over the network (needed for correct + assignment of player figures from level to locally playing players) */ + + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].connected_network) + stored_player[i].connected = TRUE; + } + else if (game.team_mode) { /* try to guess locally connected team mode players (needed for correct assignment of player figures from level to locally playing players) */ @@ -3497,26 +3743,7 @@ void InitGame() } #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("Player status after level initialization:\n"); - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - printf("- player %d: present == %d, connected == %d, active == %d", - i + 1, - player->present, - player->connected, - player->active); - - if (local_player == player) - printf(" (local player)"); - - printf("\n"); - } - } + DebugPrintPlayerStatus("Player status after level initialization"); #endif #if DEBUG_INIT_PLAYER @@ -3603,26 +3830,7 @@ void InitGame() } #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("Player status after player assignment (first stage):\n"); - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - printf("- player %d: present == %d, connected == %d, active == %d", - i + 1, - player->present, - player->connected, - player->active); - - if (local_player == player) - printf(" (local player)"); - - printf("\n"); - } - } + DebugPrintPlayerStatus("Player status after player assignment (first stage)"); #endif #else @@ -3670,6 +3878,18 @@ void InitGame() printf("::: local_player->present == %d\n", local_player->present); #endif + /* set focus to local player for network games, else to all players */ + game.centered_player_nr = (network_playing ? local_player->index_nr : -1); + game.centered_player_nr_next = game.centered_player_nr; + game.set_centered_player = FALSE; + + if (network_playing && tape.recording) + { + /* store client dependent player focus when recording network games */ + tape.centered_player_nr_next = game.centered_player_nr_next; + tape.set_centered_player = TRUE; + } + if (tape.playing) { /* when playing a tape, eliminate all players who do not participate */ @@ -3715,32 +3935,34 @@ void InitGame() } #endif } - else if (!options.network && !game.team_mode) /* && !tape.playing */ + else if (!network.enabled && !game.team_mode) /* && !tape.playing */ { - /* when in single player mode, eliminate all but the first active player */ + /* when in single player mode, eliminate all but the local player */ for (i = 0; i < MAX_PLAYERS; i++) { - if (stored_player[i].active) + struct PlayerInfo *player = &stored_player[i]; + + if (player->active && player != local_player) { - for (j = i + 1; j < MAX_PLAYERS; j++) - { - if (stored_player[j].active) - { - struct PlayerInfo *player = &stored_player[j]; - int jx = player->jx, jy = player->jy; + int jx = player->jx, jy = player->jy; - player->active = FALSE; - player->present = FALSE; + player->active = FALSE; + player->present = FALSE; - StorePlayer[jx][jy] = 0; - Feld[jx][jy] = EL_EMPTY; - } - } + StorePlayer[jx][jy] = 0; + Feld[jx][jy] = EL_EMPTY; } } } + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].active) + local_player->players_still_needed++; + + if (level.solved_by_one_player) + local_player->players_still_needed = 1; + /* when recording the game, store which players take part in the game */ if (tape.recording) { @@ -3756,26 +3978,7 @@ void InitGame() } #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("Player status after player assignment (final stage):\n"); - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - printf("- player %d: present == %d, connected == %d, active == %d", - i + 1, - player->present, - player->connected, - player->active); - - if (local_player == player) - printf(" (local player)"); - - printf("\n"); - } - } + DebugPrintPlayerStatus("Player status after player assignment (final stage)"); #endif if (BorderElement == EL_EMPTY) @@ -3893,23 +4096,13 @@ void InitGame() } } - scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left : - start_x > SBX_Right + MIDPOSX ? SBX_Right : - start_x - MIDPOSX); - - scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper : - start_y > SBY_Lower + MIDPOSY ? SBY_Lower : - start_y - MIDPOSY); + scroll_x = SCROLL_POSITION_X(start_x); + scroll_y = SCROLL_POSITION_Y(start_y); } else { - scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : - local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : - local_player->jx - MIDPOSX); - - scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : - local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : - local_player->jy - MIDPOSY); + scroll_x = SCROLL_POSITION_X(local_player->jx); + scroll_y = SCROLL_POSITION_Y(local_player->jy); } /* !!! FIX THIS (START) !!! */ @@ -3921,6 +4114,10 @@ void InitGame() { InitGameEngine_SP(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + InitGameEngine_MM(); + } else { DrawLevel(REDRAW_FIELD); @@ -3970,10 +4167,6 @@ void InitGame() FreeGameButtons(); CreateGameButtons(); - game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music; - game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops; - game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple; - MapGameButtons(); MapTapeButtons(); @@ -3982,34 +4175,10 @@ void InitGame() OpenDoor(DOOR_OPEN_ALL); - PlaySound(SND_GAME_STARTING); - - if (setup.sound_music) - PlayLevelMusic(); - KeyboardAutoRepeatOffUnlessAutoplay(); #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("Player status (final):\n"); - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - printf("- player %d: present == %d, connected == %d, active == %d", - i + 1, - player->present, - player->connected, - player->active); - - if (local_player == player) - printf(" (local player)"); - - printf("\n"); - } - } + DebugPrintPlayerStatus("Player status (final)"); #endif } @@ -4026,17 +4195,41 @@ void InitGame() } game.restart_level = FALSE; + game.restart_game_message = NULL; + game.request_active = FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + InitGameActions_MM(); SaveEngineSnapshotToListInitial(); + + if (!game.restart_level) + { + PlaySound(SND_GAME_STARTING); + + if (setup.sound_music) + PlayLevelMusic(); + } } -void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y) +void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y, + int actual_player_x, int actual_player_y) { /* this is used for non-R'n'D game engines to update certain engine values */ + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + actual_player_x = correctLevelPosX_EM(actual_player_x); + actual_player_y = correctLevelPosY_EM(actual_player_y); + } + /* needed to determine if sounds are played within the visible screen area */ scroll_x = actual_scroll_x; scroll_y = actual_scroll_y; + + /* needed to get player position for "follow finger" playing input method */ + local_player->jx = actual_player_x; + local_player->jy = actual_player_y; } void InitMovDir(int x, int y) @@ -4239,25 +4432,40 @@ void InitAmoebaNr(int x, int y) static void PlayerWins(struct PlayerInfo *player) { + if (level.game_engine_type == GAME_ENGINE_TYPE_RND && + local_player->players_still_needed > 0) + return; + player->LevelSolved = TRUE; player->GameOver = TRUE; player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->score : player->score); + level.native_em_level->lev->score : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.score : + player->score); + player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ? + MM_HEALTH(game_mm.laser_overload_value) : + player->health); player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft); player->LevelSolved_CountingScore = player->score_final; + player->LevelSolved_CountingHealth = player->health_final; } -void GameWon() +void GameWon(void) { + static int time_count_steps; static int time, time_final; static int score, score_final; + static int health, health_final; static int game_over_delay_1 = 0; static int game_over_delay_2 = 0; + static int game_over_delay_3 = 0; int game_over_delay_value_1 = 50; - int game_over_delay_value_2 = 50; + int game_over_delay_value_2 = 25; + int game_over_delay_value_3 = 50; if (!local_player->LevelSolved_GameWon) { @@ -4283,24 +4491,42 @@ void GameWon() TapeStop(); - game_over_delay_1 = game_over_delay_value_1; - game_over_delay_2 = game_over_delay_value_2; + game_over_delay_1 = 0; + game_over_delay_2 = 0; + game_over_delay_3 = game_over_delay_value_3; time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft); score = score_final = local_player->score_final; + health = health_final = local_player->health_final; - if (TimeLeft > 0) - { - time_final = 0; - score_final += TimeLeft * level.score[SC_TIME_BONUS]; - } - else if (game.no_time_limit && TimePlayed < 999) + if (level.score[SC_TIME_BONUS] > 0) { - time_final = 999; - score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; - } + if (TimeLeft > 0) + { + time_final = 0; + score_final += TimeLeft * level.score[SC_TIME_BONUS]; + } + else if (game.no_time_limit && TimePlayed < 999) + { + time_final = 999; + score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; + } - local_player->score_final = score_final; + time_count_steps = MAX(1, ABS(time_final - time) / 100); + + game_over_delay_1 = game_over_delay_value_1; + + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + health_final = 0; + score_final += health * level.score[SC_TIME_BONUS]; + + game_over_delay_2 = game_over_delay_value_2; + } + + local_player->score_final = score_final; + local_player->health_final = health_final; + } if (level_editor_test_game) { @@ -4332,8 +4558,8 @@ void GameWon() Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : - element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : - element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: + element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : + element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING: EL_EM_STEEL_EXIT_CLOSING); @@ -4372,7 +4598,9 @@ void GameWon() { int time_to_go = ABS(time_final - time); int time_count_dir = (time < time_final ? +1 : -1); - int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1); + + if (time_to_go < time_count_steps) + time_count_steps = 1; time += time_count_steps * time_count_dir; score += time_count_steps * level.score[SC_TIME_BONUS]; @@ -4395,8 +4623,6 @@ void GameWon() return; } - local_player->LevelSolved_PanelOff = TRUE; - if (game_over_delay_2 > 0) { game_over_delay_2--; @@ -4404,24 +4630,61 @@ void GameWon() return; } + if (health != health_final) + { + int health_count_dir = (health < health_final ? +1 : -1); + + health += health_count_dir; + score += level.score[SC_TIME_BONUS]; + + local_player->LevelSolved_CountingHealth = health; + local_player->LevelSolved_CountingScore = score; + + game_panel_controls[GAME_PANEL_HEALTH].value = health; + game_panel_controls[GAME_PANEL_SCORE].value = score; + + DisplayGameControlValues(); + + if (health == health_final) + StopSound(SND_GAME_LEVELTIME_BONUS); + else if (setup.sound_loops) + PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); + else + PlaySound(SND_GAME_LEVELTIME_BONUS); + + return; + } + + game.panel.active = FALSE; + + if (game_over_delay_3 > 0) + { + game_over_delay_3--; + + return; + } + GameEnd(); } -void GameEnd() +void GameEnd(void) { + /* used instead of "level_nr" (needed for network games) */ + int last_level_nr = levelset.level_nr; int hi_pos; - boolean raise_level = FALSE; local_player->LevelSolved_GameEnd = TRUE; - if (!global.use_envelope_request) - CloseDoor(DOOR_CLOSE_1); - if (local_player->LevelSolved_SaveTape) { - SaveTapeChecked(tape.level_nr); /* ask to save tape */ + /* make sure that request dialog to save tape does not open door again */ + if (!global.use_envelope_request) + CloseDoor(DOOR_CLOSE_1); + + SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */ } + /* if no tape is to be saved, close both doors simultaneously */ CloseDoor(DOOR_CLOSE_ALL); if (level_editor_test_game) @@ -4449,39 +4712,48 @@ void GameEnd() SaveLevelSetup_SeriesInfo(); } - if (level_nr < leveldir_current->last_level) - raise_level = TRUE; /* advance to next level */ - - if ((hi_pos = NewHiScore()) >= 0) + if (setup.increment_levels && + level_nr < leveldir_current->last_level && + !network_playing) { - SetGameStatus(GAME_MODE_SCORES); - - DrawHallOfFame(hi_pos); + level_nr++; /* advance to next level */ + TapeErase(); /* start with empty tape */ - if (raise_level) + if (setup.auto_play_next_level) { - level_nr++; - TapeErase(); + LoadLevel(level_nr); + + SaveLevelSetup_SeriesInfo(); } } + + hi_pos = NewHiScore(last_level_nr); + + if (hi_pos >= 0 && !setup.skip_scores_after_game) + { + SetGameStatus(GAME_MODE_SCORES); + + DrawHallOfFame(last_level_nr, hi_pos); + } + else if (setup.auto_play_next_level && setup.increment_levels && + last_level_nr < leveldir_current->last_level && + !network_playing) + { + StartGameActions(network.enabled, setup.autorecord, level.random_seed); + } else { SetGameStatus(GAME_MODE_MAIN); - if (raise_level) - { - level_nr++; - TapeErase(); - } - DrawMainMenu(); } } -int NewHiScore() +int NewHiScore(int level_nr) { int k, l; int position = -1; + boolean one_score_entry_per_name = !program.many_scores_per_name; LoadScore(level_nr); @@ -4499,13 +4771,15 @@ int NewHiScore() { int m = MAX_SCORE_ENTRIES - 1; -#ifdef ONE_PER_NAME - for (l = k; l < MAX_SCORE_ENTRIES; l++) - if (strEqual(setup.player_name, highscore[l].Name)) - m = l; - if (m == k) /* player's new highscore overwrites his old one */ - goto put_into_list; -#endif + if (one_score_entry_per_name) + { + for (l = k; l < MAX_SCORE_ENTRIES; l++) + if (strEqual(setup.player_name, highscore[l].Name)) + m = l; + + if (m == k) /* player's new highscore overwrites his old one */ + goto put_into_list; + } for (l = m; l > k; l--) { @@ -4514,22 +4788,19 @@ int NewHiScore() } } -#ifdef ONE_PER_NAME put_into_list: -#endif + strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN); highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0'; highscore[k].Score = local_player->score_final; position = k; + break; } - -#ifdef ONE_PER_NAME - else if (!strncmp(setup.player_name, highscore[k].Name, + else if (one_score_entry_per_name && + !strncmp(setup.player_name, highscore[k].Name, MAX_PLAYER_NAME_LEN)) break; /* player already there with a higher score */ -#endif - } if (position >= 0) @@ -4576,11 +4847,14 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) } } -static void ResetGfxFrame(int x, int y, boolean redraw) +static void ResetGfxFrame(int x, int y) { + // profiling showed that "autotest" spends 10~20% of its time in this function + if (DrawingDeactivatedField()) + return; + int element = Feld[x][y]; int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - int last_gfx_frame = GfxFrame[x][y]; if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; @@ -4590,9 +4864,6 @@ static void ResetGfxFrame(int x, int y, boolean redraw) GfxFrame[x][y] = element_info[element].collect_score; else if (ANIM_MODE(graphic) == ANIM_CE_DELAY) GfxFrame[x][y] = ChangeDelay[x][y]; - - if (redraw && GfxFrame[x][y] != last_gfx_frame) - DrawLevelGraphicAnimation(x, y, graphic); } static void ResetGfxAnimation(int x, int y) @@ -4601,7 +4872,7 @@ static void ResetGfxAnimation(int x, int y) GfxDir[x][y] = MovDir[x][y]; GfxFrame[x][y] = 0; - ResetGfxFrame(x, y, FALSE); + ResetGfxFrame(x, y); } static void ResetRandomAnimationValue(int x, int y) @@ -4609,7 +4880,7 @@ static void ResetRandomAnimationValue(int x, int y) GfxRandom[x][y] = INIT_GFX_RANDOM(); } -void InitMovingField(int x, int y, int direction) +static void InitMovingField(int x, int y, int direction) { int element = Feld[x][y]; int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); @@ -4682,7 +4953,7 @@ void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y) *comes_from_y = oldy; } -int MovingOrBlocked2Element(int x, int y) +static int MovingOrBlocked2Element(int x, int y) { int element = Feld[x][y]; @@ -4740,7 +5011,7 @@ static void RemoveField(int x, int y) GfxDir[x][y] = MV_NONE; } -void RemoveMovingField(int x, int y) +static void RemoveMovingField(int x, int y) { int oldx = x, oldy = y, newx = x, newy = y; int element = Feld[x][y]; @@ -4821,7 +5092,7 @@ void DrawDynamite(int x, int y) DrawGraphic(sx, sy, graphic, frame); } -void CheckDynamite(int x, int y) +static void CheckDynamite(int x, int y) { if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */ { @@ -4871,7 +5142,7 @@ static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2) } } -static boolean checkIfAllPlayersFitToScreen_RND() +static boolean checkIfAllPlayersFitToScreen_RND(void) { int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0; @@ -4891,8 +5162,8 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) *sy = (sy1 + sy2) / 2; } -void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, - boolean center_screen, boolean quick_relocation) +static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, + boolean center_screen, boolean quick_relocation) { unsigned int frame_delay_value_old = GetVideoFrameDelay(); boolean ffwd_delay = (tape.playing && tape.fast_forward); @@ -4914,36 +5185,21 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, { /* relocation _with_ centering of screen */ - new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); - - new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); + new_scroll_x = SCROLL_POSITION_X(x); + new_scroll_y = SCROLL_POSITION_Y(y); } else { /* relocation _without_ centering of screen */ - int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : - old_x > SBX_Right + MIDPOSX ? SBX_Right : - old_x - MIDPOSX); - - int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : - old_y > SBY_Lower + MIDPOSY ? SBY_Lower : - old_y - MIDPOSY); - + int center_scroll_x = SCROLL_POSITION_X(old_x); + int center_scroll_y = SCROLL_POSITION_Y(old_y); int offset_x = x + (scroll_x - center_scroll_x); int offset_y = y + (scroll_y - center_scroll_y); - new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : - offset_x > SBX_Right + MIDPOSX ? SBX_Right : - offset_x - MIDPOSX); - - new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : - offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : - offset_y - MIDPOSY); + /* for new screen position, apply previous offset to center position */ + new_scroll_x = SCROLL_POSITION_X(offset_x); + new_scroll_y = SCROLL_POSITION_Y(offset_y); } if (quick_relocation) @@ -4997,7 +5253,7 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, SetVideoFrameDelay(frame_delay_value_old); } -void RelocatePlayer(int jx, int jy, int el_player_raw) +static void RelocatePlayer(int jx, int jy, int el_player_raw) { int el_player = GET_PLAYER_ELEMENT(el_player_raw); int player_nr = GET_PLAYER_NR(el_player); @@ -5102,7 +5358,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) } } -void Explode(int ex, int ey, int phase, int mode) +static void Explode(int ex, int ey, int phase, int mode) { int x, y; int last_phase; @@ -5405,7 +5661,7 @@ void Explode(int ex, int ey, int phase, int mode) } } -void DynaExplode(int ex, int ey) +static void DynaExplode(int ex, int ey) { int i, j; int dynabomb_element = Feld[ex][ey]; @@ -5530,7 +5786,7 @@ void Bang(int x, int y) CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X); } -void SplashAcid(int x, int y) +static void SplashAcid(int x, int y) { if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) && (!IN_LEV_FIELD(x - 1, y - 2) || @@ -5545,7 +5801,7 @@ void SplashAcid(int x, int y) PlayLevelSound(x, y, SND_ACID_SPLASHING); } -static void InitBeltMovement() +static void InitBeltMovement(void) { static int belt_base_element[4] = { @@ -5779,7 +6035,7 @@ static int getInvisibleFromInvisibleActiveElement(int element) element); } -static void RedrawAllLightSwitchesAndInvisibleElements() +static void RedrawAllLightSwitchesAndInvisibleElements(void) { int x, y; @@ -5840,7 +6096,7 @@ static void RedrawAllLightSwitchesAndInvisibleElements() } } -static void RedrawAllInvisibleElementsForLenses() +static void RedrawAllInvisibleElementsForLenses(void) { int x, y; @@ -5889,7 +6145,7 @@ static void RedrawAllInvisibleElementsForLenses() } } -static void RedrawAllInvisibleElementsForMagnifier() +static void RedrawAllInvisibleElementsForMagnifier(void) { int x, y; @@ -5982,7 +6238,7 @@ static void ActivateTimegateSwitch(int x, int y) EL_DC_TIMEGATE_SWITCH_ACTIVE); } -void Impact(int x, int y) +static void Impact(int x, int y) { boolean last_line = (y == lev_fieldy - 1); boolean object_hit = FALSE; @@ -7062,7 +7318,7 @@ static void TurnRound(int x, int y) if (MovDelay[x][y]) GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction); - ResetGfxFrame(x, y, FALSE); + ResetGfxFrame(x, y); } static boolean JustBeingPushed(int x, int y) @@ -7086,7 +7342,7 @@ static boolean JustBeingPushed(int x, int y) return FALSE; } -void StartMoving(int x, int y) +static void StartMoving(int x, int y) { boolean started_moving = FALSE; /* some elements can fall _and_ move */ int element = Feld[x][y]; @@ -8265,7 +8521,7 @@ int AmoebeNachbarNr(int ax, int ay) return group_nr; } -void AmoebenVereinigen(int ax, int ay) +static void AmoebenVereinigen(int ax, int ay) { int i, x, y, xx, yy; int new_group_nr = AmoebaNr[ax][ay]; @@ -8372,7 +8628,7 @@ void AmoebeUmwandeln(int ax, int ay) } } -void AmoebeUmwandelnBD(int ax, int ay, int new_element) +static void AmoebeUmwandelnBD(int ax, int ay, int new_element) { int x, y; int group_nr = AmoebaNr[ax][ay]; @@ -8408,7 +8664,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) SND_BD_AMOEBA_TURNING_TO_GEM)); } -void AmoebeWaechst(int x, int y) +static void AmoebeWaechst(int x, int y) { static unsigned int sound_delay = 0; static unsigned int sound_delay_value = 0; @@ -8444,7 +8700,7 @@ void AmoebeWaechst(int x, int y) } } -void AmoebaDisappearing(int x, int y) +static void AmoebaDisappearing(int x, int y) { static unsigned int sound_delay = 0; static unsigned int sound_delay_value = 0; @@ -8480,7 +8736,7 @@ void AmoebaDisappearing(int x, int y) } } -void AmoebeAbleger(int ax, int ay) +static void AmoebeAbleger(int ax, int ay) { int i; int element = Feld[ax][ay]; @@ -8635,7 +8891,7 @@ void AmoebeAbleger(int ax, int ay) TEST_DrawLevelField(newax, neway); } -void Life(int ax, int ay) +static void Life(int ax, int ay) { int x1, y1, x2, y2; int life_time = 40; @@ -8664,7 +8920,8 @@ void Life(int ax, int ay) for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++) { int xx = ax+x1, yy = ay+y1; - int nachbarn = 0; + int old_element = Feld[xx][yy]; + int num_neighbours = 0; if (!IN_LEV_FIELD(xx, yy)) continue; @@ -8676,33 +8933,48 @@ void Life(int ax, int ay) if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy)) continue; - if (((Feld[x][y] == element || - (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) && - !Stop[x][y]) || - (IS_FREE(x, y) && Stop[x][y])) - nachbarn++; + boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y)); + boolean is_neighbour = FALSE; + + if (level.use_life_bugs) + is_neighbour = + (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) || + (IS_FREE(x, y) && Stop[x][y])); + else + is_neighbour = + (Last[x][y] == element || is_player_cell); + + if (is_neighbour) + num_neighbours++; } + boolean is_free = FALSE; + + if (level.use_life_bugs) + is_free = (IS_FREE(xx, yy)); + else + is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY); + if (xx == ax && yy == ay) /* field in the middle */ { - if (nachbarn < life_parameter[0] || - nachbarn > life_parameter[1]) + if (num_neighbours < life_parameter[0] || + num_neighbours > life_parameter[1]) { Feld[xx][yy] = EL_EMPTY; - if (!Stop[xx][yy]) + if (Feld[xx][yy] != old_element) TEST_DrawLevelField(xx, yy); Stop[xx][yy] = TRUE; changed = TRUE; } } - else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy])) + else if (is_free || CAN_GROW_INTO(Feld[xx][yy])) { /* free border field */ - if (nachbarn >= life_parameter[2] && - nachbarn <= life_parameter[3]) + if (num_neighbours >= life_parameter[2] && + num_neighbours <= life_parameter[3]) { Feld[xx][yy] = element; MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1); - if (!Stop[xx][yy]) + if (Feld[xx][yy] != old_element) TEST_DrawLevelField(xx, yy); Stop[xx][yy] = TRUE; changed = TRUE; @@ -8782,7 +9054,7 @@ static void ActivateMagicBall(int bx, int by) game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents; } -void CheckExit(int x, int y) +static void CheckExit(int x, int y) { if (local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || @@ -8805,7 +9077,7 @@ void CheckExit(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING); } -void CheckExitEM(int x, int y) +static void CheckExitEM(int x, int y) { if (local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || @@ -8828,7 +9100,7 @@ void CheckExitEM(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING); } -void CheckExitSteel(int x, int y) +static void CheckExitSteel(int x, int y) { if (local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || @@ -8851,7 +9123,7 @@ void CheckExitSteel(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING); } -void CheckExitSteelEM(int x, int y) +static void CheckExitSteelEM(int x, int y) { if (local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || @@ -8874,7 +9146,7 @@ void CheckExitSteelEM(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING); } -void CheckExitSP(int x, int y) +static void CheckExitSP(int x, int y) { if (local_player->gems_still_needed > 0) { @@ -8895,7 +9167,7 @@ void CheckExitSP(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING); } -static void CloseAllOpenTimegates() +static void CloseAllOpenTimegates(void) { int x, y; @@ -8912,7 +9184,7 @@ static void CloseAllOpenTimegates() } } -void DrawTwinkleOnField(int x, int y) +static void DrawTwinkleOnField(int x, int y) { if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y)) return; @@ -8939,7 +9211,7 @@ void DrawTwinkleOnField(int x, int y) } } -void MauerWaechst(int x, int y) +static void MauerWaechst(int x, int y) { int delay = 6; @@ -8989,7 +9261,7 @@ void MauerWaechst(int x, int y) } } -void MauerAbleger(int ax, int ay) +static void MauerAbleger(int ax, int ay) { int element = Feld[ax][ay]; int graphic = el2img(element); @@ -9097,7 +9369,7 @@ void MauerAbleger(int ax, int ay) PlayLevelSoundAction(ax, ay, ACTION_GROWING); } -void MauerAblegerStahl(int ax, int ay) +static void MauerAblegerStahl(int ax, int ay) { int element = Feld[ax][ay]; int graphic = el2img(element); @@ -9199,7 +9471,7 @@ void MauerAblegerStahl(int ax, int ay) PlayLevelSoundAction(ax, ay, ACTION_GROWING); } -void CheckForDragon(int x, int y) +static void CheckForDragon(int x, int y) { int i, j; boolean dragon_found = FALSE; @@ -9543,7 +9815,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { for (i = 0; i < MAX_PLAYERS; i++) if (action_arg_player_bits & (1 << i)) - PlayerWins(&stored_player[i]); + ExitPlayer(&stored_player[i]); + + if (AllPlayersGone) + PlayerWins(local_player); break; } @@ -10527,7 +10802,7 @@ static void PlayPlayerSound(struct PlayerInfo *player) } } -static void PlayAllPlayersSound() +static void PlayAllPlayersSound(void) { int i; @@ -10687,32 +10962,28 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) static void CheckSaveEngineSnapshot(struct PlayerInfo *player) { - static boolean player_was_moving = FALSE; - static boolean player_was_snapping = FALSE; - static boolean player_was_dropping = FALSE; - - if ((!player->is_moving && player_was_moving) || - (player->MovPos == 0 && player_was_moving) || - (player->is_snapping && !player_was_snapping) || - (player->is_dropping && !player_was_dropping)) + if ((!player->is_moving && player->was_moving) || + (player->MovPos == 0 && player->was_moving) || + (player->is_snapping && !player->was_snapping) || + (player->is_dropping && !player->was_dropping)) { - if (!SaveEngineSnapshotToList()) + if (!CheckSaveEngineSnapshotToList()) return; - player_was_moving = FALSE; - player_was_snapping = TRUE; - player_was_dropping = TRUE; + player->was_moving = FALSE; + player->was_snapping = TRUE; + player->was_dropping = TRUE; } else { if (player->is_moving) - player_was_moving = TRUE; + player->was_moving = TRUE; if (!player->is_snapping) - player_was_snapping = FALSE; + player->was_snapping = FALSE; if (!player->is_dropping) - player_was_dropping = FALSE; + player->was_dropping = FALSE; } } @@ -10722,7 +10993,9 @@ static void CheckSingleStepMode(struct PlayerInfo *player) { /* as it is called "single step mode", just return to pause mode when the player stopped moving after one tile (or never starts moving at all) */ - if (!player->is_moving && !player->is_pushing) + if (!player->is_moving && + !player->is_pushing && + !player->is_dropping_pressed) { TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); SnapField(player, 0, 0); /* stop snapping */ @@ -10788,11 +11061,30 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) } } -static void CheckLevelTime() +static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action, + byte *tape_action) { - int i; + if (!tape.use_mouse) + return; + + mouse_action->lx = tape_action[TAPE_ACTION_LX]; + mouse_action->ly = tape_action[TAPE_ACTION_LY]; + mouse_action->button = tape_action[TAPE_ACTION_BUTTON]; +} - /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */ +static void SetTapeActionFromMouseAction(byte *tape_action, + struct MouseActionInfo *mouse_action) +{ + if (!tape.use_mouse) + return; + + tape_action[TAPE_ACTION_LX] = mouse_action->lx; + tape_action[TAPE_ACTION_LY] = mouse_action->ly; + tape_action[TAPE_ACTION_BUTTON] = mouse_action->button; +} + +static void CheckLevelSolved(void) +{ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (level.native_em_level->lev->home == 0) /* all players at home */ @@ -10825,6 +11117,26 @@ static void CheckLevelTime() if (game_sp.GameOver) /* game lost */ AllPlayersGone = TRUE; } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + if (game_mm.level_solved && + !game_mm.game_over) /* game won */ + { + PlayerWins(local_player); + + game_mm.game_over = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_mm.game_over) /* game lost */ + AllPlayersGone = TRUE; + } +} + +static void CheckLevelTime(void) +{ + int i; if (TimeFrames >= FRAMES_PER_SECOND) { @@ -10945,19 +11257,18 @@ void StartGameActions(boolean init_network_game, boolean record_tape, if (record_tape) TapeStartRecording(new_random_seed); -#if defined(NETWORK_AVALIABLE) if (init_network_game) { + SendToServer_LevelFile(); SendToServer_StartPlaying(); return; } -#endif InitGame(); } -void GameActions() +static void GameActionsExt(void) { #if 0 static unsigned int game_frame_delay = 0; @@ -10978,51 +11289,19 @@ void GameActions() Error(ERR_WARN, "element '%s' caused endless loop in game engine", EL_NAME(recursion_loop_element)); - RequestQuitGameExt(FALSE, level_editor_test_game, message); - - recursion_loop_detected = FALSE; /* if game should be continued */ - - free(message); - - return; - } - - if (game.restart_level) - StartGameActions(options.network, setup.autorecord, level.random_seed); - - /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */ - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - { - if (level.native_em_level->lev->home == 0) /* all players at home */ - { - PlayerWins(local_player); + RequestQuitGameExt(FALSE, level_editor_test_game, message); - AllPlayersGone = TRUE; + recursion_loop_detected = FALSE; /* if game should be continued */ - level.native_em_level->lev->home = -1; - } + free(message); - if (level.native_em_level->ply[0]->alive == 0 && - level.native_em_level->ply[1]->alive == 0 && - level.native_em_level->ply[2]->alive == 0 && - level.native_em_level->ply[3]->alive == 0) /* all dead */ - AllPlayersGone = TRUE; + return; } - else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - { - if (game_sp.LevelSolved && - !game_sp.GameOver) /* game won */ - { - PlayerWins(local_player); - - game_sp.GameOver = TRUE; - AllPlayersGone = TRUE; - } + if (game.restart_level) + StartGameActions(network.enabled, setup.autorecord, level.random_seed); - if (game_sp.GameOver) /* game lost */ - AllPlayersGone = TRUE; - } + CheckLevelSolved(); if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); @@ -11060,16 +11339,15 @@ void GameActions() { /* try to get network player actions in time */ -#if defined(NETWORK_AVALIABLE) /* last chance to get network player actions without main loop delay */ HandleNetworking(); -#endif /* game was quit by network peer */ if (game_status != GAME_MODE_PLAYING) return; - if (!network_player_action_received) + /* check if network player actions still missing and game still running */ + if (!network_player_action_received && !checkGameEnded()) return; /* failed to get network player actions in time */ /* do not yet reset "network_player_action_received" (for tape.pausing) */ @@ -11085,6 +11363,12 @@ void GameActions() /* when playing tape, read previously recorded player input from tape data */ recorded_player_action = (tape.playing ? TapePlayAction() : NULL); + local_player->effective_mouse_action = local_player->mouse_action; + + if (recorded_player_action != NULL) + SetMouseActionFromTapeAction(&local_player->effective_mouse_action, + recorded_player_action); + /* TapePlayAction() may return NULL when toggling to "pause before death" */ if (tape.pausing) return; @@ -11103,14 +11387,12 @@ void GameActions() stored_player[i].effective_action = stored_player[i].action; } -#if defined(NETWORK_AVALIABLE) - if (network_playing) + if (network_playing && !checkGameEnded()) SendToServer_MovePlayer(summarized_player_action); -#endif // summarize all actions at local players mapped input device position // (this allows using different input devices in single player mode) - if (!options.network && !game.team_mode) + if (!network.enabled && !game.team_mode) stored_player[map_player_action[local_player->index_nr]].effective_action = summarized_player_action; @@ -11141,6 +11423,9 @@ void GameActions() tape.player_participates[i] = TRUE; } + SetTapeActionFromMouseAction(tape_action, + &local_player->effective_mouse_action); + /* only record actions from input devices, but not programmed actions */ if (tape.recording) TapeRecordAction(tape_action); @@ -11205,6 +11490,10 @@ void GameActions() { GameActions_SP_Main(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + GameActions_MM_Main(); + } else { GameActions_RND_Main(); @@ -11212,11 +11501,12 @@ void GameActions() BlitScreenToBitmap(backbuffer); + CheckLevelSolved(); CheckLevelTime(); AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ - if (options.debug) /* calculate frames per second */ + if (global.show_frames_per_second) { static unsigned int fps_counter = 0; static int fps_frames = 0; @@ -11224,19 +11514,42 @@ void GameActions() fps_frames++; - if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */ + if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */ { global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms; fps_frames = 0; fps_counter = Counter(); + + /* always draw FPS to screen after FPS value was updated */ + redraw_mask |= REDRAW_FPS; } - redraw_mask |= REDRAW_FPS; + /* only draw FPS if no screen areas are deactivated (invisible warp mode) */ + if (GetDrawDeactivationMask() == REDRAW_NONE) + redraw_mask |= REDRAW_FPS; } } -void GameActions_EM_Main() +static void GameActions_CheckSaveEngineSnapshot(void) +{ + if (!game.snapshot.save_snapshot) + return; + + // clear flag for saving snapshot _before_ saving snapshot + game.snapshot.save_snapshot = FALSE; + + SaveEngineSnapshotToList(); +} + +void GameActions(void) +{ + GameActionsExt(); + + GameActions_CheckSaveEngineSnapshot(); +} + +void GameActions_EM_Main(void) { byte effective_action[MAX_PLAYERS]; boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); @@ -11248,7 +11561,7 @@ void GameActions_EM_Main() GameActions_EM(effective_action, warp_mode); } -void GameActions_SP_Main() +void GameActions_SP_Main(void) { byte effective_action[MAX_PLAYERS]; boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); @@ -11258,17 +11571,32 @@ void GameActions_SP_Main() effective_action[i] = stored_player[i].effective_action; GameActions_SP(effective_action, warp_mode); + + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].force_dropping) + stored_player[i].action |= KEY_BUTTON_DROP; + + stored_player[i].force_dropping = FALSE; + } +} + +void GameActions_MM_Main(void) +{ + boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + + GameActions_MM(local_player->effective_mouse_action, warp_mode); } -void GameActions_RND_Main() +void GameActions_RND_Main(void) { GameActions_RND(); } -void GameActions_RND() +void GameActions_RND(void) { int magic_wall_x = 0, magic_wall_y = 0; - int i, x, y, element, graphic; + int i, x, y, element, graphic, last_gfx_frame; InitPlayfieldScanModeVars(); @@ -11381,6 +11709,8 @@ void GameActions_RND() SCAN_PLAYFIELD(x, y) { + Last[x][y] = Feld[x][y]; + ChangeCount[x][y] = 0; ChangeEvent[x][y] = -1; @@ -11455,8 +11785,12 @@ void GameActions_RND() { element = Feld[x][y]; graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + last_gfx_frame = GfxFrame[x][y]; + + ResetGfxFrame(x, y); - ResetGfxFrame(x, y, TRUE); + if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y]) + DrawLevelGraphicAnimation(x, y, graphic); if (ANIM_MODE(graphic) == ANIM_RANDOM && IS_NEXT_FRAME(GfxFrame[x][y], graphic)) @@ -11501,8 +11835,12 @@ void GameActions_RND() if (IS_GEM(element) || element == EL_SP_INFOTRON) TEST_DrawTwinkleOnField(x, y); } - else if ((element == EL_ACID || - element == EL_EXIT_OPEN || + else if (element == EL_ACID) + { + if (!Stop[x][y]) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + } + else if ((element == EL_EXIT_OPEN || element == EL_EM_EXIT_OPEN || element == EL_SP_EXIT_OPEN || element == EL_STEEL_EXIT_OPEN || @@ -11800,7 +12138,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY); } -static boolean AllPlayersInVisibleScreen() +static boolean AllPlayersInVisibleScreen(void) { int i; @@ -11967,7 +12305,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, } } - if (!options.network && game.centered_player_nr == -1 && + if (!network.enabled && game.centered_player_nr == -1 && !AllPlayersInSight(player, new_jx, new_jy)) return MP_NO_ACTION; @@ -12154,7 +12492,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) if (scroll_x != old_scroll_x || scroll_y != old_scroll_y) { - if (!options.network && game.centered_player_nr == -1 && + if (!network.enabled && game.centered_player_nr == -1 && !AllPlayersInVisibleScreen()) { scroll_x = old_scroll_x; @@ -12306,12 +12644,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { - DrawPlayer(player); /* needed here only to cleanup last field */ - RemovePlayer(player); + ExitPlayer(player); - if (local_player->friends_still_needed == 0 || - IS_SP_ELEMENT(Feld[jx][jy])) - PlayerWins(player); + if ((local_player->friends_still_needed == 0 || + IS_SP_ELEMENT(Feld[jx][jy])) && + AllPlayersGone) + PlayerWins(local_player); } /* this breaks one level: "machine", level 000 */ @@ -13067,6 +13405,19 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } +void ExitPlayer(struct PlayerInfo *player) +{ + DrawPlayer(player); /* needed here only to cleanup last field */ + RemovePlayer(player); + + if (local_player->players_still_needed > 0) + local_player->players_still_needed--; + + /* also set if some players not yet gone, but not needed to solve level */ + if (local_player->players_still_needed == 0) + AllPlayersGone = TRUE; +} + static void setFieldForSnapping(int x, int y, int element, int direction) { struct ElementInfo *ei = &element_info[element]; @@ -13612,6 +13963,8 @@ static int DigField(struct PlayerInfo *player, if (local_player->sokobanfields_still_needed == 0 && (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) { + local_player->players_still_needed = 0; + PlayerWins(player); PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING); @@ -13961,8 +14314,6 @@ static boolean DropElement(struct PlayerInfo *player) int drop_side = drop_direction; int drop_element = get_next_dropped_element(player); - player->is_dropping_pressed = TRUE; - /* do not drop an element on top of another element; when holding drop key pressed without moving, dropped element must move away before the next element can be dropped (this is especially important if the next element @@ -13990,6 +14341,9 @@ static boolean DropElement(struct PlayerInfo *player) if (new_element == EL_UNDEFINED) return FALSE; + /* only set if player has anything that can be dropped */ + player->is_dropping_pressed = TRUE; + /* check if drop key was pressed long enough for EM style dynamite */ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40) return FALSE; @@ -14093,7 +14447,7 @@ static boolean DropElement(struct PlayerInfo *player) static int *loop_sound_frame = NULL; static int *loop_sound_volume = NULL; -void InitPlayLevelSound() +void InitPlayLevelSound(void) { int num_sounds = getSoundListSize(); @@ -14196,12 +14550,43 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action) StopSound(sound_effect); } -static void PlayLevelMusic() +static int getLevelMusicNr(void) { if (levelset.music[level_nr] != MUS_UNDEFINED) - PlayMusic(levelset.music[level_nr]); /* from config file */ + return levelset.music[level_nr]; /* from config file */ else - PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */ + return MAP_NOCONF_MUSIC(level_nr); /* from music dir */ +} + +static void FadeLevelSounds(void) +{ + FadeSounds(); +} + +static void FadeLevelMusic(void) +{ + int music_nr = getLevelMusicNr(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(music_nr); + + if (!strEqual(curr_music, next_music)) + FadeMusic(); +} + +void FadeLevelSoundsAndMusic(void) +{ + FadeLevelSounds(); + FadeLevelMusic(); +} + +static void PlayLevelMusic(void) +{ + int music_nr = getLevelMusicNr(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(music_nr); + + if (!strEqual(curr_music, next_music)) + PlayMusicLoop(music_nr); } void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) @@ -14375,6 +14760,50 @@ void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp) PlayLevelSoundElementAction(x, y, element, action); } +void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm) +{ + int element = map_element_MM_to_RND(element_mm); + int action = map_action_MM_to_RND(action_mm); + int offset = 0; + int x = xx - offset; + int y = yy - offset; + + if (!IS_MM_ELEMENT(element)) + element = EL_MM_DEFAULT; + + PlayLevelSoundElementAction(x, y, element, action); +} + +void PlaySound_MM(int sound_mm) +{ + int sound = map_sound_MM_to_RND(sound_mm); + + if (sound == SND_UNDEFINED) + return; + + PlaySound(sound); +} + +void PlaySoundLoop_MM(int sound_mm) +{ + int sound = map_sound_MM_to_RND(sound_mm); + + if (sound == SND_UNDEFINED) + return; + + PlaySoundLoop(sound); +} + +void StopSound_MM(int sound_mm) +{ + int sound = map_sound_MM_to_RND(sound_mm); + + if (sound == SND_UNDEFINED) + return; + + StopSound(sound); +} + void RaiseScore(int value) { local_player->score += value; @@ -14472,11 +14901,9 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) if (!skip_request) CloseDoor(DOOR_CLOSE_1); -#if defined(NETWORK_AVALIABLE) - if (options.network) + if (network.enabled) SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); else -#endif { if (quick_quit) FadeSkipNextFadeIn(); @@ -14507,6 +14934,85 @@ void RequestQuitGame(boolean ask_if_really_quit) "Do you really want to quit the game?"); } +void RequestRestartGame(char *message) +{ + game.restart_game_message = NULL; + + boolean has_started_game = hasStartedNetworkGame(); + int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM); + + if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game) + { + StartGameActions(network.enabled, setup.autorecord, level.random_seed); + } + else + { + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); + } +} + +void CheckGameOver(void) +{ + static boolean last_game_over = FALSE; + static int game_over_delay = 0; + int game_over_delay_value = 50; + boolean game_over = checkGameFailed(); + + /* do not handle game over if request dialog is already active */ + if (game.request_active) + return; + + if (!game_over) + { + last_game_over = FALSE; + game_over_delay = game_over_delay_value; + + return; + } + + if (game_over_delay > 0) + { + game_over_delay--; + + return; + } + + if (last_game_over != game_over) + game.restart_game_message = (hasStartedNetworkGame() ? + "Game over! Play it again?" : + "Game over!"); + + last_game_over = game_over; +} + +boolean checkGameSolved(void) +{ + /* set for all game engines if level was solved */ + return local_player->LevelSolved_GameEnd; +} + +boolean checkGameFailed(void) +{ + if (!AllPlayersGone) + return FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return (level.native_em_level->lev->home > 0); + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + return (game_sp.GameOver && !game_sp.LevelSolved); + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + return (game_mm.game_over && !game_mm.level_solved); + else /* GAME_ENGINE_TYPE_RND */ + return (local_player->GameOver && !local_player->LevelSolved); +} + +boolean checkGameEnded(void) +{ + return (checkGameSolved() || checkGameFailed()); +} + /* ------------------------------------------------------------------------- */ /* random generator functions */ @@ -14553,7 +15059,7 @@ static struct EngineSnapshotInfo engine_snapshot_rnd; static char *snapshot_level_identifier = NULL; static int snapshot_level_nr = -1; -static void SaveEngineSnapshotValues_RND() +static void SaveEngineSnapshotValues_RND(void) { static int belt_base_active_element[4] = { @@ -14592,7 +15098,7 @@ static void SaveEngineSnapshotValues_RND() } } -static void LoadEngineSnapshotValues_RND() +static void LoadEngineSnapshotValues_RND(void) { unsigned int num_random_calls = game.num_random_calls; int i, j; @@ -14638,7 +15144,7 @@ static void LoadEngineSnapshotValues_RND() } } -void FreeEngineSnapshotSingle() +void FreeEngineSnapshotSingle(void) { FreeSnapshotSingle(); @@ -14646,12 +15152,12 @@ void FreeEngineSnapshotSingle() snapshot_level_nr = -1; } -void FreeEngineSnapshotList() +void FreeEngineSnapshotList(void) { FreeSnapshotList(); } -ListNode *SaveEngineSnapshotBuffers() +static ListNode *SaveEngineSnapshotBuffers(void) { ListNode *buffers = NULL; @@ -14663,6 +15169,8 @@ ListNode *SaveEngineSnapshotBuffers() SaveEngineSnapshotValues_EM(); if (level.game_engine_type == GAME_ENGINE_TYPE_SP) SaveEngineSnapshotValues_SP(&buffers); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + SaveEngineSnapshotValues_MM(&buffers); /* save values stored in special snapshot structure */ @@ -14672,6 +15180,8 @@ ListNode *SaveEngineSnapshotBuffers() SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); if (level.game_engine_type == GAME_ENGINE_TYPE_SP) SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm)); /* save further RND engine values */ @@ -14756,7 +15266,7 @@ ListNode *SaveEngineSnapshotBuffers() return buffers; } -void SaveEngineSnapshotSingle() +void SaveEngineSnapshotSingle(void) { ListNode *buffers = SaveEngineSnapshotBuffers(); @@ -14768,11 +15278,10 @@ void SaveEngineSnapshotSingle() snapshot_level_nr = level_nr; } -static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot) +boolean CheckSaveEngineSnapshotToList(void) { boolean save_snapshot = - (initial_snapshot || - (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) || + ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) || (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE && game.snapshot.changed_action) || (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT && @@ -14780,33 +15289,31 @@ static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot) game.snapshot.changed_action = FALSE; game.snapshot.collected_item = FALSE; + game.snapshot.save_snapshot = save_snapshot; + + return save_snapshot; +} +void SaveEngineSnapshotToList(void) +{ if (game.snapshot.mode == SNAPSHOT_MODE_OFF || - tape.quick_resume || - !save_snapshot) - return FALSE; + tape.quick_resume) + return; ListNode *buffers = SaveEngineSnapshotBuffers(); /* finally save all snapshot buffers to snapshot list */ SaveSnapshotToList(buffers); - - return TRUE; -} - -boolean SaveEngineSnapshotToList() -{ - return SaveEngineSnapshotToListExt(FALSE); } -void SaveEngineSnapshotToListInitial() +void SaveEngineSnapshotToListInitial(void) { FreeEngineSnapshotList(); - SaveEngineSnapshotToListExt(TRUE); + SaveEngineSnapshotToList(); } -void LoadEngineSnapshotValues() +static void LoadEngineSnapshotValues(void) { /* restore special values from snapshot structure */ @@ -14816,36 +15323,38 @@ void LoadEngineSnapshotValues() LoadEngineSnapshotValues_EM(); if (level.game_engine_type == GAME_ENGINE_TYPE_SP) LoadEngineSnapshotValues_SP(); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + LoadEngineSnapshotValues_MM(); } -void LoadEngineSnapshotSingle() +void LoadEngineSnapshotSingle(void) { LoadSnapshotSingle(); LoadEngineSnapshotValues(); } -void LoadEngineSnapshot_Undo(int steps) +static void LoadEngineSnapshot_Undo(int steps) { LoadSnapshotFromList_Older(steps); LoadEngineSnapshotValues(); } -void LoadEngineSnapshot_Redo(int steps) +static void LoadEngineSnapshot_Redo(int steps) { LoadSnapshotFromList_Newer(steps); LoadEngineSnapshotValues(); } -boolean CheckEngineSnapshotSingle() +boolean CheckEngineSnapshotSingle(void) { return (strEqual(snapshot_level_identifier, leveldir_current->identifier) && snapshot_level_nr == level_nr); } -boolean CheckEngineSnapshotList() +boolean CheckEngineSnapshotList(void) { return CheckSnapshotList(); } @@ -14858,69 +15367,115 @@ static struct int graphic; struct XY *pos; int gadget_id; + boolean *setup_value; + boolean allowed_on_tape; char *infotext; } gamebutton_info[NUM_GAME_BUTTONS] = { { - IMG_GFX_GAME_BUTTON_STOP, &game.button.stop, - GAME_CTRL_ID_STOP, "stop game" + IMG_GFX_GAME_BUTTON_STOP, &game.button.stop, + GAME_CTRL_ID_STOP, NULL, + TRUE, "stop game" + }, + { + IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause, + GAME_CTRL_ID_PAUSE, NULL, + TRUE, "pause game" + }, + { + IMG_GFX_GAME_BUTTON_PLAY, &game.button.play, + GAME_CTRL_ID_PLAY, NULL, + TRUE, "play game" + }, + { + IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo, + GAME_CTRL_ID_UNDO, NULL, + TRUE, "undo step" + }, + { + IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, + GAME_CTRL_ID_REDO, NULL, + TRUE, "redo step" }, { - IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause, - GAME_CTRL_ID_PAUSE, "pause game" + IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, + GAME_CTRL_ID_SAVE, NULL, + TRUE, "save game" }, { - IMG_GFX_GAME_BUTTON_PLAY, &game.button.play, - GAME_CTRL_ID_PLAY, "play game" + IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, + GAME_CTRL_ID_PAUSE2, NULL, + TRUE, "pause game" }, { - IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo, - GAME_CTRL_ID_UNDO, "undo step" + IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, + GAME_CTRL_ID_LOAD, NULL, + TRUE, "load game" }, { - IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, - GAME_CTRL_ID_REDO, "redo step" + IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop, + GAME_CTRL_ID_PANEL_STOP, NULL, + FALSE, "stop game" }, { - IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, - GAME_CTRL_ID_SAVE, "save game" + IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause, + GAME_CTRL_ID_PANEL_PAUSE, NULL, + FALSE, "pause game" }, { - IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, - GAME_CTRL_ID_PAUSE2, "pause game" + IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play, + GAME_CTRL_ID_PANEL_PLAY, NULL, + FALSE, "play game" }, { - IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, - GAME_CTRL_ID_LOAD, "load game" + IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, + SOUND_CTRL_ID_MUSIC, &setup.sound_music, + TRUE, "background music on/off" }, { - IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, - SOUND_CTRL_ID_MUSIC, "background music on/off" + IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, + SOUND_CTRL_ID_LOOPS, &setup.sound_loops, + TRUE, "sound loops on/off" }, { - IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, - SOUND_CTRL_ID_LOOPS, "sound loops on/off" + IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, + SOUND_CTRL_ID_SIMPLE, &setup.sound_simple, + TRUE, "normal sounds on/off" }, { - IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, - SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" + IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music, + SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music, + FALSE, "background music on/off" + }, + { + IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops, + SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops, + FALSE, "sound loops on/off" + }, + { + IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple, + SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple, + FALSE, "normal sounds on/off" } }; -void CreateGameButtons() +void CreateGameButtons(void) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) { - struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic]; + int graphic = gamebutton_info[i].graphic; + struct GraphicInfo *gfx = &graphic_info[graphic]; struct XY *pos = gamebutton_info[i].pos; struct GadgetInfo *gi; int button_type; boolean checked; unsigned int event_mask; - int base_x = (tape.show_game_buttons ? VX : DX); - int base_y = (tape.show_game_buttons ? VY : DY); + boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape; + boolean on_tape = (tape.show_game_buttons && allowed_on_tape); + int base_x = (on_tape ? VX : DX); + int base_y = (on_tape ? VY : DY); int gd_x = gfx->src_x; int gd_y = gfx->src_y; int gd_xp = gfx->src_x + gfx->pressed_xoffset; @@ -14939,7 +15494,9 @@ void CreateGameButtons() } if (id == GAME_CTRL_ID_STOP || + id == GAME_CTRL_ID_PANEL_STOP || id == GAME_CTRL_ID_PLAY || + id == GAME_CTRL_ID_PANEL_PLAY || id == GAME_CTRL_ID_SAVE || id == GAME_CTRL_ID_LOAD) { @@ -14957,14 +15514,13 @@ void CreateGameButtons() else { button_type = GD_TYPE_CHECK_BUTTON; - checked = - ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) || - (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) || - (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE); + checked = (gamebutton_info[i].setup_value != NULL ? + *gamebutton_info[i].setup_value : FALSE); event_mask = GD_EVENT_PRESSED; } gi = CreateGadget(GDI_CUSTOM_ID, id, + GDI_IMAGE_ID, graphic, GDI_INFO_TEXT, gamebutton_info[i].infotext, GDI_X, base_x + GDI_ACTIVE_POS(pos->x), GDI_Y, base_y + GDI_ACTIVE_POS(pos->y), @@ -14989,7 +15545,7 @@ void CreateGameButtons() } } -void FreeGameButtons() +void FreeGameButtons(void) { int i; @@ -14997,7 +15553,7 @@ void FreeGameButtons() FreeGadget(game_gadget[i]); } -static void MapGameButtonsAtSamePosition(int id) +static void UnmapGameButtonsAtSamePosition(int id) { int i; @@ -15005,10 +15561,30 @@ static void MapGameButtonsAtSamePosition(int id) if (i != id && gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) - MapGadget(game_gadget[i]); + UnmapGadget(game_gadget[i]); } -static void UnmapGameButtonsAtSamePosition(int id) +static void UnmapGameButtonsAtSamePosition_All(void) +{ + if (setup.show_snapshot_buttons) + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); + } + else + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY); + + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY); + } +} + +static void MapGameButtonsAtSamePosition(int id) { int i; @@ -15016,72 +15592,122 @@ static void UnmapGameButtonsAtSamePosition(int id) if (i != id && gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) - UnmapGadget(game_gadget[i]); + MapGadget(game_gadget[i]); + + UnmapGameButtonsAtSamePosition_All(); } -void MapUndoRedoButtons() +void MapUndoRedoButtons(void) { UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); MapGadget(game_gadget[GAME_CTRL_ID_UNDO]); MapGadget(game_gadget[GAME_CTRL_ID_REDO]); + + ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END); } -void UnmapUndoRedoButtons() +void UnmapUndoRedoButtons(void) { UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]); UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]); MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + + ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END); } -void MapGameButtons() +static void MapGameButtonsExt(boolean on_tape) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - if (i != GAME_CTRL_ID_UNDO && + if ((!on_tape || gamebutton_info[i].allowed_on_tape) && + i != GAME_CTRL_ID_UNDO && i != GAME_CTRL_ID_REDO) MapGadget(game_gadget[i]); - if (setup.show_snapshot_buttons) - { - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); - } - else - { - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP); - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE); - UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY); - } + UnmapGameButtonsAtSamePosition_All(); RedrawGameButtons(); } -void UnmapGameButtons() +static void UnmapGameButtonsExt(boolean on_tape) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - UnmapGadget(game_gadget[i]); + if (!on_tape || gamebutton_info[i].allowed_on_tape) + UnmapGadget(game_gadget[i]); } -void RedrawGameButtons() +static void RedrawGameButtonsExt(boolean on_tape) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - RedrawGadget(game_gadget[i]); + if (!on_tape || gamebutton_info[i].allowed_on_tape) + RedrawGadget(game_gadget[i]); // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area redraw_mask &= ~REDRAW_ALL; } -void GameUndoRedoExt() +static void SetGadgetState(struct GadgetInfo *gi, boolean state) +{ + if (gi == NULL) + return; + + gi->checked = state; +} + +static void RedrawSoundButtonGadget(int id) +{ + int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC : + id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS : + id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE : + id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC : + id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS : + id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE : + id); + + SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value); + RedrawGadget(game_gadget[id2]); +} + +void MapGameButtons(void) +{ + MapGameButtonsExt(FALSE); +} + +void UnmapGameButtons(void) +{ + UnmapGameButtonsExt(FALSE); +} + +void RedrawGameButtons(void) +{ + RedrawGameButtonsExt(FALSE); +} + +void MapGameButtonsOnTape(void) +{ + MapGameButtonsExt(TRUE); +} + +void UnmapGameButtonsOnTape(void) +{ + UnmapGameButtonsExt(TRUE); +} + +void RedrawGameButtonsOnTape(void) +{ + RedrawGameButtonsExt(TRUE); +} + +static void GameUndoRedoExt(void) { ClearPlayerAction(); @@ -15093,13 +15719,12 @@ void GameUndoRedoExt() DrawCompleteVideoDisplay(); DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); - DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON : - VIDEO_STATE_1STEP_OFF), 0); + DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0); BackToFront(); } -void GameUndo(int steps) +static void GameUndo(int steps) { if (!CheckEngineSnapshotList()) return; @@ -15109,7 +15734,7 @@ void GameUndo(int steps) GameUndoRedoExt(); } -void GameRedo(int steps) +static void GameRedo(int steps) { if (!CheckEngineSnapshotList()) return; @@ -15133,6 +15758,7 @@ static void HandleGameButtonsExt(int id, int button) switch (id) { case GAME_CTRL_ID_STOP: + case GAME_CTRL_ID_PANEL_STOP: if (game_status == GAME_MODE_MAIN) break; @@ -15145,14 +15771,13 @@ static void HandleGameButtonsExt(int id, int button) case GAME_CTRL_ID_PAUSE: case GAME_CTRL_ID_PAUSE2: - if (options.network && game_status == GAME_MODE_PLAYING) + case GAME_CTRL_ID_PANEL_PAUSE: + if (network.enabled && game_status == GAME_MODE_PLAYING) { -#if defined(NETWORK_AVALIABLE) if (tape.pausing) SendToServer_ContinuePlaying(); else SendToServer_PausePlaying(); -#endif } else TapeTogglePause(TAPE_TOGGLE_MANUAL); @@ -15162,18 +15787,17 @@ static void HandleGameButtonsExt(int id, int button) break; case GAME_CTRL_ID_PLAY: + case GAME_CTRL_ID_PANEL_PLAY: if (game_status == GAME_MODE_MAIN) { - StartGameActions(options.network, setup.autorecord, level.random_seed); + StartGameActions(network.enabled, setup.autorecord, level.random_seed); } else if (tape.pausing) { -#if defined(NETWORK_AVALIABLE) - if (options.network) + if (network.enabled) SendToServer_ContinuePlaying(); else -#endif - TapeTogglePause(TAPE_TOGGLE_MANUAL); + TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE); } break; @@ -15205,6 +15829,7 @@ static void HandleGameButtonsExt(int id, int button) break; case SOUND_CTRL_ID_MUSIC: + case SOUND_CTRL_ID_PANEL_MUSIC: if (setup.sound_music) { setup.sound_music = FALSE; @@ -15217,11 +15842,16 @@ static void HandleGameButtonsExt(int id, int button) SetAudioMode(setup.sound); - PlayLevelMusic(); + if (game_status == GAME_MODE_PLAYING) + PlayLevelMusic(); } + + RedrawSoundButtonGadget(id); + break; case SOUND_CTRL_ID_LOOPS: + case SOUND_CTRL_ID_PANEL_LOOPS: if (setup.sound_loops) setup.sound_loops = FALSE; else if (audio.loops_available) @@ -15230,9 +15860,13 @@ static void HandleGameButtonsExt(int id, int button) SetAudioMode(setup.sound); } + + RedrawSoundButtonGadget(id); + break; case SOUND_CTRL_ID_SIMPLE: + case SOUND_CTRL_ID_PANEL_SIMPLE: if (setup.sound_simple) setup.sound_simple = FALSE; else if (audio.sound_available) @@ -15241,6 +15875,9 @@ static void HandleGameButtonsExt(int id, int button) SetAudioMode(setup.sound); } + + RedrawSoundButtonGadget(id); + break; default: @@ -15255,7 +15892,6 @@ static void HandleGameButtons(struct GadgetInfo *gi) void HandleSoundButtonKeys(Key key) { - if (key == setup.shortcut.sound_simple) ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON); else if (key == setup.shortcut.sound_loops)