X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=aec9cfc3fbbf266cf1c0e4daf5d2df3edb457027;hb=eecbe2a298826a3c32ca78b81b77491702ced84c;hp=8472c674ec32e1fba6a1e33a6d4cf8b205375759;hpb=83adf00a85bdb806048eb96c1266d81c0d352ce9;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 8472c674..aec9cfc3 100644 --- a/src/game.c +++ b/src/game.c @@ -25,10 +25,15 @@ #define USE_NEW_AMOEBA_CODE FALSE /* EXPERIMENTAL STUFF */ -#define USE_NEW_STUFF (TRUE * 1) - -#define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1) +#define USE_NEW_STUFF ( 1) +#define USE_NEW_SP_SLIPPERY (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) /* for DigField() */ #define DF_NO_PUSH 0 @@ -88,16 +93,25 @@ #define INITIAL_MOVE_DELAY_ON 0 /* values for player movement speed (which is in fact a delay value) */ +#define MOVE_DELAY_MIN_SPEED 32 #define MOVE_DELAY_NORMAL_SPEED 8 #define MOVE_DELAY_HIGH_SPEED 4 +#define MOVE_DELAY_MAX_SPEED 1 +#if 0 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x)) #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x)) +#else +#define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x)) +#define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x)) +#endif #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value)) #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value)) /* values for other actions */ #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED) +#define MOVE_STEPSIZE_MIN (1) +#define MOVE_STEPSIZE_MAX (TILEX) #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0) #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0) @@ -112,6 +126,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)) @@ -119,9 +135,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,8 +245,8 @@ static void InitBeltMovement(void); static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *); -static void KillHeroUnlessEnemyProtected(int, int); -static void KillHeroUnlessExplosionProtected(int, int); +static void KillPlayerUnlessEnemyProtected(int, int); +static void KillPlayerUnlessExplosionProtected(int, int); static void TestIfPlayerTouchesCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); @@ -246,15 +259,13 @@ static void ChangeElement(int, int, int); 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) + 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) + 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) \ @@ -571,7 +582,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]; @@ -646,7 +657,7 @@ static int getBeltDirFromBeltSwitchElement(int element) static int belt_move_dir[3] = { MV_LEFT, - MV_NO_MOVING, + MV_NONE, MV_RIGHT }; @@ -879,8 +890,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) + 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; @@ -905,6 +929,20 @@ static void InitField(int x, int y, boolean init_game) } break; } + +#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 } static inline void InitField_WithBug1(int x, int y, boolean init_game) @@ -1248,12 +1286,29 @@ 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); SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE); } + /* ---------- initialize internal run-time variables ------------- */ + + 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++) + { + ei->change_page[j].can_change_or_has_action = + (ei->change_page[j].can_change | + ei->change_page[j].has_action); + } + } + /* add change events from custom element configuration */ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { @@ -1261,7 +1316,7 @@ static void InitGameEngine() for (j = 0; j < ei->num_change_pages; j++) { - if (!ei->change_page[j].can_change) + if (!ei->change_page[j].can_change_or_has_action) continue; for (k = 0; k < NUM_CHANGE_EVENTS; k++) @@ -1278,20 +1333,6 @@ static void InitGameEngine() } } - /* ---------- initialize internal run-time variables ------------- */ - - 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++) - { - ei->change_page[j].can_change_or_has_action = - (ei->change_page[j].can_change | - ei->change_page[j].has_action); - } - } - /* ---------- initialize run-time trigger player and element ------------- */ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -1302,6 +1343,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; } } @@ -1319,7 +1362,7 @@ static void InitGameEngine() for (j = 0; j < ei->num_change_pages; j++) { - if (!ei->change_page[j].can_change) + if (!ei->change_page[j].can_change_or_has_action) continue; if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION]) @@ -1398,16 +1441,23 @@ static void InitGameEngine() element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize; } - /* ---------- initialize gem count --------------------------------------- */ + /* ---------- initialize collect score ----------------------------------- */ - /* initialize gem count values for each element */ + /* initialize collect score values for custom elements from initial value */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + if (IS_CUSTOM_ELEMENT(i)) + element_info[i].collect_score = element_info[i].collect_score_initial; + + /* ---------- initialize collect count ----------------------------------- */ + + /* initialize collect count values for non-custom elements */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) if (!IS_CUSTOM_ELEMENT(i)) - element_info[i].collect_count = 0; + element_info[i].collect_count_initial = 0; - /* add gem count values for all elements from pre-defined list */ + /* add collect 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 = + element_info[collect_count_list[i].element].collect_count_initial = collect_count_list[i].count; /* ---------- initialize access direction -------------------------------- */ @@ -1473,10 +1523,10 @@ 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; @@ -1492,7 +1542,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; @@ -1507,6 +1557,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; @@ -1562,6 +1614,8 @@ void InitGame() player->move_delay = game.initial_move_delay; player->move_delay_value = game.initial_move_delay_value; + player->move_delay_value_next = -1; + player->move_delay_reset_counter = 0; player->push_delay = -1; /* initialized when pushing starts */ @@ -1590,7 +1644,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; @@ -1602,7 +1656,7 @@ void InitGame() TimeLeft = level.time; TapeTime = 0; - ScreenMovDir = MV_NO_MOVING; + ScreenMovDir = MV_NONE; ScreenMovPos = 0; ScreenGfxPos = 0; @@ -1616,7 +1670,7 @@ 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; @@ -1624,7 +1678,7 @@ void InitGame() 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 */ } @@ -1639,6 +1693,9 @@ void InitGame() MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; +#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; WasJustMoving[x][y] = 0; @@ -1661,7 +1718,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; } } @@ -1686,6 +1743,26 @@ void InitGame() emulate_sb ? EMU_SOKOBAN : emulate_sp ? EMU_SUPAPLEX : EMU_NONE); +#if USE_NEW_ALL_SLIPPERY + /* initialize type of slippery elements */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + if (!IS_CUSTOM_ELEMENT(i)) + { + /* default: elements slip down either to the left or right randomly */ + element_info[i].slippery_type = SLIPPERY_ANY_RANDOM; + + /* SP style elements prefer to slip down on the left side */ + if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i)) + element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; + + /* BD style elements prefer to slip down on the left side */ + if (game.emulation == EMU_BOULDERDASH) + element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; + } + } +#endif + /* initialize explosion and ignition delay */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) { @@ -1716,7 +1793,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 */ @@ -1852,6 +1929,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++) { @@ -1860,6 +1938,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; @@ -1867,6 +1956,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); @@ -1883,7 +1973,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)) @@ -1900,7 +1991,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)) @@ -2095,7 +2189,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; @@ -2226,12 +2320,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); @@ -2254,12 +2353,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); @@ -2286,7 +2390,7 @@ void GameWon() PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); } - /* Hero disappears */ + /* player disappears */ if (ExitX >= 0 && ExitY >= 0) DrawLevelField(ExitX, ExitY); @@ -2481,6 +2585,11 @@ void InitMovingField(int x, int y, int direction) Feld[newx][newy] = EL_BLOCKED; MovDir[newx][newy] = MovDir[x][y]; + +#if USE_NEW_CUSTOM_VALUE + CustomValue[newx][newy] = CustomValue[x][y]; +#endif + GfxFrame[newx][newy] = GfxFrame[x][y]; GfxRandom[newx][newy] = GfxRandom[x][y]; GfxAction[newx][newy] = GfxAction[x][y]; @@ -2562,6 +2671,10 @@ static void RemoveField(int x, int y) MovDir[x][y] = 0; MovDelay[x][y] = 0; +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; +#endif + AmoebaNr[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; @@ -2573,7 +2686,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) @@ -2771,8 +2884,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); @@ -2846,15 +2960,14 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) if (player == local_player) /* only visually relocate local player */ DrawRelocatePlayer(player); - TestIfHeroTouchesBadThing(jx, jy); + TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); if (IS_CUSTOM_ELEMENT(element)) CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER, player->index_bit, enter_side); - CheckTriggeredElementChangeByPlayer(jx, jy, element, - CE_PLAYER_ENTERS_X, + CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); } @@ -3009,10 +3122,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) @@ -3030,7 +3143,7 @@ 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; @@ -3098,7 +3211,7 @@ void Explode(int ex, int ey, int phase, int mode) if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present && !PLAYER_EXPLOSION_PROTECTED(x, y)) { - KillHeroUnlessExplosionProtected(x, y); + KillPlayerUnlessExplosionProtected(x, y); border_explosion = TRUE; } else if (CAN_EXPLODE_BY_EXPLOSION(border_element)) @@ -3146,10 +3259,14 @@ 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_CUSTOM_VALUE + CustomValue[x][y] = 0; +#endif + InitField_WithBug2(x, y, FALSE); DrawLevelField(x, y); @@ -3355,7 +3472,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; @@ -3398,9 +3515,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]; @@ -3446,7 +3563,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); @@ -3458,7 +3575,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); @@ -3688,7 +3805,7 @@ void Impact(int x, int y) if (impact && element == EL_AMOEBA_DROP) { if (object_hit && IS_PLAYER(x, y + 1)) - KillHeroUnlessEnemyProtected(x, y + 1); + KillPlayerUnlessEnemyProtected(x, y + 1); else if (object_hit && smashed == EL_PENGUIN) Bang(x, y + 1); else @@ -3730,7 +3847,7 @@ void Impact(int x, int y) { if (CAN_SMASH_PLAYER(element)) { - KillHeroUnlessEnemyProtected(x, y + 1); + KillPlayerUnlessEnemyProtected(x, y + 1); return; } } @@ -3822,8 +3939,8 @@ void Impact(int x, int y) CheckElementChangeBySide(x, y + 1, smashed, element, CE_SWITCHED, CH_SIDE_TOP); - CheckTriggeredElementChangeBySide(x, y + 1, smashed, - CE_SWITCH_OF_X, CH_SIDE_TOP); + CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X, + CH_SIDE_TOP); } } else @@ -4111,7 +4228,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) @@ -4119,7 +4236,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; } @@ -4188,7 +4305,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) @@ -4325,6 +4442,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)) @@ -4378,7 +4500,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) @@ -4424,7 +4546,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; } @@ -4452,7 +4574,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; @@ -4701,7 +4823,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; @@ -4733,11 +4855,26 @@ void StartMoving(int x, int y) Feld[x + 1][y + 1] == EL_ACID)); boolean can_fall_any = (can_fall_left || can_fall_right); boolean can_fall_both = (can_fall_left && can_fall_right); + int slippery_type = element_info[Feld[x][y + 1]].slippery_type; - if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) +#if USE_NEW_ALL_SLIPPERY + if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM) { - int slippery_type = element_info[Feld[x][y + 1]].slippery_type; + if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) + can_fall_right = FALSE; + else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both) + can_fall_left = FALSE; + else if (slippery_type == SLIPPERY_ONLY_LEFT) + can_fall_right = FALSE; + else if (slippery_type == SLIPPERY_ONLY_RIGHT) + can_fall_left = FALSE; + can_fall_any = (can_fall_left || can_fall_right); + can_fall_both = FALSE; + } +#else + if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) + { if (slippery_type == SLIPPERY_ONLY_LEFT) can_fall_right = FALSE; else if (slippery_type == SLIPPERY_ONLY_RIGHT) @@ -4750,7 +4887,10 @@ void StartMoving(int x, int y) can_fall_any = (can_fall_left || can_fall_right); can_fall_both = (can_fall_left && can_fall_right); } +#endif +#if USE_NEW_ALL_SLIPPERY +#else #if USE_NEW_SP_SLIPPERY /* !!! better use the same properties as for custom elements here !!! */ else if (game.engine_version >= VERSION_IDENT(3,1,1,0) && @@ -4760,7 +4900,19 @@ void StartMoving(int x, int y) can_fall_both = FALSE; } #endif +#endif +#if USE_NEW_ALL_SLIPPERY + if (can_fall_both) + { + if (element == EL_BD_ROCK || element == EL_BD_DIAMOND) + can_fall_right = FALSE; /* slip down on left side */ + else + can_fall_left = !(can_fall_right = RND(2)); + + can_fall_both = FALSE; + } +#else if (can_fall_both) { if (game.emulation == EMU_BOULDERDASH || @@ -4771,6 +4923,7 @@ void StartMoving(int x, int y) can_fall_both = FALSE; } +#endif if (can_fall_any) { @@ -4812,7 +4965,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 @@ -4822,7 +4975,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); @@ -4973,7 +5126,7 @@ void StartMoving(int x, int y) IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) && !PLAYER_ENEMY_PROTECTED(newx, newy)) { - TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]); + TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]); return; } @@ -5009,7 +5162,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)) { @@ -5274,7 +5427,7 @@ void StartMoving(int x, int y) DrawLevelElementAnimation(x, y, element); if (DONT_TOUCH(element)) - TestIfBadThingTouchesHero(x, y); + TestIfBadThingTouchesPlayer(x, y); return; } @@ -5288,11 +5441,6 @@ void StartMoving(int x, int y) ContinueMoving(x, y); } -/* (emacs is confused here for some reason; this makes it happy again ;-) ) */ -void dummy() -{ -} - void ContinueMoving(int x, int y) { int element = Feld[x][y]; @@ -5357,6 +5505,10 @@ void ContinueMoving(int x, int y) if (!game.magic_wall_active) Feld[x][y] = EL_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; + +#if USE_NEW_CUSTOM_VALUE + InitField(newx, newy, FALSE); +#endif } else if (element == EL_BD_MAGIC_WALL_FILLING) { @@ -5371,6 +5523,10 @@ void ContinueMoving(int x, int y) if (!game.magic_wall_active) Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; + +#if USE_NEW_CUSTOM_VALUE + InitField(newx, newy, FALSE); +#endif } else if (element == EL_AMOEBA_DROPPING) { @@ -5392,6 +5548,7 @@ void ContinueMoving(int x, int y) MovPos[x][y] = 0; MovDir[x][y] = 0; MovDelay[x][y] = 0; + MovDelay[newx][newy] = 0; if (CAN_CHANGE(element)) @@ -5401,6 +5558,10 @@ void ContinueMoving(int x, int y) ChangePage[newx][newy] = ChangePage[x][y]; Changed[newx][newy] = Changed[x][y]; ChangeEvent[newx][newy] = ChangeEvent[x][y]; + +#if USE_NEW_CUSTOM_VALUE + CustomValue[newx][newy] = CustomValue[x][y]; +#endif } ChangeDelay[x][y] = 0; @@ -5408,6 +5569,10 @@ void ContinueMoving(int x, int y) Changed[x][y] = FALSE; ChangeEvent[x][y] = -1; +#if USE_NEW_CUSTOM_VALUE + CustomValue[x][y] = 0; +#endif + /* copy animation control values to new field */ GfxFrame[newx][newy] = GfxFrame[x][y]; GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */ @@ -5484,7 +5649,7 @@ void ContinueMoving(int x, int y) if (DONT_TOUCH(element)) /* object may be nasty to player or others */ { - TestIfBadThingTouchesHero(newx, newy); + TestIfBadThingTouchesPlayer(newx, newy); TestIfBadThingTouchesFriend(newx, newy); if (!IS_CUSTOM_ELEMENT(element)) @@ -5924,10 +6089,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)) @@ -5970,7 +6139,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]) @@ -5981,7 +6151,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); @@ -6174,7 +6345,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); } } @@ -6400,14 +6571,15 @@ static int getSpecialActionElement(int element, int number, int base_element) EL_EMPTY); } -static int getModifiedActionNumber(int value_old, int value_min, int value_max, - int operator, int operand) +static int getModifiedActionNumber(int value_old, int operator, int operand, + int value_min, int value_max) { - int value_new = (operator == CA_MODE_ADD ? value_old + operand : + int value_new = (operator == CA_MODE_SET ? operand : + operator == CA_MODE_ADD ? value_old + operand : operator == CA_MODE_SUBTRACT ? value_old - operand : operator == CA_MODE_MULTIPLY ? value_old * operand : operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) : - operator == CA_MODE_SET ? operand : + operator == CA_MODE_MODULO ? value_old % MAX(1, operand) : value_old); return (value_new < value_min ? value_min : @@ -6415,7 +6587,7 @@ static int getModifiedActionNumber(int value_old, int value_min, int value_max, value_new); } -static void ExecuteCustomElementAction(int element, int page) +static void ExecuteCustomElementAction(int x, int y, int element, int page) { struct ElementInfo *ei = &element_info[element]; struct ElementChangeInfo *change = &ei->change_page[page]; @@ -6427,7 +6599,11 @@ static void ExecuteCustomElementAction(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 : @@ -6435,35 +6611,89 @@ static void ExecuteCustomElementAction(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_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_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_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 : +#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 = (action_arg <= CA_ARG_MAX ? action_arg : - action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN : - action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX : + 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_CE_SCORE ? ei->collect_score : - action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count : +#if USE_NEW_CUSTOM_VALUE + action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] : +#else + 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); - /* (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)) : + int action_arg_number_old = + (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_VALUE ? CustomValue[x][y] : 0); - /* (for implicit player choice, set invalid value to "all players") */ + int action_arg_number_new = + getModifiedActionNumber(action_arg_number_old, + action_mode, action_arg_number, + action_arg_number_min, action_arg_number_max); + 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) { @@ -6472,23 +6702,7 @@ static void ExecuteCustomElementAction(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)) - KillHero(&stored_player[i]); - - break; - } + /* ---------- level actions ------------------------------------------- */ case CA_RESTART_LEVEL: { @@ -6508,183 +6722,213 @@ static void ExecuteCustomElementAction(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: { - int element = getSpecialActionElement(action_arg_element, - action_arg_number, EL_KEY_1); - - if (IS_KEY(element)) - { - for (i = 0; i < MAX_PLAYERS; i++) - { - if (trigger_player_bits & (1 << i)) - { - stored_player[i].key[KEY_NR(element)] = FALSE; + local_player->score = action_arg_number_new; - DrawGameValue_Keys(stored_player[i].key); - - redraw_mask |= REDRAW_DOOR_1; - } - } - } + DrawGameValue_Score(local_player->score); break; } - case CA_SET_PLAYER_SPEED: + case CA_SET_LEVEL_GEMS: { - for (i = 0; i < MAX_PLAYERS; i++) - { - if (trigger_player_bits & (1 << i)) - { - if (action_arg == CA_ARG_NUMBER_RESET) - stored_player[i].move_delay_value = game.initial_move_delay_value; - else if (action_arg == CA_ARG_NUMBER_NORMAL) - stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED; - else if (action_arg == CA_ARG_NUMBER_MIN) - stored_player[i].move_delay_value = 16; - else if (action_arg == CA_ARG_NUMBER_MAX) - stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED; - else - { -#if 0 - if (action_mode == CA_MODE_ADD) - { - action_mode = CA_MODE_DIVIDE; - action_arg_number = (1 << action_arg_number); - } - else if (action_mode == CA_MODE_SUBTRACT) - { - action_mode = CA_MODE_MULTIPLY; - action_arg_number = (1 << action_arg_number); - } - - int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE : - action_mode == CA_MODE_DIVIDE ? CA_MODE_MULTIPLY : - action_mode); + local_player->gems_still_needed = action_arg_number_new; - stored_player[i].move_delay_value = - getModifiedActionNumber(stored_player[i].move_delay_value, - 1, 16, - action_mode, action_arg_number); -#endif - } - } - } + DrawGameValue_Emeralds(local_player->gems_still_needed); break; } - case CA_SET_GEMS: + case CA_SET_LEVEL_GRAVITY: { - local_player->gems_still_needed = - getModifiedActionNumber(local_player->gems_still_needed, 0, 999, - action_mode, action_arg_number); - - DrawGameValue_Emeralds(local_player->gems_still_needed); - + 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_TIME: + case CA_SET_LEVEL_WIND: { - if (level.time > 0) /* only modify limited time value */ - { - TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999, - action_mode, action_arg_number); - - DrawGameValue_Time(TimeLeft); - } + game.wind_direction = action_arg_direction; break; } - case CA_SET_SCORE: - { - local_player->score = - getModifiedActionNumber(local_player->score, 0, 99999, - action_mode, action_arg_number); + /* ---------- player actions ------------------------------------------ */ - DrawGameValue_Score(local_player->score); + 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_SET_CE_SCORE: + case CA_EXIT_PLAYER: { - printf("::: CA_SET_CE_SCORE -- not yet implemented\n"); + 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_SET_CE_COUNT: + case CA_KILL_PLAYER: { - printf("::: CA_SET_CE_COUNT -- not yet implemented\n"); + for (i = 0; i < MAX_PLAYERS; i++) + if (action_arg_player_bits & (1 << i)) + KillPlayer(&stored_player[i]); break; } - case CA_SET_DYNABOMB_NUMBER: + case CA_SET_PLAYER_KEYS: { - printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n"); + int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE); + int element = getSpecialActionElement(action_arg_element, + action_arg_number, EL_KEY_1); + + if (IS_KEY(element)) + { + for (i = 0; i < MAX_PLAYERS; i++) + { + if (trigger_player_bits & (1 << i)) + { + stored_player[i].key[KEY_NR(element)] = key_state; + + DrawGameValue_Keys(stored_player[i].key); + + redraw_mask |= REDRAW_DOOR_1; + } + } + } break; } - case CA_SET_DYNABOMB_SIZE: + case CA_SET_PLAYER_SPEED: { - printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n"); + for (i = 0; i < MAX_PLAYERS; i++) + { + if (trigger_player_bits & (1 << i)) + { + int move_stepsize = TILEX / stored_player[i].move_delay_value; + + if (action_arg == CA_ARG_SPEED_SLOWER || + action_arg == CA_ARG_SPEED_FASTER) + { + action_arg_number = 2; + action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE : + CA_MODE_MULTIPLY); + } + + move_stepsize = + getModifiedActionNumber(move_stepsize, + action_mode, + action_arg_number, + action_arg_number_min, + action_arg_number_max); + + /* make sure that value is power of 2 */ + move_stepsize = (1 << log_2(move_stepsize)); + + /* do no immediately change -- the player might just be moving */ + stored_player[i].move_delay_value_next = TILEX / move_stepsize; + + stored_player[i].cannot_move = + (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE); + } + } break; } - case CA_SET_DYNABOMB_POWER: + case CA_SET_PLAYER_SHIELD: { - printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n"); + 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_TOGGLE_PLAYER_GRAVITY: + case CA_SET_PLAYER_ARTWORK: { - game.gravity = !game.gravity; + for (i = 0; i < MAX_PLAYERS; i++) + { + int element = action_arg_element; + } break; } - case CA_ENABLE_PLAYER_GRAVITY: + /* ---------- CE actions ---------------------------------------------- */ + + case CA_SET_CE_SCORE: { - game.gravity = TRUE; + ei->collect_score = action_arg_number_new; break; } - case CA_DISABLE_PLAYER_GRAVITY: + case CA_SET_CE_VALUE: { - game.gravity = FALSE; +#if USE_NEW_CUSTOM_VALUE + int last_custom_value = CustomValue[x][y]; + + CustomValue[x][y] = action_arg_number_new; + +#if 0 + printf("::: Count == %d\n", CustomValue[x][y]); +#endif + + if (CustomValue[x][y] == 0 && last_custom_value > 0) + { +#if 0 + printf("::: CE_VALUE_GETS_ZERO\n"); +#endif + + CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); + CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); + } +#endif break; } @@ -6698,6 +6942,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])); @@ -6725,6 +6972,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); @@ -6743,7 +6995,7 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ #endif - TestIfBadThingTouchesHero(x, y); + TestIfBadThingTouchesPlayer(x, y); TestIfPlayerTouchesCustomElement(x, y); TestIfElementTouchesCustomElement(x, y); } @@ -6763,6 +7015,8 @@ 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 @@ -6804,7 +7058,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; @@ -6876,7 +7130,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); @@ -6890,7 +7144,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 @@ -6900,6 +7157,7 @@ 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 */ @@ -6908,6 +7166,100 @@ static boolean ChangeElementNow(int x, int y, int element, int page) return TRUE; } +#if USE_NEW_DELAYED_ACTION + +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_OR_HAS_ACTION(element) && + !CAN_CHANGE_OR_HAS_ACTION(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_OR_HAS_ACTION(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; + + if (change->can_change) + { + 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 */ + { + if (change->can_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 (change->can_change) + { + if (ChangeElementNow(x, y, element, page)) + { + if (change->post_change_function) + change->post_change_function(x, y); + } + } + + if (change->has_action) + ExecuteCustomElementAction(x, y, element, page); + } +} + +#else + static void ChangeElement(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); @@ -6985,7 +7337,9 @@ static void ChangeElement(int x, int y, int page) } } -static boolean CheckTriggeredElementChangeExt(int lx, int ly, +#endif + +static boolean CheckTriggeredElementChangeExt(int x, int y, int trigger_element, int trigger_event, int trigger_player, @@ -6994,7 +7348,7 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly, { boolean change_done_any = FALSE; int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page); - int i, p, x, y; + int i; if (!(trigger_events[trigger_element][trigger_event])) return FALSE; @@ -7003,6 +7357,7 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly, { int element = EL_CUSTOM_START + i; boolean change_done = FALSE; + int p; if (!CAN_CHANGE_OR_HAS_ACTION(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event)) @@ -7021,25 +7376,45 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly, { 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) + if ((change->can_change && !change_done) || change->has_action) { + int x, y; + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) { if (Feld[x][y] == element) { - ChangeDelay[x][y] = 1; - ChangeEvent[x][y] = trigger_event; - ChangeElement(x, y, p); + if (change->can_change && !change_done) + { + ChangeDelay[x][y] = 1; + ChangeEvent[x][y] = trigger_event; + ChangeElement(x, y, p); + } +#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 } } - change_done = TRUE; - change_done_any = TRUE; + if (change->can_change) + { + change_done = TRUE; + change_done_any = TRUE; + } } - - if (change->has_action) - ExecuteCustomElementAction(element, p); } } } @@ -7088,6 +7463,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) { @@ -7097,9 +7474,19 @@ static boolean CheckElementChangeExt(int x, int y, change_done = TRUE; } - +#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(element, p); + { + ExecuteCustomElementAction(x, y, element, p); + PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); + } +#endif } } @@ -7340,12 +7727,25 @@ void AdvanceFrameAndPlayerCounters(int player_nr) for (i = 0; i < MAX_PLAYERS; i++) { boolean advance_player_counters = (player_nr == -1 || player_nr == i); - int move_frames = - MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value; + int move_delay_value = stored_player[i].move_delay_value; + int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value; if (!advance_player_counters) /* not all players may be affected */ continue; +#if USE_NEW_PLAYER_ANIM + if (move_frames == 0) /* less than one move per game frame */ + { + int stepsize = TILEX / move_delay_value; + int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED; + int count = (stored_player[i].is_moving ? + ABS(stored_player[i].MovPos) / stepsize : FrameCounter); + + if (count % delay == 0) + move_frames = 1; + } +#endif + stored_player[i].Frame += move_frames; if (stored_player[i].MovPos != 0) @@ -7526,6 +7926,18 @@ 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); + } + } +#endif + #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) { @@ -7599,11 +8011,25 @@ void GameActions() if (IS_CHANGING(x, y) && (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y])) { + int page = element_info[element].event_page_nr[CE_DELAY]; #if 0 - ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : - element_info[element].event_page_nr[CE_DELAY]); + ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page); #else - ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]); + +#if 0 + printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]); +#endif + +#if 1 + ChangeElement(x, y, page); +#else + if (CAN_CHANGE(element)) + ChangeElement(x, y, page); + + if (HAS_ACTION(element)) + ExecuteCustomElementAction(x, y, element, page); +#endif + #endif element = Feld[x][y]; @@ -7666,6 +8092,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); @@ -7847,7 +8281,7 @@ void GameActions() if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) - KillHero(&stored_player[i]); + KillPlayer(&stored_player[i]); } else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ DrawGameValue_Time(TimePlayed); @@ -8064,11 +8498,19 @@ 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) + { + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); + SnapField(player, 0, 0); + + return MF_NO_ACTION; + } + if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) return MF_NO_ACTION; @@ -8083,10 +8525,10 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, InitMovingField(jx, jy, MV_DOWN); Store[jx][jy] = EL_ACID; ContinueMoving(jx, jy); - BuryHero(player); + BuryPlayer(player); } else - TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir); + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); return MF_MOVING; } @@ -8106,6 +8548,12 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, player->jy = new_jy; StorePlayer[new_jx][new_jy] = player->element_nr; + if (player->move_delay_value_next != -1) + { + player->move_delay_value = player->move_delay_value_next; + player->move_delay_value_next = -1; + } + player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value); @@ -8147,7 +8595,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; @@ -8301,12 +8749,12 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) if (game.engine_version < VERSION_IDENT(3,0,7,0)) { - TestIfHeroTouchesBadThing(jx, jy); + TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); } if (!player->active) - RemoveHero(player); + RemovePlayer(player); return moved; } @@ -8317,8 +8765,16 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) int last_jx = player->last_jx, last_jy = player->last_jy; int move_stepsize = TILEX / player->move_delay_value; - if (!player->active || !player->MovPos) +#if USE_NEW_PLAYER_SPEED + if (!player->active) + return; + + if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */ + return; +#else + if (!player->active || player->MovPos == 0) return; +#endif if (mode == SCROLL_INIT) { @@ -8348,20 +8804,47 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) MovDelay[last_jx][last_jy] = last_field_block_delay + 1; } +#if USE_NEW_PLAYER_SPEED + if (player->MovPos != 0) /* player has not yet reached destination */ + return; +#else return; +#endif } else if (!FrameReached(&player->actual_frame_counter, 1)) return; +#if 0 + printf("::: player->MovPos: %d -> %d\n", + player->MovPos, + player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize); +#endif + +#if USE_NEW_PLAYER_SPEED + if (player->MovPos != 0) + { + player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; + player->GfxPos = move_stepsize * (player->MovPos / move_stepsize); + + /* before DrawPlayer() to draw correct player graphic for this case */ + if (player->MovPos == 0) + CheckGravityMovement(player); + } +#else player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; player->GfxPos = move_stepsize * (player->MovPos / move_stepsize); /* before DrawPlayer() to draw correct player graphic for this case */ if (player->MovPos == 0) CheckGravityMovement(player); +#endif if (player->MovPos == 0) /* player reached destination field */ { +#if 0 + printf("::: player reached destination field\n"); +#endif + if (player->move_delay_reset_counter > 0) { player->move_delay_reset_counter--; @@ -8384,7 +8867,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { DrawPlayer(player); /* needed here only to cleanup last field */ - RemoveHero(player); + RemovePlayer(player); if (local_player->friends_still_needed == 0 || IS_SP_ELEMENT(Feld[jx][jy])) @@ -8421,7 +8904,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (game.engine_version >= VERSION_IDENT(3,0,7,0)) { - TestIfHeroTouchesBadThing(jx, jy); + TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); /* needed because pushed element has not yet reached its destination, @@ -8430,7 +8913,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) TestIfElementTouchesCustomElement(jx, jy); /* for empty space */ if (!player->active) - RemoveHero(player); + RemovePlayer(player); } if (level.use_step_counter) @@ -8450,7 +8933,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) - KillHero(&stored_player[i]); + KillPlayer(&stored_player[i]); } else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ DrawGameValue_Time(TimePlayed); @@ -8487,7 +8970,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) @@ -8763,7 +9246,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); @@ -8791,7 +9274,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) !IS_INDESTRUCTIBLE(bad_element)) Bang(kill_x, kill_y); else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y)) - KillHero(player); + KillPlayer(player); } else Bang(good_x, good_y); @@ -8837,7 +9320,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]; @@ -8884,41 +9367,41 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) !IS_INDESTRUCTIBLE(bad_element)) Bang(bad_x, bad_y); else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y)) - KillHero(player); + KillPlayer(player); } else Bang(kill_x, kill_y); } } -void TestIfHeroTouchesBadThing(int x, int y) +void TestIfPlayerTouchesBadThing(int x, int y) { - TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING); + TestIfGoodThingHitsBadThing(x, y, MV_NONE); } -void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir) +void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir) { TestIfGoodThingHitsBadThing(x, y, move_dir); } -void TestIfBadThingTouchesHero(int x, int y) +void TestIfBadThingTouchesPlayer(int x, int y) { - TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING); + TestIfBadThingHitsGoodThing(x, y, MV_NONE); } -void TestIfBadThingRunsIntoHero(int x, int y, int move_dir) +void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir) { TestIfBadThingHitsGoodThing(x, y, 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) @@ -8955,7 +9438,7 @@ void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y) Bang(bad_x, bad_y); } -void KillHero(struct PlayerInfo *player) +void KillPlayer(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; @@ -8970,22 +9453,22 @@ void KillHero(struct PlayerInfo *player) player->shield_deadly_time_left = 0; Bang(jx, jy); - BuryHero(player); + BuryPlayer(player); } -static void KillHeroUnlessEnemyProtected(int x, int y) +static void KillPlayerUnlessEnemyProtected(int x, int y) { if (!PLAYER_ENEMY_PROTECTED(x, y)) - KillHero(PLAYERINFO(x, y)); + KillPlayer(PLAYERINFO(x, y)); } -static void KillHeroUnlessExplosionProtected(int x, int y) +static void KillPlayerUnlessExplosionProtected(int x, int y) { if (!PLAYER_EXPLOSION_PROTECTED(x, y)) - KillHero(PLAYERINFO(x, y)); + KillPlayer(PLAYERINFO(x, y)); } -void BuryHero(struct PlayerInfo *player) +void BuryPlayer(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; @@ -8996,10 +9479,10 @@ void BuryHero(struct PlayerInfo *player) PlayLevelSound(jx, jy, SND_GAME_LOSING); player->GameOver = TRUE; - RemoveHero(player); + RemovePlayer(player); } -void RemoveHero(struct PlayerInfo *player) +void RemovePlayer(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; int i, found = FALSE; @@ -9010,6 +9493,9 @@ void RemoveHero(struct PlayerInfo *player) if (!ExplodeField[jx][jy]) StorePlayer[jx][jy] = 0; + if (player->is_moving) + DrawLevelField(player->last_jx, player->last_jy); + for (i = 0; i < MAX_PLAYERS; i++) if (stored_player[i].active) found = TRUE; @@ -9021,6 +9507,27 @@ void RemoveHero(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() @@ -9067,14 +9574,15 @@ 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]; int element; + int collect_count; if (is_player) /* function can also be called by EL_PENGUIN */ { @@ -9114,6 +9622,26 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; /* field has no opening in this direction */ element = Feld[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 && + CustomValue[x][y] != element_info[element].collect_count_initial) + printf("::: %d: %d != %d\n", + element, + CustomValue[x][y], + element_info[element].collect_count_initial); +#endif if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */ return MF_NO_ACTION; @@ -9224,7 +9752,14 @@ int DigField(struct PlayerInfo *player, 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); +#endif + } } else if (IS_COLLECTIBLE(element)) { @@ -9242,14 +9777,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) { @@ -9288,19 +9823,18 @@ int DigField(struct PlayerInfo *player, { int i; - if (element_info[element].collect_count == 0) + if (collect_count == 0) player->inventory_infinite_element = element; else - for (i = 0; i < element_info[element].collect_count; i++) + for (i = 0; i < collect_count; i++) if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; DrawGameValue_Dynamite(local_player->inventory_size); } - else if (element_info[element].collect_count > 0) + else if (collect_count > 0) { - local_player->gems_still_needed -= - element_info[element].collect_count; + local_player->gems_still_needed -= collect_count; if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; @@ -9311,12 +9845,18 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); if (is_player) - CheckTriggeredElementChangeByPlayer(x, y, 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); +#endif + } } else if (IS_PUSHABLE(element)) { @@ -9445,7 +9985,7 @@ int DigField(struct PlayerInfo *player, { CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X, player->index_bit, dig_side); } } @@ -9453,8 +9993,7 @@ int DigField(struct PlayerInfo *player, { if (PLAYER_SWITCHING(player, x, y)) { - CheckTriggeredElementChangeByPlayer(x,y, element, - CE_PLAYER_PRESSES_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); return MF_ACTION; @@ -9504,20 +10043,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) { @@ -9530,15 +10068,18 @@ 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(x, y, element, - CE_SWITCH_OF_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, player->index_bit, dig_side); CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, @@ -9556,8 +10097,7 @@ int DigField(struct PlayerInfo *player, CheckElementChangeByPlayer(x, y, element, CE_SWITCHED, player->index_bit, dig_side); - CheckTriggeredElementChangeByPlayer(x, y, element, - CE_SWITCH_OF_X, + CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, player->index_bit, dig_side); } @@ -9584,10 +10124,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; @@ -10119,7 +10659,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: