X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=9cec7d1bd06054258377214ba45294bae56cbdf3;hb=c973cc688a931e86ae3d17d6fa0d1c8b7180da75;hp=1d2230b38335817fb7e4dec6138c4a59cad7ffcb;hpb=e855da19ad53a6bd6b20e4871e293b7dc84ded9e;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 1d2230b3..9cec7d1b 100644 --- a/src/game.c +++ b/src/game.c @@ -25,14 +25,14 @@ #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_COLLECT_COUNT (TRUE * USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_ANIM (TRUE * USE_NEW_STUFF * 1) -#define USE_NEW_ALL_SLIPPERY (TRUE * USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_SPEED (TRUE * 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_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) /* for DigField() */ #define DF_NO_PUSH 0 @@ -245,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); @@ -1263,6 +1263,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); @@ -1584,6 +1587,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 */ @@ -1725,8 +1730,7 @@ void InitGame() element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; /* BD style elements prefer to slip down on the left side */ - if (i == EL_BD_ROCK || i == EL_BD_DIAMOND || - game.emulation == EMU_BOULDERDASH) + if (game.emulation == EMU_BOULDERDASH) element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; } } @@ -2332,7 +2336,7 @@ void GameWon() PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); } - /* Hero disappears */ + /* player disappears */ if (ExitX >= 0 && ExitY >= 0) DrawLevelField(ExitX, ExitY); @@ -2900,7 +2904,7 @@ 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)) @@ -3151,7 +3155,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)) @@ -3745,7 +3749,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 @@ -3787,7 +3791,7 @@ void Impact(int x, int y) { if (CAN_SMASH_PLAYER(element)) { - KillHeroUnlessEnemyProtected(x, y + 1); + KillPlayerUnlessEnemyProtected(x, y + 1); return; } } @@ -4840,7 +4844,11 @@ void StartMoving(int x, int y) #if USE_NEW_ALL_SLIPPERY if (can_fall_both) { - can_fall_left = !(can_fall_right = RND(2)); + 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 @@ -5057,7 +5065,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; } @@ -5358,7 +5366,7 @@ void StartMoving(int x, int y) DrawLevelElementAnimation(x, y, element); if (DONT_TOUCH(element)) - TestIfBadThingTouchesHero(x, y); + TestIfBadThingTouchesPlayer(x, y); return; } @@ -5580,7 +5588,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)) @@ -6499,11 +6507,12 @@ static int getSpecialActionElement(int element, int number, int base_element) 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 : @@ -6626,7 +6635,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { for (i = 0; i < MAX_PLAYERS; i++) if (action_arg_player_bits & (1 << i)) - KillHero(&stored_player[i]); + KillPlayer(&stored_player[i]); break; } @@ -6721,11 +6730,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) /* make sure that value is power of 2 */ move_stepsize = (1 << log_2(move_stepsize)); - stored_player[i].move_delay_value = TILEX / move_stepsize; + /* 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, action_arg_number); + stored_player[i].move_delay_value_next, action_arg_number); #endif } } @@ -6752,7 +6762,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) - KillHero(&stored_player[i]); + KillPlayer(&stored_player[i]); } break; @@ -6895,7 +6905,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); } @@ -7060,6 +7070,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); @@ -7137,6 +7241,8 @@ static void ChangeElement(int x, int y, int page) } } +#endif + static boolean CheckTriggeredElementChangeExt(int trigger_element, int trigger_event, int trigger_player, @@ -7188,9 +7294,13 @@ static boolean CheckTriggeredElementChangeExt(int trigger_element, ChangeEvent[x][y] = trigger_event; ChangeElement(x, y, p); } - +#if USE_NEW_DELAYED_ACTION + else if (change->has_action) + ExecuteCustomElementAction(x, y, element, p); +#else if (change->has_action) ExecuteCustomElementAction(x, y, element, p); +#endif } } @@ -7257,9 +7367,13 @@ 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); +#else if (change->has_action) ExecuteCustomElementAction(x, y, element, p); +#endif } } @@ -7781,11 +7895,16 @@ void GameActions() 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) && ChangeDelay[x][y] == 0) + if (HAS_ACTION(element)) ExecuteCustomElementAction(x, y, element, page); +#endif + #endif element = Feld[x][y]; @@ -8029,7 +8148,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); @@ -8265,10 +8384,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; } @@ -8288,6 +8407,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); @@ -8483,12 +8608,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; } @@ -8549,21 +8674,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) return; #if 0 - printf("::: player->MovPos: %d -> %d\n", - player->MovPos, - player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize); + 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); + 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); - } + /* 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); @@ -8601,7 +8726,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])) @@ -8636,7 +8761,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, @@ -8645,7 +8770,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) @@ -8665,7 +8790,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); @@ -9004,7 +9129,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); @@ -9097,29 +9222,29 @@ 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); } -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); } -void TestIfBadThingRunsIntoHero(int x, int y, int move_dir) +void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir) { TestIfBadThingHitsGoodThing(x, y, move_dir); } @@ -9168,7 +9293,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; @@ -9183,22 +9308,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; @@ -9209,10 +9334,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; @@ -9223,6 +9348,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;