+ case EL_SOKOBAN_FIELD_EMPTY:
+ local_player->sokobanfields_still_needed++;
+ break;
+
+ case EL_PENGUIN:
+ local_player->friends_still_needed++;
+ break;
+
+ case EL_PIG:
+ case EL_DRAGON:
+ MovDir[x][y] = 1 << RND(4);
+ break;
+
+#if 0
+ case EL_SP_EMPTY:
+ Feld[x][y] = EL_EMPTY;
+ break;
+#endif
+
+#if 0
+ case EL_EM_KEY_1_FILE:
+ Feld[x][y] = EL_EM_KEY_1;
+ break;
+ case EL_EM_KEY_2_FILE:
+ Feld[x][y] = EL_EM_KEY_2;
+ break;
+ case EL_EM_KEY_3_FILE:
+ Feld[x][y] = EL_EM_KEY_3;
+ break;
+ case EL_EM_KEY_4_FILE:
+ Feld[x][y] = EL_EM_KEY_4;
+ break;
+#endif
+
+ case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
+ case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
+ case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
+ case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
+ case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
+ case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
+ case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
+ case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
+ case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
+ case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
+ case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
+ case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
+ if (init_game)
+ {
+ int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
+ int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
+ int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
+
+ if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
+ {
+ game.belt_dir[belt_nr] = belt_dir;
+ game.belt_dir_nr[belt_nr] = belt_dir_nr;
+ }
+ else /* more than one switch -- set it like the first switch */
+ {
+ Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
+ }
+ }
+ break;
+
+ case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
+ if (init_game)
+ Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
+ break;
+
+ case EL_LIGHT_SWITCH_ACTIVE:
+ if (init_game)
+ game.light_time_left = level.time_light * FRAMES_PER_SECOND;
+ break;
+
+ default:
+ if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
+ InitMovDir(x, y);
+ break;
+ }
+}
+
+void DrawGameDoorValues()
+{
+ int i, j;
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ for (j=0; j<4; j++)
+ if (stored_player[i].key[j])
+ DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
+ el2edimg(EL_KEY_1 + j));
+
+ DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
+ int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
+ DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
+ int2str(local_player->inventory_size, 3), FONT_TEXT_2);
+ DrawText(DX + XX_SCORE, DY + YY_SCORE,
+ int2str(local_player->score, 5), FONT_TEXT_2);
+ DrawText(DX + XX_TIME, DY + YY_TIME,
+ int2str(TimeLeft, 3), FONT_TEXT_2);
+}
+
+
+/*
+ =============================================================================
+ InitGameEngine()
+ -----------------------------------------------------------------------------
+ initialize game engine due to level / tape version number
+ =============================================================================
+*/
+
+static void InitGameEngine()
+{
+ int i, j, k;
+
+ /* set game engine from tape file when re-playing, else from level file */
+ game.engine_version = (tape.playing ? tape.engine_version :
+ level.game_version);
+
+ /* dynamically adjust element properties according to game engine version */
+ InitElementPropertiesEngine(game.engine_version);
+
+#if 0
+ printf("level %d: level version == %06d\n", level_nr, level.game_version);
+ printf(" tape version == %06d [%s] [file: %06d]\n",
+ tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
+ tape.file_version);
+ printf(" => game.engine_version == %06d\n", game.engine_version);
+#endif
+
+ /* ---------- initialize player's initial move delay --------------------- */
+
+ /* dynamically adjust player properties according to game engine version */
+ game.initial_move_delay =
+ (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
+ INITIAL_MOVE_DELAY_OFF);
+
+ /* dynamically adjust player properties according to level information */
+ game.initial_move_delay_value =
+ (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+
+ /* ---------- initialize changing elements ------------------------------- */
+
+ /* initialize changing elements information */
+ for (i=0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ /* this pointer might have been changed in the level editor */
+ ei->change = &ei->change_page[0];
+
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ ei->change->target_element = EL_EMPTY_SPACE;
+ ei->change->delay_fixed = 0;
+ ei->change->delay_random = 0;
+ ei->change->delay_frames = 1;
+ }
+
+ ei->change_events = CE_BITMASK_DEFAULT;
+ for (j=0; j < NUM_CHANGE_EVENTS; j++)
+ {
+ ei->event_page_nr[j] = 0;
+ ei->event_page[j] = &ei->change_page[0];
+ }
+ }
+
+ /* add changing elements from pre-defined list */
+ for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
+ {
+ struct ChangingElementInfo *ch_delay = &change_delay_list[i];
+ struct ElementInfo *ei = &element_info[ch_delay->element];
+
+ ei->change->target_element = ch_delay->target_element;
+ ei->change->delay_fixed = ch_delay->change_delay;
+
+ ei->change->pre_change_function = ch_delay->pre_change_function;
+ ei->change->change_function = ch_delay->change_function;
+ ei->change->post_change_function = ch_delay->post_change_function;
+
+ ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+ }
+
+#if 1
+ /* add change events from custom element configuration */
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
+
+ for (j=0; j < ei->num_change_pages; j++)
+ {
+ if (!ei->change_page[j].can_change)
+ continue;
+
+ for (k=0; k < NUM_CHANGE_EVENTS; k++)
+ {
+ /* only add event page for the first page found with this event */
+ if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
+ !(ei->change_events & CH_EVENT_BIT(k)))
+ {
+ ei->change_events |= CH_EVENT_BIT(k);
+ ei->event_page_nr[k] = j;
+ ei->event_page[k] = &ei->change_page[j];
+ }
+ }
+ }
+ }
+
+#else
+
+ /* add change events from custom element configuration */
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ /* only add custom elements that change after fixed/random frame delay */
+ if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
+ element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
+ }
+#endif
+
+ /* ---------- initialize trigger events ---------------------------------- */
+
+ /* initialize trigger events information */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ trigger_events[i] = EP_BITMASK_DEFAULT;
+
+#if 1
+ /* add trigger events from element change event properties */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ for (j=0; j < ei->num_change_pages; j++)
+ {
+ if (!ei->change_page->can_change)
+ continue;
+
+ if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
+ {
+ int trigger_element = ei->change_page[j].trigger_element;
+
+ trigger_events[trigger_element] |= ei->change_page[j].events;
+ }
+ }
+ }
+#else
+ /* add trigger events from element change event properties */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
+ trigger_events[element_info[i].change->trigger_element] |=
+ element_info[i].change->events;
+#endif
+
+ /* ---------- initialize push delay -------------------------------------- */
+
+ /* initialize push delay values to default */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 8;
+ }
+ }
+
+ /* set push delay value for certain elements from pre-defined list */
+ for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
+ {
+ int e = push_delay_list[i].element;
+
+ element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
+ element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
+ }
+
+ /* ---------- initialize move stepsize ----------------------------------- */
+
+ /* initialize move stepsize values to default */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
+
+ /* set move stepsize value for certain elements from pre-defined list */
+ for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
+ {
+ int e = move_stepsize_list[i].element;
+
+ element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
+ }
+
+ /* ---------- initialize gem count --------------------------------------- */
+
+ /* initialize gem count values for each element */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].collect_count = 0;
+
+ /* add gem count values for all elements from pre-defined list */
+ for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
+ element_info[collect_count_list[i].element].collect_count =
+ collect_count_list[i].count;
+}
+
+
+/*
+ =============================================================================
+ InitGame()
+ -----------------------------------------------------------------------------
+ initialize and start new game
+ =============================================================================
+*/
+
+void InitGame()
+{
+ boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
+ boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
+ boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
+ int i, j, x, y;
+
+ InitGameEngine();
+
+#if 0
+#if DEBUG
+#if USE_NEW_AMOEBA_CODE
+ printf("Using new amoeba code.\n");
+#else
+ printf("Using old amoeba code.\n");
+#endif
+#endif
+#endif
+
+ /* don't play tapes over network */
+ network_playing = (options.network && !tape.playing);
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ player->index_nr = i;
+ player->element_nr = EL_PLAYER_1 + i;
+
+ player->present = FALSE;
+ player->active = FALSE;
+
+ player->action = 0;
+ player->effective_action = 0;
+ player->programmed_action = 0;
+
+ player->score = 0;
+ player->gems_still_needed = level.gems_needed;
+ player->sokobanfields_still_needed = 0;
+ player->lights_still_needed = 0;
+ player->friends_still_needed = 0;
+
+ for (j=0; j<4; j++)
+ player->key[j] = FALSE;
+
+ player->dynabomb_count = 0;
+ player->dynabomb_size = 1;
+ player->dynabombs_left = 0;
+ player->dynabomb_xl = FALSE;
+
+ player->MovDir = MV_NO_MOVING;
+ player->MovPos = 0;
+ player->Pushing = FALSE;
+ player->Switching = FALSE;
+ player->GfxPos = 0;
+ player->GfxDir = MV_NO_MOVING;
+ player->GfxAction = ACTION_DEFAULT;
+ player->Frame = 0;
+ player->StepFrame = 0;
+
+ player->use_murphy_graphic = FALSE;
+ player->use_disk_red_graphic = FALSE;
+
+ player->actual_frame_counter = 0;
+
+ player->last_move_dir = MV_NO_MOVING;
+
+ player->is_moving = FALSE;
+ player->is_waiting = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+
+ player->show_envelope = 0;
+
+ player->move_delay = game.initial_move_delay;
+ player->move_delay_value = game.initial_move_delay_value;
+
+ player->push_delay = 0;
+ player->push_delay_value = 5;
+
+ player->snapped = FALSE;
+
+ player->last_jx = player->last_jy = 0;
+ player->jx = player->jy = 0;
+
+ player->shield_normal_time_left = 0;
+ player->shield_deadly_time_left = 0;
+
+ player->inventory_size = 0;
+
+ DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
+
+ player->LevelSolved = FALSE;
+ player->GameOver = FALSE;
+ }
+
+ network_player_action_received = FALSE;
+
+#if defined(PLATFORM_UNIX)
+ /* initial null action */
+ if (network_playing)
+ SendToServer_MovePlayer(MV_NO_MOVING);
+#endif
+
+ ZX = ZY = -1;
+
+ FrameCounter = 0;
+ TimeFrames = 0;
+ TimePlayed = 0;
+ TimeLeft = level.time;
+
+ ScreenMovDir = MV_NO_MOVING;
+ ScreenMovPos = 0;
+ ScreenGfxPos = 0;
+
+ ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
+
+ AllPlayersGone = FALSE;
+
+ game.yamyam_content_nr = 0;
+ game.magic_wall_active = FALSE;
+ game.magic_wall_time_left = 0;
+ game.light_time_left = 0;
+ game.timegate_time_left = 0;
+ game.switchgate_pos = 0;
+ game.balloon_dir = MV_NO_MOVING;
+ game.explosions_delayed = TRUE;
+
+ for (i=0; i<4; i++)
+ {
+ game.belt_dir[i] = MV_NO_MOVING;
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+ }
+
+ for (i=0; i<MAX_NUM_AMOEBA; i++)
+ AmoebaCnt[i] = AmoebaCnt2[i] = 0;
+
+ for (x=0; x<lev_fieldx; x++)
+ {
+ for (y=0; y<lev_fieldy; y++)
+ {
+ Feld[x][y] = level.field[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ ChangeDelay[x][y] = 0;
+ Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
+ AmoebaNr[x][y] = 0;
+ JustStopped[x][y] = 0;
+ Stop[x][y] = FALSE;
+ Pushed[x][y] = FALSE;
+
+ Changed[x][y] = CE_BITMASK_DEFAULT;
+ ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+
+ ExplodePhase[x][y] = 0;
+ ExplodeField[x][y] = EX_NO_EXPLOSION;
+
+ GfxFrame[x][y] = 0;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+ GfxElement[x][y] = EL_UNDEFINED;
+ }
+ }
+
+ for(y=0; y<lev_fieldy; y++)
+ {
+ for(x=0; x<lev_fieldx; x++)
+ {
+ if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+ emulate_bd = FALSE;
+ if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+ emulate_sb = FALSE;
+ if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+ emulate_sp = FALSE;
+
+ InitField(x, y, TRUE);
+ }
+ }
+
+ InitBeltMovement();
+
+ game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+ emulate_sb ? EMU_SOKOBAN :
+ emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+ /* correct non-moving belts to start moving left */
+ for (i=0; i<4; i++)
+ if (game.belt_dir[i] == MV_NO_MOVING)
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+
+ /* check if any connected player was not found in playfield */
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (player->connected && !player->present)
+ {
+ for (j=0; j<MAX_PLAYERS; j++)
+ {
+ struct PlayerInfo *some_player = &stored_player[j];
+ int jx = some_player->jx, jy = some_player->jy;
+
+ /* assign first free player found that is present in the playfield */
+ if (some_player->present && !some_player->connected)
+ {
+ player->present = TRUE;
+ player->active = TRUE;
+ some_player->present = FALSE;
+
+ StorePlayer[jx][jy] = player->element_nr;
+ player->jx = player->last_jx = jx;
+ player->jy = player->last_jy = jy;
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (tape.playing)
+ {
+ /* when playing a tape, eliminate all players who do not participate */
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active && !tape.player_participates[i])
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
+
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+ else if (!options.network && !setup.team_mode) /* && !tape.playing */
+ {
+ /* when in single player mode, eliminate all but the first active player */
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ 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;
+
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+ }
+ }
+
+ /* when recording the game, store which players take part in the game */
+ if (tape.recording)
+ {
+ for (i=0; i<MAX_PLAYERS; i++)
+ if (stored_player[i].active)
+ tape.player_participates[i] = TRUE;
+ }
+
+ if (options.debug)
+ {
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ printf("Player %d: present == %d, connected == %d, active == %d.\n",
+ i+1,
+ player->present,
+ player->connected,
+ player->active);
+ if (local_player == player)
+ printf("Player %d is local player.\n", i+1);
+ }
+ }
+
+ if (BorderElement == EL_EMPTY)
+ {
+ SBX_Left = 0;
+ SBX_Right = lev_fieldx - SCR_FIELDX;
+ SBY_Upper = 0;
+ SBY_Lower = lev_fieldy - SCR_FIELDY;
+ }
+ else
+ {
+ SBX_Left = -1;
+ SBX_Right = lev_fieldx - SCR_FIELDX + 1;
+ SBY_Upper = -1;
+ SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
+ }
+
+ if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+ SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+ if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+ SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+
+ /* if local player not found, look for custom element that might create
+ the player (make some assumptions about the right custom element) */
+ if (!local_player->present)
+ {
+ int start_x = 0, start_y = 0;
+ int found_rating = 0;
+ int found_element = EL_UNDEFINED;
+
+ for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
+ {
+ int element = Feld[x][y];
+ int content;
+ int xx, yy;
+ boolean is_player;
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ continue;
+
+ if (CAN_CHANGE(element))
+ {
+ for (i=0; i < element_info[element].num_change_pages; i++)
+ {
+ content = element_info[element].change_page[i].target_element;
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 3 || element < found_element))
+ {
+ start_x = x;
+ start_y = y;
+
+ found_rating = 3;
+ found_element = element;
+ }
+ }
+ }
+
+ for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
+ {
+ content = element_info[element].content[xx][yy];
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 2 || element < found_element))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
+
+ found_rating = 2;
+ found_element = element;
+ }
+
+ if (!CAN_CHANGE(element))
+ continue;
+
+ for (i=0; i < element_info[element].num_change_pages; i++)
+ {
+ content = element_info[element].change_page[i].content[xx][yy];
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 1 || element < found_element))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
+
+ found_rating = 1;
+ found_element = element;
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ else
+ {
+#if 1
+ scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
+ local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+ local_player->jx - MIDPOSX);