X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=c51b7ece8e596108139c1ebc94670c66497faa6a;hp=a3ad6f14842b48d363b52286776b2ffa97ffd748;hb=6687b5314f1e08eb7364c0c5d753b96db18f6089;hpb=7b3991f74b0e0dc6774453a59e674696560c2d93 diff --git a/src/game.c b/src/game.c index a3ad6f14..c51b7ece 100644 --- a/src/game.c +++ b/src/game.c @@ -122,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 { @@ -219,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; @@ -402,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, @@ -976,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 @@ -985,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 */ @@ -1062,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); @@ -1078,6 +1112,7 @@ void TestIfGoodThingGetsHitByBadThing(int, int, int); void KillPlayer(struct PlayerInfo *); void BuryPlayer(struct PlayerInfo *); void RemovePlayer(struct PlayerInfo *); +void RemovePlayerWithCleanup(struct PlayerInfo *); static int getInvisibleActiveFromInvisibleElement(int); static int getInvisibleFromInvisibleActiveElement(int); @@ -1697,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; @@ -2150,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 : @@ -2157,19 +2194,31 @@ 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(); @@ -2260,6 +2309,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 = @@ -2412,6 +2469,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; } @@ -2535,6 +2622,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); @@ -2702,6 +2877,9 @@ static void InitGameEngine() 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 */ @@ -3057,6 +3235,11 @@ static void InitGameEngine() 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) @@ -3091,6 +3274,36 @@ int get_num_special_action(int element, int action_first, int action_last) ============================================================================= */ +#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() { int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0); @@ -3116,29 +3329,30 @@ void InitGame() else FadeSetEnterScreen(); - if (CheckIfGlobalBorderHasChanged()) + if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) fade_mask = REDRAW_ALL; FadeLevelSoundsAndMusic(); ExpireSoundLoops(TRUE); - FadeOut(fade_mask); + if (!level_editor_test_game) + FadeOut(fade_mask); /* needed if different viewport properties defined for playing */ ChangeViewportPropertiesIfNeeded(); ClearField(); - OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); - 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++) { @@ -3159,9 +3373,22 @@ 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; @@ -3297,19 +3524,19 @@ void InitGame() 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; @@ -3347,18 +3574,6 @@ void InitGame() 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; - - 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; - } - for (i = 0; i < NUM_BELTS; i++) { game.belt_dir[i] = MV_NONE; @@ -3369,10 +3584,7 @@ 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) @@ -3486,22 +3698,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) */ @@ -3513,26 +3736,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 @@ -3619,26 +3823,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 @@ -3686,6 +3871,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 */ @@ -3731,28 +3928,23 @@ 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; } } } @@ -3772,26 +3964,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) @@ -3927,6 +4100,10 @@ void InitGame() { InitGameEngine_SP(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + InitGameEngine_MM(); + } else { DrawLevel(REDRAW_FIELD); @@ -3976,10 +4153,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(); @@ -3988,34 +4161,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 } @@ -4032,8 +4181,20 @@ void InitGame() } game.restart_level = FALSE; + game.restart_game_message = NULL; + + 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, @@ -4260,21 +4421,32 @@ static void PlayerWins(struct PlayerInfo *player) 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() { + 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) { @@ -4300,24 +4472,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]; + } + + time_count_steps = MAX(1, ABS(time_final - time) / 100); - local_player->score_final = score_final; + 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) { @@ -4389,7 +4579,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]; @@ -4412,8 +4604,6 @@ void GameWon() return; } - local_player->LevelSolved_PanelOff = TRUE; - if (game_over_delay_2 > 0) { game_over_delay_2--; @@ -4421,24 +4611,60 @@ 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; + } + + local_player->LevelSolved_PanelOff = TRUE; + + if (game_over_delay_3 > 0) + { + game_over_delay_3--; + + return; + } + GameEnd(); } void GameEnd() { int hi_pos; - boolean raise_level = FALSE; + int last_level_nr = level_nr; 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) @@ -4468,35 +4694,39 @@ void GameEnd() if (setup.increment_levels && level_nr < leveldir_current->last_level) - raise_level = TRUE; /* advance to next level */ - - if ((hi_pos = NewHiScore()) >= 0) { - 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(); } } - else + + hi_pos = NewHiScore(last_level_nr); + + if (hi_pos >= 0 && !setup.skip_scores_after_game) { - SetGameStatus(GAME_MODE_MAIN); + SetGameStatus(GAME_MODE_SCORES); - if (raise_level) - { - level_nr++; - TapeErase(); - } + DrawHallOfFame(last_level_nr, hi_pos); + } + else if (!setup.auto_play_next_level || !setup.increment_levels) + { + SetGameStatus(GAME_MODE_MAIN); DrawMainMenu(); } + else + { + StartGameActions(network.enabled, setup.autorecord, level.random_seed); + } } -int NewHiScore() +int NewHiScore(int level_nr) { int k, l; int position = -1; @@ -4596,6 +4826,10 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) 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]); @@ -9542,7 +9776,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]); + RemovePlayerWithCleanup(&stored_player[i]); + + if (AllPlayersGone) + PlayerWins(local_player); break; } @@ -10785,6 +11022,28 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) } } +static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action, + byte *tape_action) +{ + 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]; +} + +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 CheckLevelTime() { int i; @@ -10822,6 +11081,21 @@ 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; + } if (TimeFrames >= FRAMES_PER_SECOND) { @@ -10942,14 +11216,12 @@ 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_StartPlaying(); return; } -#endif InitGame(); } @@ -10985,7 +11257,7 @@ void GameActionsExt() } if (game.restart_level) - StartGameActions(options.network, setup.autorecord, level.random_seed); + StartGameActions(network.enabled, setup.autorecord, level.random_seed); /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) @@ -11020,6 +11292,21 @@ void GameActionsExt() 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; + } if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); @@ -11057,10 +11344,8 @@ void GameActionsExt() { /* 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) @@ -11082,6 +11367,12 @@ void GameActionsExt() /* 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; @@ -11100,14 +11391,12 @@ void GameActionsExt() stored_player[i].effective_action = stored_player[i].action; } -#if defined(NETWORK_AVALIABLE) if (network_playing) 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; @@ -11138,6 +11427,9 @@ void GameActionsExt() 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); @@ -11202,6 +11494,10 @@ void GameActionsExt() { GameActions_SP_Main(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + GameActions_MM_Main(); + } else { GameActions_RND_Main(); @@ -11288,6 +11584,13 @@ void GameActions_SP_Main() } } +void GameActions_MM_Main() +{ + boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + + GameActions_MM(local_player->effective_mouse_action, warp_mode); +} + void GameActions_RND_Main() { GameActions_RND(); @@ -12003,7 +12306,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; @@ -12190,7 +12493,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; @@ -12342,12 +12645,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); + RemovePlayerWithCleanup(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 */ @@ -13103,6 +13406,12 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } +void RemovePlayerWithCleanup(struct PlayerInfo *player) +{ + DrawPlayer(player); /* needed here only to cleanup last field */ + RemovePlayer(player); +} + static void setFieldForSnapping(int x, int y, int element, int direction) { struct ElementInfo *ei = &element_info[element]; @@ -14443,6 +14752,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; @@ -14540,11 +14893,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(); @@ -14575,6 +14926,22 @@ void RequestQuitGame(boolean ask_if_really_quit) "Do you really want to quit the game?"); } +void RequestRestartGame(char *message) +{ + game.restart_game_message = NULL; + + if (Request(message, REQ_ASK | REQ_STAY_CLOSED)) + { + StartGameActions(network.enabled, setup.autorecord, level.random_seed); + } + else + { + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); + } +} + /* ------------------------------------------------------------------------- */ /* random generator functions */ @@ -14731,6 +15098,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 */ @@ -14740,6 +15109,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 */ @@ -14881,6 +15252,8 @@ 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() @@ -14923,52 +15296,95 @@ 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, "pause 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, "play 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, "undo step" + 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, "redo step" + IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, + GAME_CTRL_ID_REDO, NULL, + TRUE, "redo step" }, { - IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, - GAME_CTRL_ID_SAVE, "save game" + IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, + GAME_CTRL_ID_SAVE, NULL, + TRUE, "save game" }, { - IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, - GAME_CTRL_ID_PAUSE2, "pause game" + IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, + GAME_CTRL_ID_PAUSE2, NULL, + TRUE, "pause game" }, { - IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, - GAME_CTRL_ID_LOAD, "load game" + IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, + GAME_CTRL_ID_LOAD, NULL, + TRUE, "load game" }, { - IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, - SOUND_CTRL_ID_MUSIC, "background music on/off" + IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop, + GAME_CTRL_ID_PANEL_STOP, NULL, + FALSE, "stop game" }, { - IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, - SOUND_CTRL_ID_LOOPS, "sound loops on/off" + IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause, + GAME_CTRL_ID_PANEL_PAUSE, NULL, + FALSE, "pause game" }, { - IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, - SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" + IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play, + GAME_CTRL_ID_PANEL_PLAY, NULL, + FALSE, "play 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_LOOPS, &game.button.sound_loops, + SOUND_CTRL_ID_LOOPS, &setup.sound_loops, + TRUE, "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_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" } }; @@ -14978,14 +15394,17 @@ void CreateGameButtons() 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; @@ -15004,7 +15423,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) { @@ -15022,14 +15443,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), @@ -15086,6 +15506,10 @@ static void UnmapGameButtonsAtSamePosition_All() 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); } } @@ -15124,12 +15548,13 @@ void UnmapUndoRedoButtons() ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END); } -void MapGameButtons() +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]); @@ -15138,25 +15563,79 @@ void MapGameButtons() RedrawGameButtons(); } -void UnmapGameButtons() +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() +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 SetGadgetState(struct GadgetInfo *gi, boolean state) +{ + if (gi == NULL) + return; + + gi->checked = state; +} + +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() +{ + MapGameButtonsExt(FALSE); +} + +void UnmapGameButtons() +{ + UnmapGameButtonsExt(FALSE); +} + +void RedrawGameButtons() +{ + RedrawGameButtonsExt(FALSE); +} + +void MapGameButtonsOnTape() +{ + MapGameButtonsExt(TRUE); +} + +void UnmapGameButtonsOnTape() +{ + UnmapGameButtonsExt(TRUE); +} + +void RedrawGameButtonsOnTape() +{ + RedrawGameButtonsExt(TRUE); +} + void GameUndoRedoExt() { ClearPlayerAction(); @@ -15208,6 +15687,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; @@ -15220,14 +15700,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); @@ -15237,17 +15716,16 @@ 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 | TAPE_TOGGLE_PLAY_PAUSE); } break; @@ -15280,6 +15758,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; @@ -15292,11 +15771,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) @@ -15305,9 +15789,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) @@ -15316,6 +15804,9 @@ static void HandleGameButtonsExt(int id, int button) SetAudioMode(setup.sound); } + + RedrawSoundButtonGadget(id); + break; default: @@ -15330,7 +15821,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)