X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=55ffb998d9285bf24dc928e1f7bec7e8fcf7e13a;hb=63f41483ea7866802a7f6382c1d1b7135a1cd86a;hp=06de1e48621fab80bf5e0595823c6814083eefd2;hpb=de94c39950b38b1dd99f387cda61cb650214b59e;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 06de1e48..55ffb998 100644 --- a/src/game.c +++ b/src/game.c @@ -25,14 +25,16 @@ #define USE_NEW_AMOEBA_CODE FALSE /* EXPERIMENTAL STUFF */ -#define USE_NEW_STUFF ( * 1) +#define USE_NEW_STUFF ( 1) #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1) -#define USE_NEW_COLLECT_COUNT (USE_NEW_STUFF * 1) +#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1) #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1) #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1) #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1) - +#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1) +#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1) +#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 0) /* for DigField() */ #define DF_NO_PUSH 0 @@ -48,13 +50,14 @@ #define SCROLL_INIT 0 #define SCROLL_GO_ON 1 -/* for Explode() */ +/* for Bang()/Explode() */ #define EX_PHASE_START 0 #define EX_TYPE_NONE 0 #define EX_TYPE_NORMAL (1 << 0) #define EX_TYPE_CENTER (1 << 1) #define EX_TYPE_BORDER (1 << 2) #define EX_TYPE_CROSS (1 << 3) +#define EX_TYPE_DYNA (1 << 4) #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER) /* special positions in the game control window (relative to control window) */ @@ -125,6 +128,8 @@ RND(element_info[e].move_delay_random)) #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ (element_info[e].move_delay_random)) +#define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\ + RND(element_info[e].ce_value_random_initial)) #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \ RND((c)->delay_random * (c)->delay_frames)) @@ -132,9 +137,6 @@ ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e)) -#define GET_VALID_PLAYER_ELEMENT(e) \ - ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1) - #define CAN_GROW_INTO(e) \ ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable)) @@ -232,6 +234,7 @@ /* forward declaration for internal use */ +static void SetPlayerWaiting(struct PlayerInfo *, boolean); static void AdvanceFrameAndPlayerCounters(int); static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int); @@ -257,15 +260,15 @@ static void TestIfElementSmashesCustomElement(int, int, int); static void ChangeElement(int, int, int); -static boolean CheckTriggeredElementChangeExt(int, int, int,int,int); -#define CheckTriggeredElementChange(e, ev) \ - CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1) -#define CheckTriggeredElementChangeByPlayer(e, ev, p, s) \ - CheckTriggeredElementChangeExt(e, ev, p, s, -1) -#define CheckTriggeredElementChangeBySide(e, ev, s) \ - CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1) -#define CheckTriggeredElementChangeByPage(e, ev, p) \ - CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p) +static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); +#define CheckTriggeredElementChange(x, y, e, ev) \ + CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1) +#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \ + CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1) +#define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \ + CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1) +#define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \ + CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p) static boolean CheckElementChangeExt(int, int, int, int, int, int, int); #define CheckElementChange(x, y, e, te, ev) \ @@ -582,7 +585,7 @@ access_direction_list[] = { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN }, { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP }, - { EL_UNDEFINED, MV_NO_MOVING } + { EL_UNDEFINED, MV_NONE } }; static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS]; @@ -657,7 +660,7 @@ static int getBeltDirFromBeltSwitchElement(int element) static int belt_move_dir[3] = { MV_LEFT, - MV_NO_MOVING, + MV_NONE, MV_RIGHT }; @@ -680,7 +683,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) } else { - stored_player[0].use_murphy_graphic = TRUE; + stored_player[0].use_murphy = TRUE; } Feld[x][y] = EL_PLAYER_1; @@ -890,8 +893,21 @@ static void InitField(int x, int y, boolean init_game) break; default: +#if 1 + if (IS_CUSTOM_ELEMENT(element)) + { + if (CAN_MOVE(element)) + InitMovDir(x, y); + +#if USE_NEW_CUSTOM_VALUE + if (!element_info[element].use_last_ce_value || init_game) + CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]); +#endif + } +#else if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element)) InitMovDir(x, y); +#endif else if (IS_GROUP_ELEMENT(element)) { struct ElementGroupInfo *group = element_info[element].group; @@ -917,8 +933,18 @@ static void InitField(int x, int y, boolean init_game) break; } -#if USE_NEW_COLLECT_COUNT - Count[x][y] = element_info[Feld[x][y]].collect_count_initial; +#if 0 + +#if USE_NEW_CUSTOM_VALUE + +#if 1 + CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]); +#else + CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial; +#endif + +#endif + #endif } @@ -1113,7 +1139,7 @@ static void resolve_group_element(int group_element, int recursion_depth) static void InitGameEngine() { - int i, j, k, l; + int i, j, k, l, x, y; /* set game engine from tape file when re-playing, else from level file */ game.engine_version = (tape.playing ? tape.engine_version : @@ -1185,6 +1211,38 @@ static void InitGameEngine() game.use_block_last_field_bug = (game.engine_version < VERSION_IDENT(3,1,1,0)); + /* + Summary of bugfix/change: + Changed behaviour of CE changes with multiple changes per single frame. + + Fixed/changed in version: + 3.2.0-6 + + Description: + Before 3.2.0-6, only one single CE change was allowed in each engine frame. + This resulted in race conditions where CEs seem to behave strange in some + situations (where triggered CE changes were just skipped because there was + already a CE change on that tile in the playfield in that engine frame). + Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME. + (The number of changes per frame must be limited in any case, because else + it is easily possible to define CE changes that would result in an infinite + loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value + should be set large enough so that it would only be reached in cases where + the corresponding CE change conditions run into a loop. Therefore, it seems + to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the + maximal number of change pages for custom elements.) + + Affected levels/tapes: + Probably many. + */ + +#if USE_ONLY_ONE_CHANGE_PER_FRAME + game.max_num_changes_per_frame = 1; +#else + game.max_num_changes_per_frame = + (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32); +#endif + /* ---------------------------------------------------------------------- */ /* dynamically adjust element properties according to game engine version */ @@ -1263,6 +1321,9 @@ static void InitGameEngine() ei->change->change_function = ch_delay->change_function; ei->change->post_change_function = ch_delay->post_change_function; + ei->change->can_change = TRUE; + ei->change->can_change_or_has_action = TRUE; + ei->has_change_event[CE_DELAY] = TRUE; SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE); @@ -1317,6 +1378,8 @@ static void InitGameEngine() { ei->change_page[j].actual_trigger_element = EL_EMPTY; ei->change_page[j].actual_trigger_player = EL_PLAYER_1; + ei->change_page[j].actual_trigger_side = CH_SIDE_NONE; + ei->change_page[j].actual_trigger_ce_value = 0; } } @@ -1443,8 +1506,64 @@ static void InitGameEngine() for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++) element_info[access_direction_list[i].element].access_direction = access_direction_list[i].direction; + + /* ---------- initialize explosion content ------------------------------- */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + if (IS_CUSTOM_ELEMENT(i)) + continue; + + for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) + { + /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */ + + element_info[i].content.e[x][y] = + (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW : + i == EL_PLAYER_2 ? EL_EMERALD_RED : + i == EL_PLAYER_3 ? EL_EMERALD : + i == EL_PLAYER_4 ? EL_EMERALD_PURPLE : + i == EL_MOLE ? EL_EMERALD_RED : + i == EL_PENGUIN ? EL_EMERALD_PURPLE : + i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) : + i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND : + i == EL_SP_ELECTRON ? EL_SP_INFOTRON : + i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content : + i == EL_YAMYAM ? EL_UNDEFINED : + i == EL_WALL_EMERALD ? EL_EMERALD : + i == EL_WALL_DIAMOND ? EL_DIAMOND : + i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND : + i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW : + i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED : + i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE : + i == EL_WALL_PEARL ? EL_PEARL : + i == EL_WALL_CRYSTAL ? EL_CRYSTAL : + EL_EMPTY); + } + } } +int get_num_special_action(int element, int action_first, int action_last) +{ + int num_special_action = 0; + int i, j; + + for (i = action_first; i <= action_last; i++) + { + boolean found = FALSE; + + for (j = 0; j < NUM_DIRECTIONS; j++) + if (el_act_dir2img(element, i, j) != + el_act_dir2img(element, ACTION_DEFAULT, j)) + found = TRUE; + + if (found) + num_special_action++; + else + break; + } + + return num_special_action; +} /* ============================================================================= @@ -1459,7 +1578,7 @@ 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, k, x, y; + int i, j, x, y; InitGameEngine(); @@ -1495,15 +1614,18 @@ void InitGame() player->dynabombs_left = 0; player->dynabomb_xl = FALSE; - player->MovDir = MV_NO_MOVING; + player->MovDir = MV_NONE; player->MovPos = 0; player->GfxPos = 0; - player->GfxDir = MV_NO_MOVING; + player->GfxDir = MV_NONE; player->GfxAction = ACTION_DEFAULT; player->Frame = 0; player->StepFrame = 0; - player->use_murphy_graphic = FALSE; + player->use_murphy = FALSE; + player->artwork_element = + (level.use_artwork_element[i] ? level.artwork_element[i] : + player->element_nr); player->block_last_field = FALSE; /* initialized in InitPlayerField() */ player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */ @@ -1514,7 +1636,7 @@ void InitGame() player->step_counter = 0; - player->last_move_dir = MV_NO_MOVING; + player->last_move_dir = MV_NONE; player->is_waiting = FALSE; player->is_moving = FALSE; @@ -1529,6 +1651,8 @@ void InitGame() player->is_bored = FALSE; player->is_sleeping = FALSE; + player->cannot_move = FALSE; + player->frame_counter_bored = -1; player->frame_counter_sleeping = -1; @@ -1540,38 +1664,13 @@ void InitGame() player->special_action_bored = ACTION_DEFAULT; player->special_action_sleeping = ACTION_DEFAULT; - player->num_special_action_bored = 0; - player->num_special_action_sleeping = 0; - - /* determine number of special actions for bored and sleeping animation */ - for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++) - { - boolean found = FALSE; - - for (k = 0; k < NUM_DIRECTIONS; k++) - if (el_act_dir2img(player->element_nr, j, k) != - el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) - found = TRUE; - - if (found) - player->num_special_action_bored++; - else - break; - } - for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++) - { - boolean found = FALSE; - - for (k = 0; k < NUM_DIRECTIONS; k++) - if (el_act_dir2img(player->element_nr, j, k) != - el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) - found = TRUE; - - if (found) - player->num_special_action_sleeping++; - else - break; - } + /* set number of special actions for bored and sleeping animation */ + player->num_special_action_bored = + get_num_special_action(player->artwork_element, + ACTION_BORING_1, ACTION_BORING_LAST); + player->num_special_action_sleeping = + get_num_special_action(player->artwork_element, + ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); player->switch_x = -1; player->switch_y = -1; @@ -1614,7 +1713,7 @@ void InitGame() #if defined(NETWORK_AVALIABLE) /* initial null action */ if (network_playing) - SendToServer_MovePlayer(MV_NO_MOVING); + SendToServer_MovePlayer(MV_NONE); #endif ZX = ZY = -1; @@ -1626,7 +1725,7 @@ void InitGame() TimeLeft = level.time; TapeTime = 0; - ScreenMovDir = MV_NO_MOVING; + ScreenMovDir = MV_NONE; ScreenMovPos = 0; ScreenGfxPos = 0; @@ -1640,15 +1739,18 @@ void InitGame() game.light_time_left = 0; game.timegate_time_left = 0; game.switchgate_pos = 0; - game.balloon_dir = MV_NO_MOVING; + game.wind_direction = level.wind_direction_initial; game.gravity = level.initial_gravity; game.explosions_delayed = TRUE; + game.lenses_time_left = 0; + game.magnify_time_left = 0; + game.envelope_active = FALSE; for (i = 0; i < NUM_BELTS; i++) { - game.belt_dir[i] = MV_NO_MOVING; + game.belt_dir[i] = MV_NONE; game.belt_dir_nr[i] = 3; /* not moving, next moving left */ } @@ -1663,8 +1765,8 @@ void InitGame() MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_COLLECT_COUNT - Count[x][y] = 0; /* initialized in InitField() */ +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; /* initialized in InitField() */ #endif Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; AmoebaNr[x][y] = 0; @@ -1674,7 +1776,7 @@ void InitGame() Stop[x][y] = FALSE; Pushed[x][y] = FALSE; - Changed[x][y] = FALSE; + Changed[x][y] = 0; ChangeEvent[x][y] = -1; ExplodePhase[x][y] = 0; @@ -1688,7 +1790,7 @@ void InitGame() GfxRandom[x][y] = INIT_GFX_RANDOM(); GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; - GfxDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MV_NONE; } } @@ -1763,7 +1865,7 @@ void InitGame() /* correct non-moving belts to start moving left */ for (i = 0; i < NUM_BELTS; i++) - if (game.belt_dir[i] == MV_NO_MOVING) + if (game.belt_dir[i] == MV_NONE) game.belt_dir_nr[i] = 3; /* not moving, next moving left */ /* check if any connected player was not found in playfield */ @@ -1791,6 +1893,8 @@ void InitGame() player->element_nr = some_player->element_nr; #endif + player->artwork_element = some_player->artwork_element; + player->block_last_field = some_player->block_last_field; player->block_delay_adjustment = some_player->block_delay_adjustment; @@ -1899,6 +2003,7 @@ void InitGame() int start_x = 0, start_y = 0; int found_rating = 0; int found_element = EL_UNDEFINED; + int player_nr = local_player->index_nr; for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) { @@ -1907,6 +2012,17 @@ void InitGame() int xx, yy; boolean is_player; + if (level.use_start_element[player_nr] && + level.start_element[player_nr] == element && + found_rating < 4) + { + start_x = x; + start_y = y; + + found_rating = 4; + found_element = element; + } + if (!IS_CUSTOM_ELEMENT(element)) continue; @@ -1914,6 +2030,7 @@ void InitGame() { for (i = 0; i < element_info[element].num_change_pages; i++) { + /* check for player created from custom element as single target */ content = element_info[element].change_page[i].target_element; is_player = ELEM_IS_PLAYER(content); @@ -1930,7 +2047,8 @@ void InitGame() for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++) { - content = element_info[element].content[xx][yy]; + /* check for player created from custom element as explosion content */ + content = element_info[element].content.e[xx][yy]; is_player = ELEM_IS_PLAYER(content); if (is_player && (found_rating < 2 || element < found_element)) @@ -1947,7 +2065,10 @@ void InitGame() for (i = 0; i < element_info[element].num_change_pages; i++) { - content= element_info[element].change_page[i].target_content[xx][yy]; + /* check for player created from custom element as extended target */ + content = + element_info[element].change_page[i].target_content.e[xx][yy]; + is_player = ELEM_IS_PLAYER(content); if (is_player && (found_rating < 1 || element < found_element)) @@ -2142,7 +2263,7 @@ void InitMovDir(int x, int y) if (move_direction_initial == MV_START_PREVIOUS) { - if (MovDir[x][y] != MV_NO_MOVING) + if (MovDir[x][y] != MV_NONE) return; move_direction_initial = MV_START_AUTOMATIC; @@ -2273,12 +2394,17 @@ void GameWon() { if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); - if (TimeLeft > 0 && !(TimeLeft % 10)) - RaiseScore(level.score[SC_TIME_BONUS]); - if (TimeLeft > 100 && !(TimeLeft % 10)) + + if (TimeLeft > 100 && TimeLeft % 10 == 0) + { TimeLeft -= 10; + RaiseScore(level.score[SC_TIME_BONUS] * 10); + } else + { TimeLeft--; + RaiseScore(level.score[SC_TIME_BONUS]); + } DrawGameValue_Time(TimeLeft); @@ -2301,12 +2427,17 @@ void GameWon() { if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); - if (TimePlayed < 999 && !(TimePlayed % 10)) - RaiseScore(level.score[SC_TIME_BONUS]); - if (TimePlayed < 900 && !(TimePlayed % 10)) + + if (TimePlayed < 900 && TimePlayed % 10 == 0) + { TimePlayed += 10; + RaiseScore(level.score[SC_TIME_BONUS] * 10); + } else + { TimePlayed++; + RaiseScore(level.score[SC_TIME_BONUS]); + } DrawGameValue_Time(TimePlayed); @@ -2529,8 +2660,8 @@ void InitMovingField(int x, int y, int direction) MovDir[newx][newy] = MovDir[x][y]; -#if USE_NEW_COLLECT_COUNT - Count[newx][newy] = Count[x][y]; +#if USE_NEW_CUSTOM_VALUE + CustomValue[newx][newy] = CustomValue[x][y]; #endif GfxFrame[newx][newy] = GfxFrame[x][y]; @@ -2614,8 +2745,8 @@ static void RemoveField(int x, int y) MovDir[x][y] = 0; MovDelay[x][y] = 0; -#if USE_NEW_COLLECT_COUNT - Count[x][y] = 0; +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; #endif AmoebaNr[x][y] = 0; @@ -2629,7 +2760,7 @@ static void RemoveField(int x, int y) GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; - GfxDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MV_NONE; } void RemoveMovingField(int x, int y) @@ -2827,8 +2958,9 @@ void DrawRelocatePlayer(struct PlayerInfo *player) void RelocatePlayer(int jx, int jy, int el_player_raw) { - int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw); - struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1]; + int el_player = GET_PLAYER_ELEMENT(el_player_raw); + int player_nr = GET_PLAYER_NR(el_player); + struct PlayerInfo *player = &stored_player[player_nr]; boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); @@ -2886,7 +3018,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) CE_LEFT_BY_PLAYER, player->index_bit, leave_side); - CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X, + CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element, + CE_PLAYER_LEAVES_X, player->index_bit, leave_side); Feld[jx][jy] = el_player; @@ -2908,7 +3041,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER, player->index_bit, enter_side); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_ENTERS_X, + CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); } @@ -2930,6 +3063,21 @@ void Explode(int ex, int ey, int phase, int mode) if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ { int center_element = Feld[ex][ey]; + int artwork_element = center_element; /* for custom player artwork */ + int explosion_element = center_element; /* for custom player artwork */ + + if (IS_PLAYER(ex, ey)) + { + int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]); + + artwork_element = stored_player[player_nr].artwork_element; + + if (level.use_explosion_element[player_nr]) + { + explosion_element = level.explosion_element[player_nr]; + artwork_element = explosion_element; + } + } #if 0 /* --- This is only really needed (and now handled) in "Impact()". --- */ @@ -2943,7 +3091,7 @@ void Explode(int ex, int ey, int phase, int mode) if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER || mode == EX_TYPE_CROSS) - PlayLevelSoundAction(ex, ey, ACTION_EXPLODING); + PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); /* remove things displayed in background while burning dynamite */ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) @@ -2957,7 +3105,7 @@ void Explode(int ex, int ey, int phase, int mode) Feld[ex][ey] = center_element; } - last_phase = element_info[center_element].explosion_delay + 1; + last_phase = element_info[explosion_element].explosion_delay + 1; for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++) { @@ -3030,6 +3178,11 @@ void Explode(int ex, int ey, int phase, int mode) if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey)) { + int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1; + + Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr; + +#if 0 switch(StorePlayer[ex][ey]) { case EL_PLAYER_2: @@ -3046,10 +3199,21 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_PLAYER_IS_EXPLODING_1; break; } +#endif - if (PLAYERINFO(ex, ey)->use_murphy_graphic) + if (PLAYERINFO(ex, ey)->use_murphy) Store[x][y] = EL_EMPTY; } +#if 1 + else if (center_element == EL_YAMYAM) + Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy]; + else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY) + Store[x][y] = element_info[center_element].content.e[xx][yy]; + else if (!CAN_EXPLODE(element)) + Store[x][y] = element_info[element].content.e[1][1]; + else + Store[x][y] = EL_EMPTY; +#else else if (center_element == EL_MOLE) Store[x][y] = EL_EMERALD_RED; else if (center_element == EL_PENGUIN) @@ -3063,10 +3227,10 @@ void Explode(int ex, int ey, int phase, int mode) else if (center_element == EL_AMOEBA_TO_DIAMOND) Store[x][y] = level.amoeba_content; else if (center_element == EL_YAMYAM) - Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy]; + Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy]; else if (IS_CUSTOM_ELEMENT(center_element) && - element_info[center_element].content[xx][yy] != EL_EMPTY) - Store[x][y] = element_info[center_element].content[xx][yy]; + element_info[center_element].content.e[xx][yy] != EL_EMPTY) + Store[x][y] = element_info[center_element].content.e[xx][yy]; else if (element == EL_WALL_EMERALD) Store[x][y] = EL_EMERALD; else if (element == EL_WALL_DIAMOND) @@ -3084,16 +3248,17 @@ void Explode(int ex, int ey, int phase, int mode) else if (element == EL_WALL_CRYSTAL) Store[x][y] = EL_CRYSTAL; else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element)) - Store[x][y] = element_info[element].content[1][1]; + Store[x][y] = element_info[element].content.e[1][1]; else Store[x][y] = EL_EMPTY; +#endif if (x != ex || y != ey || mode == EX_TYPE_BORDER || center_element == EL_AMOEBA_TO_DIAMOND) Store2[x][y] = element; Feld[x][y] = EL_EXPLOSION; - GfxElement[x][y] = center_element; + GfxElement[x][y] = artwork_element; ExplodePhase[x][y] = 1; ExplodeDelay[x][y] = last_phase; @@ -3187,12 +3352,30 @@ void Explode(int ex, int ey, int phase, int mode) /* player can escape from explosions and might therefore be still alive */ if (element >= EL_PLAYER_IS_EXPLODING_1 && element <= EL_PLAYER_IS_EXPLODING_4) - Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ? - EL_EMPTY : - element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW : - element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED : - element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD : - EL_EMERALD_PURPLE); + { + static int player_death_elements[] = + { + EL_EMERALD_YELLOW, + EL_EMERALD_RED, + EL_EMERALD, + EL_EMERALD_PURPLE + }; + int player_nr = element - EL_PLAYER_IS_EXPLODING_1; + int player_death_element = player_death_elements[player_nr]; + + if (level.use_explosion_element[player_nr]) + { + int explosion_element = level.explosion_element[player_nr]; + int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2); + int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2); + + player_death_element = + element_info[explosion_element].content.e[xx][yy]; + } + + Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY : + player_death_element); + } /* restore probably existing indestructible background element */ if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y])) @@ -3200,12 +3383,12 @@ void Explode(int ex, int ey, int phase, int mode) Back[x][y] = 0; MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0; - GfxDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MV_NONE; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_COLLECT_COUNT - Count[x][y] = 0; +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; #endif InitField_WithBug2(x, y, FALSE); @@ -3300,13 +3483,24 @@ void DynaExplode(int ex, int ey) void Bang(int x, int y) { int element = MovingOrBlocked2Element(x, y); + int explosion_type = EX_TYPE_NORMAL; if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) { struct PlayerInfo *player = PLAYERINFO(x, y); - element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY : + element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY : player->element_nr); + + if (level.use_explosion_element[player->index_nr]) + { + int explosion_element = level.explosion_element[player->index_nr]; + + if (element_info[explosion_element].explosion_type == EXPLODES_CROSS) + explosion_type = EX_TYPE_CROSS; + else if (element_info[explosion_element].explosion_type == EXPLODES_1X1) + explosion_type = EX_TYPE_CENTER; + } } switch(element) @@ -3321,8 +3515,8 @@ void Bang(int x, int y) case EL_PACMAN: case EL_MOLE: RaiseScoreElement(element); - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); break; + case EL_DYNABOMB_PLAYER_1_ACTIVE: case EL_DYNABOMB_PLAYER_2_ACTIVE: case EL_DYNABOMB_PLAYER_3_ACTIVE: @@ -3330,28 +3524,31 @@ void Bang(int x, int y) case EL_DYNABOMB_INCREASE_NUMBER: case EL_DYNABOMB_INCREASE_SIZE: case EL_DYNABOMB_INCREASE_POWER: - DynaExplode(x, y); + explosion_type = EX_TYPE_DYNA; break; + case EL_PENGUIN: case EL_LAMP: case EL_LAMP_ACTIVE: case EL_AMOEBA_TO_DIAMOND: - if (IS_PLAYER(x, y)) - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); - else - Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER); + if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */ + explosion_type = EX_TYPE_CENTER; break; + default: if (element_info[element].explosion_type == EXPLODES_CROSS) - Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS); + explosion_type = EX_TYPE_CROSS; else if (element_info[element].explosion_type == EXPLODES_1X1) - Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER); - else - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); + explosion_type = EX_TYPE_CENTER; break; } - CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X); + if (explosion_type == EX_TYPE_DYNA) + DynaExplode(x, y); + else + Explode(x, y, EX_PHASE_START, explosion_type); + + CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X); } void SplashAcid(int x, int y) @@ -3413,7 +3610,7 @@ static void InitBeltMovement() for (i = 0; i < NUM_BELTS; i++) { - if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING) + if (IS_BELT(element) && game.belt_dir[i] != MV_NONE) { int e_belt_nr = getBeltNrFromBeltElement(element); int belt_nr = i; @@ -3456,9 +3653,9 @@ static void ToggleBeltSwitch(int x, int y) static int belt_move_dir[4] = { MV_LEFT, - MV_NO_MOVING, + MV_NONE, MV_RIGHT, - MV_NO_MOVING, + MV_NONE, }; int element = Feld[x][y]; @@ -3504,7 +3701,7 @@ static void ToggleBeltSwitch(int x, int y) DrawLevelField(xx, yy); } } - else if (IS_BELT(element) && belt_dir != MV_NO_MOVING) + else if (IS_BELT(element) && belt_dir != MV_NONE) { int e_belt_nr = getBeltNrFromBeltElement(element); @@ -3516,7 +3713,7 @@ static void ToggleBeltSwitch(int x, int y) DrawLevelField(xx, yy); } } - else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING) + else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE) { int e_belt_nr = getBeltNrFromBeltActiveElement(element); @@ -3588,50 +3785,155 @@ static void RedrawAllLightSwitchesAndInvisibleElements() { int x, y; - for (y = 0; y < lev_fieldy; y++) + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) { - for (x = 0; x < lev_fieldx; x++) + int element = Feld[x][y]; + + if (element == EL_LIGHT_SWITCH && + game.light_time_left > 0) { - int element = Feld[x][y]; + Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_LIGHT_SWITCH_ACTIVE && + game.light_time_left == 0) + { + Feld[x][y] = EL_LIGHT_SWITCH; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER && + game.light_time_left > 0) + { + Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER_ACTIVE && + game.light_time_left == 0) + { + Feld[x][y] = EL_EMC_DRIPPER; + DrawLevelField(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL || + element == EL_INVISIBLE_WALL || + element == EL_INVISIBLE_SAND) + { + if (game.light_time_left > 0) + Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); - if (element == EL_LIGHT_SWITCH && - game.light_time_left > 0) - { - Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE; - DrawLevelField(x, y); - } - else if (element == EL_LIGHT_SWITCH_ACTIVE && - game.light_time_left == 0) - { - Feld[x][y] = EL_LIGHT_SWITCH; - DrawLevelField(x, y); - } - else if (element == EL_INVISIBLE_STEELWALL || - element == EL_INVISIBLE_WALL || - element == EL_INVISIBLE_SAND) - { - if (game.light_time_left > 0) - Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); + DrawLevelField(x, y); - DrawLevelField(x, y); + /* uncrumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || + element == EL_INVISIBLE_WALL_ACTIVE || + element == EL_INVISIBLE_SAND_ACTIVE) + { + if (game.light_time_left == 0) + Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); - /* uncrumble neighbour fields, if needed */ - if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); - } - else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || - element == EL_INVISIBLE_WALL_ACTIVE || - element == EL_INVISIBLE_SAND_ACTIVE) - { - if (game.light_time_left == 0) - Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); + DrawLevelField(x, y); - DrawLevelField(x, y); + /* re-crumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + } +} - /* re-crumble neighbour fields, if needed */ - if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); - } +static void RedrawAllInvisibleElementsForLenses() +{ + int x, y; + + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) + { + int element = Feld[x][y]; + + if (element == EL_EMC_DRIPPER && + game.lenses_time_left > 0) + { + Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER_ACTIVE && + game.lenses_time_left == 0) + { + Feld[x][y] = EL_EMC_DRIPPER; + DrawLevelField(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL || + element == EL_INVISIBLE_WALL || + element == EL_INVISIBLE_SAND) + { + if (game.lenses_time_left > 0) + Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); + + DrawLevelField(x, y); + + /* uncrumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || + element == EL_INVISIBLE_WALL_ACTIVE || + element == EL_INVISIBLE_SAND_ACTIVE) + { + if (game.lenses_time_left == 0) + Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); + + DrawLevelField(x, y); + + /* re-crumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + } +} + +static void RedrawAllInvisibleElementsForMagnifier() +{ + int x, y; + + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) + { + int element = Feld[x][y]; + + if (element == EL_EMC_FAKE_GRASS && + game.magnify_time_left > 0) + { + Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_FAKE_GRASS_ACTIVE && + game.magnify_time_left == 0) + { + Feld[x][y] = EL_EMC_FAKE_GRASS; + DrawLevelField(x, y); + } + else if (IS_GATE_GRAY(element) && + game.magnify_time_left > 0) + { + Feld[x][y] = (IS_RND_GATE_GRAY(element) ? + element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE : + IS_EM_GATE_GRAY(element) ? + element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE : + IS_EMC_GATE_GRAY(element) ? + element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE : + element); + DrawLevelField(x, y); + } + else if (IS_GATE_GRAY_ACTIVE(element) && + game.magnify_time_left == 0) + { + Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ? + element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY : + IS_EM_GATE_GRAY_ACTIVE(element) ? + element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY : + IS_EMC_GATE_GRAY_ACTIVE(element) ? + element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY : + element); + DrawLevelField(x, y); } } } @@ -3880,7 +4182,7 @@ void Impact(int x, int y) CheckElementChangeBySide(x, y + 1, smashed, element, CE_SWITCHED, CH_SIDE_TOP); - CheckTriggeredElementChangeBySide(smashed, CE_SWITCH_OF_X, + CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X, CH_SIDE_TOP); } } @@ -4169,7 +4471,7 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_BALLOON) { - MovDir[x][y] = game.balloon_dir; + MovDir[x][y] = game.wind_direction; MovDelay[x][y] = 0; } else if (element == EL_SPRING) @@ -4177,7 +4479,7 @@ inline static void TurnRoundExt(int x, int y) if (MovDir[x][y] & MV_HORIZONTAL && (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || SPRING_CAN_ENTER_FIELD(element, x, y + 1))) - MovDir[x][y] = MV_NO_MOVING; + MovDir[x][y] = MV_NONE; MovDelay[x][y] = 0; } @@ -4246,7 +4548,7 @@ inline static void TurnRoundExt(int x, int y) } } - MovDir[x][y] = MV_NO_MOVING; + MovDir[x][y] = MV_NONE; if (attr_x < x) MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT); else if (attr_x > x) @@ -4383,6 +4685,11 @@ inline static void TurnRoundExt(int x, int y) MovDir[x][y] = move_pattern; MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } + else if (move_pattern & MV_WIND_DIRECTION) + { + MovDir[x][y] = game.wind_direction; + MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); + } else if (move_pattern == MV_ALONG_LEFT_SIDE) { if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y)) @@ -4436,7 +4743,7 @@ inline static void TurnRoundExt(int x, int y) } } - MovDir[x][y] = MV_NO_MOVING; + MovDir[x][y] = MV_NONE; if (attr_x < x) MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT); else if (attr_x > x) @@ -4482,7 +4789,7 @@ inline static void TurnRoundExt(int x, int y) move_pattern == MV_WHEN_DROPPED) { if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) - MovDir[x][y] = MV_NO_MOVING; + MovDir[x][y] = MV_NONE; MovDelay[x][y] = 0; } @@ -4510,7 +4817,7 @@ inline static void TurnRoundExt(int x, int y) }; boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER); int move_preference = -1000000; /* start with very low preference */ - int new_move_dir = MV_NO_MOVING; + int new_move_dir = MV_NONE; int start_test = RND(4); int i; @@ -4759,7 +5066,7 @@ void StartMoving(int x, int y) } else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug) { - if (MovDir[x][y] == MV_NO_MOVING) + if (MovDir[x][y] == MV_NONE) { InitMovingField(x, y, MV_DOWN); started_moving = TRUE; @@ -4901,7 +5208,7 @@ void StartMoving(int x, int y) /* not "else if" because of elements that can fall and move (EL_SPRING) */ #if 0 - if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING) + if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE) #else if (CAN_MOVE(element) && !started_moving) #endif @@ -4911,7 +5218,7 @@ void StartMoving(int x, int y) #if 0 #if DEBUG - if (MovDir[x][y] == MV_NO_MOVING) + if (MovDir[x][y] == MV_NONE) { printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n", x, y, element, element_info[element].token_name); @@ -5098,7 +5405,7 @@ void StartMoving(int x, int y) if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING) DrawLevelField(newx, newy); else - GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MovDir[x][y] = MV_NONE; } else if (!IS_FREE(newx, newy)) { @@ -5442,7 +5749,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_COLLECT_COUNT +#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); #endif } @@ -5460,7 +5767,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_COLLECT_COUNT +#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); #endif } @@ -5495,18 +5802,18 @@ void ContinueMoving(int x, int y) Changed[newx][newy] = Changed[x][y]; ChangeEvent[newx][newy] = ChangeEvent[x][y]; -#if USE_NEW_COLLECT_COUNT - Count[newx][newy] = Count[x][y]; +#if USE_NEW_CUSTOM_VALUE + CustomValue[newx][newy] = CustomValue[x][y]; #endif } ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; - Changed[x][y] = FALSE; + Changed[x][y] = 0; ChangeEvent[x][y] = -1; -#if USE_NEW_COLLECT_COUNT - Count[x][y] = 0; +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; #endif /* copy animation control values to new field */ @@ -5603,15 +5910,17 @@ void ContinueMoving(int x, int y) if (pushed_by_player && !game.use_change_when_pushing_bug) { - int dig_side = MV_DIR_OPPOSITE(direction); + int push_side = MV_DIR_OPPOSITE(direction); struct PlayerInfo *player = PLAYERINFO(x, y); CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER, - player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X, - player->index_bit, dig_side); + player->index_bit, push_side); + CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X, + player->index_bit, push_side); } + CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction); + TestIfElementTouchesCustomElement(x, y); /* empty or new element */ TestIfElementHitsCustomElement(newx, newy, direction); @@ -6025,10 +6334,14 @@ void AmoebeAbleger(int ax, int ay) void Life(int ax, int ay) { int x1, y1, x2, y2; +#if 0 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */ +#endif int life_time = 40; int element = Feld[ax][ay]; int graphic = el2img(element); + int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life : + level.biomaze); boolean changed = FALSE; if (IS_ANIMATED(graphic)) @@ -6071,7 +6384,8 @@ void Life(int ax, int ay) if (xx == ax && yy == ay) /* field in the middle */ { - if (nachbarn < life[0] || nachbarn > life[1]) + if (nachbarn < life_parameter[0] || + nachbarn > life_parameter[1]) { Feld[xx][yy] = EL_EMPTY; if (!Stop[xx][yy]) @@ -6082,7 +6396,8 @@ void Life(int ax, int ay) } else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy])) { /* free border field */ - if (nachbarn >= life[2] && nachbarn <= life[3]) + if (nachbarn >= life_parameter[2] && + nachbarn <= life_parameter[3]) { Feld[xx][yy] = element; MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1); @@ -6275,7 +6590,7 @@ void MauerWaechst(int x, int y) Feld[x][y] = Store[x][y]; Store[x][y] = 0; - GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MovDir[x][y] = MV_NONE; DrawLevelField(x, y); } } @@ -6529,7 +6844,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) if (!change->has_action) return; - /* ---------- determine action paramater values ---------- */ + /* ---------- determine action paramater values -------------------------- */ + + int level_time_value = + (level.time > 0 ? TimeLeft : + TimePlayed); int action_arg_element = (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player : @@ -6537,53 +6856,69 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element : EL_EMPTY); + int action_arg_direction = + (action_arg >= CA_ARG_DIRECTION_LEFT && + action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION : + action_arg == CA_ARG_DIRECTION_TRIGGER ? + change->actual_trigger_side : + action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ? + MV_DIR_OPPOSITE(change->actual_trigger_side) : + MV_NONE); + int action_arg_number_min = (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN : CA_ARG_MIN); int action_arg_number_max = (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX : - action_type == CA_SET_GEMS ? 999 : - action_type == CA_SET_TIME ? 9999 : - action_type == CA_SET_SCORE ? 99999 : + action_type == CA_SET_LEVEL_GEMS ? 999 : + action_type == CA_SET_LEVEL_TIME ? 9999 : + action_type == CA_SET_LEVEL_SCORE ? 99999 : action_type == CA_SET_CE_SCORE ? 9999 : - action_type == CA_SET_CE_COUNT ? 9999 : + action_type == CA_SET_CE_VALUE ? 9999 : CA_ARG_MAX); int action_arg_number_reset = (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value : - action_type == CA_SET_GEMS ? level.gems_needed : - action_type == CA_SET_TIME ? level.time : - action_type == CA_SET_SCORE ? 0 : + action_type == CA_SET_LEVEL_GEMS ? level.gems_needed : + action_type == CA_SET_LEVEL_TIME ? level.time : + action_type == CA_SET_LEVEL_SCORE ? 0 : action_type == CA_SET_CE_SCORE ? 0 : - action_type == CA_SET_CE_COUNT ? ei->collect_count_initial : +#if 1 + action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) : +#else + action_type == CA_SET_CE_VALUE ? ei->custom_value_initial : +#endif 0); - int action_arg_number_normal = - (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_NORMAL : - action_arg_number_reset); - int action_arg_number = (action_arg <= CA_ARG_MAX ? action_arg : + action_arg >= CA_ARG_SPEED_NOT_MOVING && + action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) : + action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset : action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min : action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max : action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset : - action_arg == CA_ARG_NUMBER_NORMAL ? action_arg_number_normal : action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : -#if USE_NEW_COLLECT_COUNT - action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] : +#if USE_NEW_CUSTOM_VALUE + action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] : #else - action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial : + action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial : #endif action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) : + action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : + action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed : + action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : + action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : + action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value : -1); int action_arg_number_old = - (action_type == CA_SET_GEMS ? local_player->gems_still_needed : - action_type == CA_SET_TIME ? TimeLeft : - action_type == CA_SET_SCORE ? local_player->score : + (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed : + action_type == CA_SET_LEVEL_TIME ? TimeLeft : + action_type == CA_SET_LEVEL_SCORE ? local_player->score : action_type == CA_SET_CE_SCORE ? ei->collect_score : - action_type == CA_SET_CE_COUNT ? Count[x][y] : + action_type == CA_SET_CE_VALUE ? CustomValue[x][y] : 0); int action_arg_number_new = @@ -6591,26 +6926,19 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_mode, action_arg_number, action_arg_number_min, action_arg_number_max); - /* (for explicit player choice, set invalid value to "no player") */ - int action_arg_player_bits = - (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER : - action_arg >= CA_ARG_PLAYER_1 && - action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER : - action_arg >= CA_ARG_1 && - action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) : - action_arg_element >= EL_PLAYER_1 && - action_arg_element <= EL_PLAYER_4 ? - (1 << (action_arg_element - EL_PLAYER_1)) : - 0); - - /* (for implicit player choice, set invalid value to "all players") */ int trigger_player_bits = (change->actual_trigger_player >= EL_PLAYER_1 && change->actual_trigger_player <= EL_PLAYER_4 ? (1 << (change->actual_trigger_player - EL_PLAYER_1)) : PLAYER_BITS_ANY); - /* ---------- execute action ---------- */ + int action_arg_player_bits = + (action_arg >= CA_ARG_PLAYER_1 && + action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER : + action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits : + PLAYER_BITS_ANY); + + /* ---------- execute action -------------------------------------------- */ switch(action_type) { @@ -6619,23 +6947,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) return; } - case CA_EXIT_PLAYER: - { - for (i = 0; i < MAX_PLAYERS; i++) - if (action_arg_player_bits & (1 << i)) - stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE; - - break; - } - - case CA_KILL_PLAYER: - { - for (i = 0; i < MAX_PLAYERS; i++) - if (action_arg_player_bits & (1 << i)) - KillPlayer(&stored_player[i]); - - break; - } + /* ---------- level actions ------------------------------------------- */ case CA_RESTART_LEVEL: { @@ -6655,31 +6967,89 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } - case CA_ADD_KEY: + case CA_SET_LEVEL_TIME: { - int element = getSpecialActionElement(action_arg_element, - action_arg_number, EL_KEY_1); - - if (IS_KEY(element)) + if (level.time > 0) /* only modify limited time value */ { - for (i = 0; i < MAX_PLAYERS; i++) - { - if (trigger_player_bits & (1 << i)) - { - stored_player[i].key[KEY_NR(element)] = TRUE; + TimeLeft = action_arg_number_new; - DrawGameValue_Keys(stored_player[i].key); + DrawGameValue_Time(TimeLeft); - redraw_mask |= REDRAW_DOOR_1; - } - } + if (!TimeLeft && setup.time_limit) + for (i = 0; i < MAX_PLAYERS; i++) + KillPlayer(&stored_player[i]); } break; } - case CA_DEL_KEY: + case CA_SET_LEVEL_SCORE: + { + local_player->score = action_arg_number_new; + + DrawGameValue_Score(local_player->score); + + break; + } + + case CA_SET_LEVEL_GEMS: + { + local_player->gems_still_needed = action_arg_number_new; + + DrawGameValue_Emeralds(local_player->gems_still_needed); + + break; + } + + case CA_SET_LEVEL_GRAVITY: + { + game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE : + action_arg == CA_ARG_GRAVITY_ON ? TRUE : + action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity : + game.gravity); + break; + } + + case CA_SET_LEVEL_WIND: + { + game.wind_direction = action_arg_direction; + + break; + } + + /* ---------- player actions ------------------------------------------ */ + + case CA_MOVE_PLAYER: + { + /* automatically move to the next field in specified direction */ + for (i = 0; i < MAX_PLAYERS; i++) + if (trigger_player_bits & (1 << i)) + stored_player[i].programmed_action = action_arg_direction; + + break; + } + + case CA_EXIT_PLAYER: { + for (i = 0; i < MAX_PLAYERS; i++) + if (action_arg_player_bits & (1 << i)) + stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE; + + break; + } + + case CA_KILL_PLAYER: + { + for (i = 0; i < MAX_PLAYERS; i++) + if (action_arg_player_bits & (1 << i)) + KillPlayer(&stored_player[i]); + + break; + } + + case CA_SET_PLAYER_KEYS: + { + int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE); int element = getSpecialActionElement(action_arg_element, action_arg_number, EL_KEY_1); @@ -6689,7 +7059,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { if (trigger_player_bits & (1 << i)) { - stored_player[i].key[KEY_NR(element)] = FALSE; + stored_player[i].key[KEY_NR(element)] = key_state; DrawGameValue_Keys(stored_player[i].key); @@ -6709,12 +7079,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { int move_stepsize = TILEX / stored_player[i].move_delay_value; - if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT) + if (action_arg == CA_ARG_SPEED_SLOWER || + action_arg == CA_ARG_SPEED_FASTER) { - /* translate "+" and "-" to "*" and "/" with powers of two */ - action_arg_number = 1 << action_arg_number; - action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY : - CA_MODE_DIVIDE); + action_arg_number = 2; + action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE : + CA_MODE_MULTIPLY); } move_stepsize = @@ -6730,50 +7100,72 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) /* do no immediately change -- the player might just be moving */ stored_player[i].move_delay_value_next = TILEX / move_stepsize; -#if 0 - printf("::: move_delay_value == %d [%d]\n", - stored_player[i].move_delay_value_next, action_arg_number); -#endif + stored_player[i].cannot_move = + (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE); } } break; } - case CA_SET_GEMS: + case CA_SET_PLAYER_SHIELD: { - local_player->gems_still_needed = action_arg_number_new; - - DrawGameValue_Emeralds(local_player->gems_still_needed); + for (i = 0; i < MAX_PLAYERS; i++) + { + if (trigger_player_bits & (1 << i)) + { + if (action_arg == CA_ARG_SHIELD_OFF) + { + stored_player[i].shield_normal_time_left = 0; + stored_player[i].shield_deadly_time_left = 0; + } + else if (action_arg == CA_ARG_SHIELD_NORMAL) + { + stored_player[i].shield_normal_time_left = 999999; + } + else if (action_arg == CA_ARG_SHIELD_DEADLY) + { + stored_player[i].shield_normal_time_left = 999999; + stored_player[i].shield_deadly_time_left = 999999; + } + } + } break; } - case CA_SET_TIME: + case CA_SET_PLAYER_ARTWORK: { - if (level.time > 0) /* only modify limited time value */ + for (i = 0; i < MAX_PLAYERS; i++) { - TimeLeft = action_arg_number_new; - - DrawGameValue_Time(TimeLeft); + if (trigger_player_bits & (1 << i)) + { + int artwork_element = action_arg_element; - if (!TimeLeft && setup.time_limit) - for (i = 0; i < MAX_PLAYERS; i++) - KillPlayer(&stored_player[i]); - } + if (action_arg == CA_ARG_ELEMENT_RESET) + artwork_element = + (level.use_artwork_element[i] ? level.artwork_element[i] : + stored_player[i].element_nr); - break; - } + stored_player[i].artwork_element = artwork_element; - case CA_SET_SCORE: - { - local_player->score = action_arg_number_new; + SetPlayerWaiting(&stored_player[i], FALSE); - DrawGameValue_Score(local_player->score); + /* set number of special actions for bored and sleeping animation */ + stored_player[i].num_special_action_bored = + get_num_special_action(artwork_element, + ACTION_BORING_1, ACTION_BORING_LAST); + stored_player[i].num_special_action_sleeping = + get_num_special_action(artwork_element, + ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); + } + } break; } + /* ---------- CE actions ---------------------------------------------- */ + case CA_SET_CE_SCORE: { ei->collect_score = action_arg_number_new; @@ -6781,73 +7173,31 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } - case CA_SET_CE_COUNT: + case CA_SET_CE_VALUE: { -#if USE_NEW_COLLECT_COUNT - int count_last = Count[x][y]; +#if USE_NEW_CUSTOM_VALUE + int last_custom_value = CustomValue[x][y]; - Count[x][y] = action_arg_number_new; + CustomValue[x][y] = action_arg_number_new; #if 0 - printf("::: Count == %d\n", Count[x][y]); + printf("::: Count == %d\n", CustomValue[x][y]); #endif - if (Count[x][y] == 0 && count_last > 0) + if (CustomValue[x][y] == 0 && last_custom_value > 0) { #if 0 - printf("::: CE_COUNT_AT_ZERO\n"); + printf("::: CE_VALUE_GETS_ZERO\n"); #endif - CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO); - CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X); + CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); + CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); } #endif break; } - case CA_SET_DYNABOMB_NUMBER: - { - printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n"); - - break; - } - - case CA_SET_DYNABOMB_SIZE: - { - printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n"); - - break; - } - - case CA_SET_DYNABOMB_POWER: - { - printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n"); - - break; - } - - case CA_TOGGLE_PLAYER_GRAVITY: - { - game.gravity = !game.gravity; - - break; - } - - case CA_ENABLE_PLAYER_GRAVITY: - { - game.gravity = TRUE; - - break; - } - - case CA_DISABLE_PLAYER_GRAVITY: - { - game.gravity = FALSE; - - break; - } - default: break; } @@ -6857,6 +7207,9 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, int x, int y, int target_element) { int previous_move_direction = MovDir[x][y]; +#if USE_NEW_CUSTOM_VALUE + int last_ce_value = CustomValue[x][y]; +#endif boolean add_player = (ELEM_IS_PLAYER(target_element) && IS_WALKABLE(Feld[x][y])); @@ -6884,6 +7237,11 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; +#if USE_NEW_CUSTOM_VALUE + if (element_info[Feld[x][y]].use_last_ce_value) + CustomValue[x][y] = last_ce_value; +#endif + InitField_WithBug1(x, y, FALSE); DrawLevelField(x, y); @@ -6896,11 +7254,7 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, if (ELEM_IS_PLAYER(target_element)) RelocatePlayer(x, y, target_element); -#if 1 - Changed[x][y] = TRUE; /* ignore all further changes in this frame */ -#else - Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ -#endif + Changed[x][y]++; /* count number of changes in the same frame */ TestIfBadThingTouchesPlayer(x, y); TestIfPlayerTouchesCustomElement(x, y); @@ -6922,23 +7276,15 @@ static boolean ChangeElementNow(int x, int y, int element, int page) /* reset actual trigger element, trigger player and action element */ change->actual_trigger_element = EL_EMPTY; change->actual_trigger_player = EL_PLAYER_1; + change->actual_trigger_side = CH_SIDE_NONE; + change->actual_trigger_ce_value = 0; } -#if 1 - /* do not change any elements that have already changed in this frame */ - if (Changed[x][y]) - return FALSE; -#else - /* do not change already changed elements with same change event */ - if (Changed[x][y] & ChangeEvent[x][y]) + /* do not change elements more than a specified maximum number of changes */ + if (Changed[x][y] >= game.max_num_changes_per_frame) return FALSE; -#endif -#if 1 - Changed[x][y] = TRUE; /* ignore all further changes in this frame */ -#else - Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ -#endif + Changed[x][y]++; /* count number of changes in the same frame */ if (change->explode) { @@ -6963,7 +7309,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page) boolean is_destructible; int ex = x + xx - 1; int ey = y + yy - 1; - int content_element = change->target_content[xx][yy]; + int content_element = change->target_content.e[xx][yy]; int e; can_replace[xx][yy] = TRUE; @@ -7035,7 +7381,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page) ChangeEvent[ex][ey] = ChangeEvent[x][y]; - content_element = change->target_content[xx][yy]; + content_element = change->target_content.e[xx][yy]; target_element = GET_TARGET_ELEMENT(content_element, change); ChangeElementNowExt(change, ex, ey, target_element); @@ -7049,7 +7395,10 @@ static boolean ChangeElementNow(int x, int y, int element, int page) } if (something_has_changed) + { PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page); + } } } else @@ -7059,14 +7408,17 @@ static boolean ChangeElementNow(int x, int y, int element, int page) ChangeElementNowExt(change, x, y, target_element); PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page); } /* this uses direct change before indirect change */ - CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page); + CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page); return TRUE; } +#if USE_NEW_DELAYED_ACTION + static void ChangeElement(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); @@ -7157,7 +7509,89 @@ static void ChangeElement(int x, int y, int page) } } -static boolean CheckTriggeredElementChangeExt(int trigger_element, +#else + +static void ChangeElement(int x, int y, int page) +{ + int element = MovingOrBlocked2Element(x, y); + struct ElementInfo *ei = &element_info[element]; + struct ElementChangeInfo *change = &ei->change_page[page]; + +#ifdef DEBUG + if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y])) + { + printf("\n\n"); + printf("ChangeElement(): %d,%d: element = %d ('%s')\n", + x, y, element, element_info[element].token_name); + printf("ChangeElement(): This should never happen!\n"); + printf("\n\n"); + } +#endif + + /* this can happen with classic bombs on walkable, changing elements */ + if (!CAN_CHANGE(element)) + { +#if 0 + if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */ + ChangeDelay[x][y] = 0; +#endif + + return; + } + + if (ChangeDelay[x][y] == 0) /* initialize element change */ + { + ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1; + + ResetGfxAnimation(x, y); + ResetRandomAnimationValue(x, y); + + if (change->pre_change_function) + change->pre_change_function(x, y); + } + + ChangeDelay[x][y]--; + + if (ChangeDelay[x][y] != 0) /* continue element change */ + { + int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + if (change->change_function) + change->change_function(x, y); + } + else /* finish element change */ + { + if (ChangePage[x][y] != -1) /* remember page from delayed change */ + { + page = ChangePage[x][y]; + ChangePage[x][y] = -1; + + change = &ei->change_page[page]; + } + + if (IS_MOVING(x, y)) /* never change a running system ;-) */ + { + ChangeDelay[x][y] = 1; /* try change after next move step */ + ChangePage[x][y] = page; /* remember page to use for change */ + + return; + } + + if (ChangeElementNow(x, y, element, page)) + { + if (change->post_change_function) + change->post_change_function(x, y); + } + } +} + +#endif + +static boolean CheckTriggeredElementChangeExt(int x, int y, + int trigger_element, int trigger_event, int trigger_player, int trigger_side, @@ -7193,6 +7627,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_element, { change->actual_trigger_element = trigger_element; change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); + change->actual_trigger_side = trigger_side; + change->actual_trigger_ce_value = CustomValue[x][y]; if ((change->can_change && !change_done) || change->has_action) { @@ -7208,12 +7644,18 @@ static boolean CheckTriggeredElementChangeExt(int trigger_element, ChangeEvent[x][y] = trigger_event; ChangeElement(x, y, p); } -#if 1 +#if USE_NEW_DELAYED_ACTION else if (change->has_action) + { ExecuteCustomElementAction(x, y, element, p); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); + } #else if (change->has_action) + { ExecuteCustomElementAction(x, y, element, p); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); + } #endif } } @@ -7272,6 +7714,8 @@ static boolean CheckElementChangeExt(int x, int y, { change->actual_trigger_element = trigger_element; change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); + change->actual_trigger_side = trigger_side; + change->actual_trigger_ce_value = CustomValue[x][y]; if (change->can_change && !change_done) { @@ -7281,12 +7725,18 @@ static boolean CheckElementChangeExt(int x, int y, change_done = TRUE; } -#if 1 +#if USE_NEW_DELAYED_ACTION else if (change->has_action) + { ExecuteCustomElementAction(x, y, element, p); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); + } #else if (change->has_action) + { ExecuteCustomElementAction(x, y, element, p); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); + } #endif } } @@ -7297,24 +7747,24 @@ static boolean CheckElementChangeExt(int x, int y, static void PlayPlayerSound(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; - int element = player->element_nr; + int sound_element = player->artwork_element; int last_action = player->last_action_waiting; int action = player->action_waiting; if (player->is_waiting) { if (action != last_action) - PlayLevelSoundElementAction(jx, jy, element, action); + PlayLevelSoundElementAction(jx, jy, sound_element, action); else - PlayLevelSoundElementActionIfLoop(jx, jy, element, action); + PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action); } else { if (action != last_action) - StopSound(element_info[element].sound[last_action]); + StopSound(element_info[sound_element].sound[last_action]); if (last_action == ACTION_SLEEPING) - PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING); + PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING); } } @@ -7381,7 +7831,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ? last_special_action + 1 : ACTION_SLEEPING); int special_graphic = - el_act_dir2img(player->element_nr, special_action, move_dir); + el_act_dir2img(player->artwork_element, special_action, move_dir); player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + @@ -7413,7 +7863,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) int special_action = ACTION_BORING_1 + SimpleRND(player->num_special_action_bored); int special_graphic = - el_act_dir2img(player->element_nr, special_action, move_dir); + el_act_dir2img(player->artwork_element, special_action, move_dir); player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + @@ -7716,7 +8166,7 @@ void GameActions() for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) { - Changed[x][y] = FALSE; + Changed[x][y] = 0; ChangeEvent[x][y] = -1; /* this must be handled before main playfield loop */ @@ -7727,6 +8177,20 @@ void GameActions() RemoveField(x, y); } +#if USE_NEW_SNAP_DELAY + if (Feld[x][y] == EL_ELEMENT_SNAPPING) + { + MovDelay[x][y]--; + if (MovDelay[x][y] <= 0) + { + RemoveField(x, y); + DrawLevelField(x, y); + + TestIfElementTouchesCustomElement(x, y); /* for empty space */ + } + } +#endif + #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) { @@ -7881,6 +8345,14 @@ void GameActions() CheckForDragon(x, y); else if (element == EL_EXPLOSION) ; /* drawing of correct explosion animation is handled separately */ + else if (element == EL_ELEMENT_SNAPPING) + { +#if 1 + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); + + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); +#endif + } else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); @@ -8016,6 +8488,22 @@ void GameActions() CloseAllOpenTimegates(); } + if (game.lenses_time_left > 0) + { + game.lenses_time_left--; + + if (game.lenses_time_left == 0) + RedrawAllInvisibleElementsForLenses(); + } + + if (game.magnify_time_left > 0) + { + game.magnify_time_left--; + + if (game.magnify_time_left == 0) + RedrawAllInvisibleElementsForMagnifier(); + } + for (i = 0; i < MAX_PLAYERS; i++) { struct PlayerInfo *player = &stored_player[i]; @@ -8279,11 +8767,30 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, player->MovDir = (dx < 0 ? MV_LEFT : dx > 0 ? MV_RIGHT : dy < 0 ? MV_UP : - dy > 0 ? MV_DOWN : MV_NO_MOVING); + dy > 0 ? MV_DOWN : MV_NONE); if (!IN_LEV_FIELD(new_jx, new_jy)) return MF_NO_ACTION; + if (player->cannot_move) + { +#if 1 + if (player->MovPos == 0) + { + player->is_moving = FALSE; + player->is_digging = FALSE; + player->is_collecting = FALSE; + player->is_snapping = FALSE; + player->is_pushing = FALSE; + } +#else + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); + SnapField(player, 0, 0); +#endif + + return MF_NO_ACTION; + } + if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) return MF_NO_ACTION; @@ -8368,7 +8875,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->move_delay = -1; /* set to "uninitialized" value */ /* store if player is automatically moved to next field */ - player->is_auto_moving = (player->programmed_action != MV_NO_MOVING); + player->is_auto_moving = (player->programmed_action != MV_NONE); /* remove the last programmed player action */ player->programmed_action = 0; @@ -8662,15 +9169,20 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CE_LEFT_BY_PLAYER, player->index_bit, leave_side); - CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X, + CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element, + CE_PLAYER_LEAVES_X, player->index_bit, leave_side); if (IS_CUSTOM_ELEMENT(new_element)) CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER, player->index_bit, enter_side); - CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X, + CheckTriggeredElementChangeByPlayer(jx, jy, new_element, + CE_PLAYER_ENTERS_X, player->index_bit, enter_side); + + CheckTriggeredElementChangeBySide(jx, jy, player->element_nr, + CE_MOVE_OF_X, move_direction); } if (game.engine_version >= VERSION_IDENT(3,0,7,0)) @@ -8741,7 +9253,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode) redraw_mask |= REDRAW_FIELD; } else - ScreenMovDir = MV_NO_MOVING; + ScreenMovDir = MV_NONE; } void TestIfPlayerTouchesCustomElement(int x, int y) @@ -8797,7 +9309,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER, player->index_bit, border_side); - CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X, + CheckTriggeredElementChangeByPlayer(xx, yy, border_element, + CE_PLAYER_TOUCHES_X, player->index_bit, border_side); } else if (IS_PLAYER(xx, yy)) @@ -8812,7 +9325,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER, player->index_bit, center_side); - CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X, + CheckTriggeredElementChangeByPlayer(x, y, center_element, + CE_PLAYER_TOUCHES_X, player->index_bit, center_side); break; } @@ -9015,7 +9529,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) continue; test_move_dir = - (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING); + (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE); test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y); @@ -9089,7 +9603,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) continue; test_move_dir = - (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING); + (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE); test_element = Feld[test_x][test_y]; @@ -9145,7 +9659,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) void TestIfPlayerTouchesBadThing(int x, int y) { - TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING); + TestIfGoodThingHitsBadThing(x, y, MV_NONE); } void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir) @@ -9155,7 +9669,7 @@ void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir) void TestIfBadThingTouchesPlayer(int x, int y) { - TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING); + TestIfBadThingHitsGoodThing(x, y, MV_NONE); } void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir) @@ -9165,12 +9679,12 @@ void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir) void TestIfFriendTouchesBadThing(int x, int y) { - TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING); + TestIfGoodThingHitsBadThing(x, y, MV_NONE); } void TestIfBadThingTouchesFriend(int x, int y) { - TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING); + TestIfBadThingHitsGoodThing(x, y, MV_NONE); } void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y) @@ -9244,7 +9758,7 @@ void BuryPlayer(struct PlayerInfo *player) if (!player->active) return; - PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING); + PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING); PlayLevelSound(jx, jy, SND_GAME_LOSING); player->GameOver = TRUE; @@ -9276,6 +9790,27 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } +#if USE_NEW_SNAP_DELAY +static void setFieldForSnapping(int x, int y, int element, int direction) +{ + struct ElementInfo *ei = &element_info[element]; + int direction_bit = MV_DIR_BIT(direction); + int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit]; + int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING : + IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING); + + Feld[x][y] = EL_ELEMENT_SNAPPING; + MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1; + + ResetGfxAnimation(x, y); + + GfxElement[x][y] = element; + GfxAction[x][y] = action; + GfxDir[x][y] = direction; + GfxFrame[x][y] = -1; +} +#endif + /* ============================================================================= checkDiagonalPushing() @@ -9322,10 +9857,10 @@ int DigField(struct PlayerInfo *player, int jx = oldx, jy = oldy; int dx = x - jx, dy = y - jy; int nextx = x + dx, nexty = y + dy; - int move_direction = (dx == -1 ? MV_LEFT : + int move_direction = (dx == -1 ? MV_LEFT : dx == +1 ? MV_RIGHT : - dy == -1 ? MV_UP : - dy == +1 ? MV_DOWN : MV_NO_MOVING); + dy == -1 ? MV_UP : + dy == +1 ? MV_DOWN : MV_NONE); int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = MV_DIR_OPPOSITE(move_direction); int old_element = Feld[jx][jy]; @@ -9370,18 +9905,24 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; /* field has no opening in this direction */ element = Feld[x][y]; -#if USE_NEW_COLLECT_COUNT - collect_count = Count[x][y]; +#if USE_NEW_CUSTOM_VALUE + +#if 1 + collect_count = element_info[element].collect_count_initial; +#else + collect_count = CustomValue[x][y]; +#endif + #else collect_count = element_info[element].collect_count_initial; #endif #if 0 if (element != EL_BLOCKED && - Count[x][y] != element_info[element].collect_count_initial) + CustomValue[x][y] != element_info[element].collect_count_initial) printf("::: %d: %d != %d\n", element, - Count[x][y], + CustomValue[x][y], element_info[element].collect_count_initial); #endif @@ -9390,7 +9931,17 @@ int DigField(struct PlayerInfo *player, if (mode == DF_SNAP && !IS_SNAPPABLE(element) && game.engine_version >= VERSION_IDENT(2,2,0,0)) + { + CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER, + player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + + if (Feld[x][y] != element) /* field changed by snapping */ + return MF_ACTION; + return MF_NO_ACTION; + } if (game.gravity && is_player && !player->is_auto_moving && canFallDown(player) && move_direction != MV_DOWN && @@ -9412,6 +9963,11 @@ int DigField(struct PlayerInfo *player, if (!player->key[RND_GATE_GRAY_NR(element)]) return MF_NO_ACTION; } + else if (IS_RND_GATE_GRAY_ACTIVE(element)) + { + if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)]) + return MF_NO_ACTION; + } else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) @@ -9427,7 +9983,7 @@ int DigField(struct PlayerInfo *player, if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED) PlayLevelSoundElementAction(x, y, sound_element, sound_action); else - PlayLevelSoundElementAction(x, y, player->element_nr, sound_action); + PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action); } else if (IS_PASSABLE(element) && canPassField(x, y, move_direction)) { @@ -9447,6 +10003,11 @@ int DigField(struct PlayerInfo *player, if (!player->key[EM_GATE_GRAY_NR(element)]) return MF_NO_ACTION; } + else if (IS_EM_GATE_GRAY_ACTIVE(element)) + { + if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)]) + return MF_NO_ACTION; + } else if (IS_SP_PORT(element)) { if (element == EL_SP_GRAVITY_PORT_LEFT || @@ -9490,11 +10051,23 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X, player->index_bit, dig_side); if (mode == DF_SNAP) - TestIfElementTouchesCustomElement(x, y); /* for empty space */ + { +#if USE_NEW_SNAP_DELAY + if (level.block_snap_field) + setFieldForSnapping(x, y, element, move_direction); + else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#endif + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + } } else if (IS_COLLECTIBLE(element)) { @@ -9512,14 +10085,14 @@ int DigField(struct PlayerInfo *player, } else if (element == EL_EXTRA_TIME && level.time > 0) { - TimeLeft += 10; + TimeLeft += level.extra_time; DrawGameValue_Time(TimeLeft); } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { - player->shield_normal_time_left += 10; + player->shield_normal_time_left += level.shield_normal_time; if (element == EL_SHIELD_DEADLY) - player->shield_deadly_time_left += 10; + player->shield_deadly_time_left += level.shield_deadly_time; } else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED) { @@ -9553,6 +10126,18 @@ int DigField(struct PlayerInfo *player, { player->show_envelope = element; } + else if (element == EL_EMC_LENSES) + { + game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND; + + RedrawAllInvisibleElementsForLenses(); + } + else if (element == EL_EMC_MAGNIFIER) + { + game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND; + + RedrawAllInvisibleElementsForMagnifier(); + } else if (IS_DROPPABLE(element) || IS_THROWABLE(element)) /* can be collected and dropped */ { @@ -9580,11 +10165,23 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); if (is_player) - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X, player->index_bit, dig_side); if (mode == DF_SNAP) - TestIfElementTouchesCustomElement(x, y); /* for empty space */ + { +#if USE_NEW_SNAP_DELAY + if (level.block_snap_field) + setFieldForSnapping(x, y, element, move_direction); + else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#endif + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + } } else if (IS_PUSHABLE(element)) { @@ -9713,7 +10310,7 @@ int DigField(struct PlayerInfo *player, { CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X, player->index_bit, dig_side); } } @@ -9721,7 +10318,7 @@ int DigField(struct PlayerInfo *player, { if (PLAYER_SWITCHING(player, x, y)) { - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); return MF_ACTION; @@ -9771,20 +10368,19 @@ int DigField(struct PlayerInfo *player, { ActivateTimegateSwitch(x, y); } - else if (element == EL_BALLOON_SWITCH_LEFT || + else if (element == EL_BALLOON_SWITCH_LEFT || element == EL_BALLOON_SWITCH_RIGHT || - element == EL_BALLOON_SWITCH_UP || - element == EL_BALLOON_SWITCH_DOWN || + element == EL_BALLOON_SWITCH_UP || + element == EL_BALLOON_SWITCH_DOWN || + element == EL_BALLOON_SWITCH_NONE || element == EL_BALLOON_SWITCH_ANY) { - if (element == EL_BALLOON_SWITCH_ANY) - game.balloon_dir = move_direction; - else - game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT : - element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT : - element == EL_BALLOON_SWITCH_UP ? MV_UP : - element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN : - MV_NO_MOVING); + game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT : + element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT : + element == EL_BALLOON_SWITCH_UP ? MV_UP : + element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN : + element == EL_BALLOON_SWITCH_NONE ? MV_NONE : + move_direction); } else if (element == EL_LAMP) { @@ -9797,17 +10393,24 @@ int DigField(struct PlayerInfo *player, else if (element == EL_TIME_ORB_FULL) { Feld[x][y] = EL_TIME_ORB_EMPTY; - TimeLeft += 10; - DrawGameValue_Time(TimeLeft); + + if (level.time > 0 || level.use_time_orb_bug) + { + TimeLeft += level.time_orb_time; + DrawGameValue_Time(TimeLeft); + } ResetGfxAnimation(x, y); DrawLevelField(x, y); } - CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X, + player->index_bit, dig_side); + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); return MF_ACTION; @@ -9822,13 +10425,18 @@ int DigField(struct PlayerInfo *player, CheckElementChangeByPlayer(x, y, element, CE_SWITCHED, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, + player->index_bit, dig_side); + + CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER, + player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X, player->index_bit, dig_side); } CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); return MF_NO_ACTION; @@ -9849,10 +10457,10 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) { int jx = player->jx, jy = player->jy; int x = jx + dx, y = jy + dy; - int snap_direction = (dx == -1 ? MV_LEFT : + int snap_direction = (dx == -1 ? MV_LEFT : dx == +1 ? MV_RIGHT : - dy == -1 ? MV_UP : - dy == +1 ? MV_DOWN : MV_NO_MOVING); + dy == -1 ? MV_UP : + dy == +1 ? MV_DOWN : MV_NONE); if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0)) return FALSE; @@ -9993,11 +10601,12 @@ boolean DropElement(struct PlayerInfo *player) PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING); /* needed if previous element just changed to "empty" in the last frame */ - Changed[dropx][dropy] = FALSE; /* allow another change */ + Changed[dropx][dropy] = 0; /* allow at least one more change */ CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER, player->index_bit, drop_side); - CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X, + CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element, + CE_PLAYER_DROPS_X, player->index_bit, drop_side); TestIfElementTouchesCustomElement(dropx, dropy); @@ -10032,7 +10641,7 @@ boolean DropElement(struct PlayerInfo *player) nextx = dropx + GET_DX_FROM_DIR(move_direction); nexty = dropy + GET_DY_FROM_DIR(move_direction); - Changed[dropx][dropy] = FALSE; /* allow another change */ + Changed[dropx][dropy] = 0; /* allow at least one more change */ CheckCollision[dropx][dropy] = 2; } @@ -10383,7 +10992,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_SHIELD]); break; case EL_EXTRA_TIME: - RaiseScore(level.score[SC_TIME_BONUS]); + RaiseScore(level.extra_time_score); break; case EL_KEY_1: case EL_KEY_2: