X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=3051f5ac9d93771620536c019413d08420dceec0;hb=9de8eede744c45187c7011543ef8097d1ebc879c;hp=b51a18739ef7ed2f4dd3092b061f9b3c5c9e0d2b;hpb=12a224578d49391c7c4c27ad898059ae17e9b880;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index b51a1873..3051f5ac 100644 --- a/src/game.c +++ b/src/game.c @@ -89,6 +89,9 @@ #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 INIT_GFX_RANDOM() (SimpleRND(1000000)) #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \ @@ -163,9 +166,12 @@ static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); static void KillHeroUnlessProtected(int, int); -static void CheckTriggeredElementChange(int, int); -static void CheckPlayerElementChange(int, int, int, int); -static void ChangeElementDoIt(int, int, int); +static void TestIfPlayerTouchesCustomElement(int, int); +static void TestIfElementTouchesCustomElement(int, int); + +static boolean CheckTriggeredElementChange(int, int, int, int); +static boolean CheckElementChange(int, int, int, int); +static void ChangeElementNow(int, int, int); static void PlaySoundLevel(int, int, int); static void PlaySoundLevelNearest(int, int, int); @@ -202,8 +208,8 @@ static void RunTimegateWheel(int x, int y); struct ChangingElementInfo { - int base_element; - int next_element; + int element; + int target_element; int change_delay; void (*pre_change_function)(int x, int y); void (*change_function)(int x, int y); @@ -370,34 +376,52 @@ push_delay_list[] = { EL_UNDEFINED, 0, 0 }, }; +struct +{ + int element; + int move_stepsize; +} +move_stepsize_list[] = +{ + { EL_AMOEBA_DROP, 2 }, + { EL_AMOEBA_DROPPING, 2 }, + { EL_QUICKSAND_FILLING, 1 }, + { EL_QUICKSAND_EMPTYING, 1 }, + { EL_MAGIC_WALL_FILLING, 2 }, + { EL_BD_MAGIC_WALL_FILLING, 2 }, + { EL_MAGIC_WALL_EMPTYING, 2 }, + { EL_BD_MAGIC_WALL_EMPTYING, 2 }, + + { EL_UNDEFINED, 0 }, +}; + struct { int element; int gem_count; } -collect_gem_count_list[] = +gem_count_list[] = { - { EL_EMERALD, 1 }, - { EL_BD_DIAMOND, 1 }, - { EL_EMERALD_YELLOW, 1 }, - { EL_EMERALD_RED, 1 }, - { EL_EMERALD_PURPLE, 1 }, - { EL_DIAMOND, 3 }, - { EL_SP_INFOTRON, 1 }, - { EL_PEARL, 5 }, - { EL_CRYSTAL, 8 }, - - { EL_UNDEFINED, 0 }, + { EL_EMERALD, 1 }, + { EL_BD_DIAMOND, 1 }, + { EL_EMERALD_YELLOW, 1 }, + { EL_EMERALD_RED, 1 }, + { EL_EMERALD_PURPLE, 1 }, + { EL_DIAMOND, 3 }, + { EL_SP_INFOTRON, 1 }, + { EL_PEARL, 5 }, + { EL_CRYSTAL, 8 }, + + { EL_UNDEFINED, 0 }, }; -static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS]; +static boolean changing_element[MAX_NUM_ELEMENTS]; static unsigned long trigger_events[MAX_NUM_ELEMENTS]; -#define IS_AUTO_CHANGING(e) (changing_element[e].base_element != EL_UNDEFINED) +#define IS_AUTO_CHANGING(e) (changing_element[e]) #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0) #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \ IS_JUST_CHANGING(x, y)) -#define TRIGGERS_BY_COLLECTING(e) (trigger_events[e] & CE_OTHER_COLLECTING) void GetPlayerConfig() @@ -742,42 +766,75 @@ static void InitGameEngine() /* initialize changing elements information */ for (i=0; ibase_element; + struct ElementChangeInfo *change = &element_info[element].change; + +#if 1 + change->target_element = ce->target_element; + change->delay_fixed = ce->change_delay; + change->pre_change_function = ce->pre_change_function; + change->change_function = ce->change_function; + change->post_change_function = ce->post_change_function; + changing_element[element] = TRUE; +#else changing_element[element].base_element = ce->base_element; changing_element[element].next_element = ce->next_element; changing_element[element].change_delay = ce->change_delay; changing_element[element].pre_change_function = ce->pre_change_function; changing_element[element].change_function = ce->change_function; changing_element[element].post_change_function = ce->post_change_function; +#endif } /* add changing elements from custom element configuration */ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) { int element = EL_CUSTOM_START + i; +#if 0 struct ElementChangeInfo *change = &element_info[element].change; +#endif /* only add custom elements that change after fixed/random frame delay */ if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, CE_DELAY)) continue; +#if 1 + changing_element[element] = TRUE; +#else changing_element[element].base_element = element; - changing_element[element].next_element = change->successor; + changing_element[element].next_element = change->target_element; changing_element[element].change_delay = (change->delay_fixed * change->delay_frames); +#endif } /* ---------- initialize trigger events ---------------------------------- */ @@ -789,7 +846,7 @@ static void InitGameEngine() /* add trigger events from element change event properties */ for (i=0; iPushing && player->MovPos != 0); -#else - boolean pushing = (player != NULL && player->Pushing && player->is_moving); -#endif -#endif -#if 0 - if (player && player->is_moving && player->MovPos == 0) - printf("::: !!!\n"); -#endif - - if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING) - step /= 2; - else if (element == EL_QUICKSAND_FILLING || - element == EL_QUICKSAND_EMPTYING) - step /= 4; - else if (element == EL_MAGIC_WALL_FILLING || - element == EL_BD_MAGIC_WALL_FILLING || - element == EL_MAGIC_WALL_EMPTYING || - element == EL_BD_MAGIC_WALL_EMPTYING) - step /= 2; - else if (CAN_FALL(element) && horiz_move && - y < lev_fieldy-1 && IS_BELT_ACTIVE(Feld[x][y+1])) - step /= 2; - else if (element == EL_SPRING && horiz_move) - step *= 2; - else if (IS_CUSTOM_ELEMENT(element)) - step = SIGN(step) * element_info[element].move_stepsize; - -#if OLD_GAME_BEHAVIOUR - else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element)) - step*=2; -#endif + /* special values for move stepsize for spring and things on conveyor belt */ + if (horiz_move) + { + if (CAN_FALL(element) && + y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1])) + step = sign * MOVE_STEPSIZE_NORMAL / 2; + else if (element == EL_SPRING) + step = sign * MOVE_STEPSIZE_NORMAL * 2; + } MovPos[x][y] += step; -#if 1 -#if 1 if (pushed) /* special case: moving object pushed by player */ -#else - if (pushing) /* special case: moving object pushed by player */ -#endif -#if 1 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); -#else - MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->GfxPos)); -#endif -#endif - -#if 0 - if (element == EL_SPRING) - printf("::: spring moves %d [%d: %d, %d, %d/%d]\n", - MovPos[x][y], - pushing, - (player?player->Pushing:-42), - (player?player->is_moving:-42), - (player?player->MovPos:-42), - (player?player->GfxPos:-42)); -#endif if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */ { @@ -3958,31 +4033,11 @@ void ContinueMoving(int x, int y) if (element == EL_MOLE) { - int i; - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - Feld[x][y] = EL_SAND; - DrawLevelField(x, y); - for(i=0; i<4; i++) - { - int xx, yy; - - xx = x + xy[i][0]; - yy = y + xy[i][1]; - - if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND) - DrawLevelField(xx, yy); /* for "crumbled sand" */ - } + DrawLevelFieldCrumbledSandNeighbours(x, y); } - - if (element == EL_QUICKSAND_FILLING) + else if (element == EL_QUICKSAND_FILLING) { element = Feld[newx][newy] = get_next_element(element); Store[newx][newy] = Store[x][y]; @@ -4056,7 +4111,6 @@ void ContinueMoving(int x, int y) ResetGfxAnimation(x, y); /* reset animation values for old field */ -#if 1 #if 0 /* 2.1.1 (does not work correctly for spring) */ if (!CAN_MOVE(element)) @@ -4078,20 +4132,14 @@ void ContinueMoving(int x, int y) (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)) MovDir[newx][newy] = 0; #endif - -#endif #endif DrawLevelField(x, y); DrawLevelField(newx, newy); -#if 0 - if (game.engine_version >= RELEASE_IDENT(2,2,0,7) || !pushing) -#endif - Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ -#if 1 + Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ + if (!pushed) /* special case: moving object pushed by player */ -#endif JustStopped[newx][newy] = 3; if (DONT_TOUCH(element)) /* object may be nasty to player or others */ @@ -4103,15 +4151,15 @@ void ContinueMoving(int x, int y) else if (element == EL_PENGUIN) TestIfFriendTouchesBadThing(newx, newy); -#if 1 if (CAN_FALL(element) && direction == MV_DOWN && (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1))) Impact(x, newy); -#else - if (CAN_SMASH(element) && direction == MV_DOWN && - (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1))) - Impact(x, newy); -#endif + + if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) + CheckElementChange(newx, newy, element, CE_COLLISION); + + TestIfPlayerTouchesCustomElement(newx, newy); + TestIfElementTouchesCustomElement(newx, newy); } else /* still moving on */ { @@ -5003,12 +5051,16 @@ static void ChangeActiveTrap(int x, int y) DrawLevelFieldCrumbledSand(x, y); } -static void ChangeElementDoIt(int x, int y, int element_new) +static void ChangeElementNowExt(int x, int y, int target_element) { - CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING); + if (IS_PLAYER(x, y) && !IS_ACCESSIBLE(target_element)) + { + Bang(x, y); + return; + } RemoveField(x, y); - Feld[x][y] = element_new; + Feld[x][y] = target_element; ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); @@ -5020,41 +5072,111 @@ static void ChangeElementDoIt(int x, int y, int element_new) DrawLevelField(x, y); if (CAN_BE_CRUMBLED(Feld[x][y])) + DrawLevelFieldCrumbledSandNeighbours(x, y); +} + +static void ChangeElementNow(int x, int y, int element) +{ + struct ElementChangeInfo *change = &element_info[element].change; + + CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING); + + if (change->explode) { - int sx = SCREENX(x), sy = SCREENY(y); - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - int i; + Bang(x, y); + return; + } - for(i=0; i<4; i++) + if (change->use_content) + { + boolean complete_change = TRUE; + boolean can_change[3][3]; + int xx, yy; + + for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++) { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int sxx = sx + xy[i][0]; - int syy = sy + xy[i][1]; + boolean half_destructible; + int ex = x + xx - 1; + int ey = y + yy - 1; + int e; + + can_change[xx][yy] = TRUE; + + if (ex == x && ey == y) /* do not check changing element itself */ + continue; + + if (change->content[xx][yy] == EL_EMPTY_SPACE) + { + can_change[xx][yy] = FALSE; /* do not change empty borders */ + + continue; + } + + if (!IN_LEV_FIELD(ex, ey)) + { + can_change[xx][yy] = FALSE; + complete_change = FALSE; - if (!IN_LEV_FIELD(xx, yy) || - !IN_SCR_FIELD(sxx, syy) || - !CAN_BE_CRUMBLED(Feld[xx][yy]) || - IS_MOVING(xx, yy)) continue; + } - DrawLevelField(xx, yy); + e = Feld[ex][ey]; + + half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e)); + + if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) || + (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) || + (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e))) + { + can_change[xx][yy] = FALSE; + complete_change = FALSE; + } + } + + if (!change->only_complete || complete_change) + { + if (change->only_complete && change->use_random_change && + RND(change->random) != 0) + return; + + for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++) + { + int ex = x + xx - 1; + int ey = y + yy - 1; + + if (can_change[xx][yy] && (!change->use_random_change || + RND(change->random) == 0)) + { + ChangeElementNowExt(ex, ey, change->content[xx][yy]); + + /* for symmetry reasons, stop newly created border elements */ + if (ex != x || ey != y) + Stop[ex][ey] = TRUE; + } + } + + return; } } + + ChangeElementNowExt(x, y, change->target_element); } static void ChangeElement(int x, int y) { +#if 1 + int element = MovingOrBlocked2Element(x, y); +#else int element = Feld[x][y]; +#endif + struct ElementChangeInfo *change = &element_info[element].change; if (ChangeDelay[x][y] == 0) /* initialize element change */ { +#if 1 + ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames + + RND(change->delay_random * change->delay_frames)) + 1; +#else ChangeDelay[x][y] = changing_element[element].change_delay + 1; if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY)) @@ -5064,12 +5186,18 @@ static void ChangeElement(int x, int y) ChangeDelay[x][y] += RND(max_random_delay * delay_frames); } +#endif ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); +#if 1 + if (change->pre_change_function) + change->pre_change_function(x, y); +#else if (changing_element[element].pre_change_function) changing_element[element].pre_change_function(x, y); +#endif } ChangeDelay[x][y]--; @@ -5081,12 +5209,19 @@ static void ChangeElement(int x, int y) if (IS_ANIMATED(graphic)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); +#if 1 + if (change->change_function) + change->change_function(x, y); +#else if (changing_element[element].change_function) changing_element[element].change_function(x, y); +#endif } else /* finish element change */ { +#if 0 int next_element = changing_element[element].next_element; +#endif if (IS_MOVING(x, y)) /* never change a running system ;-) */ { @@ -5095,31 +5230,42 @@ static void ChangeElement(int x, int y) return; } +#if 1 + ChangeElementNow(x, y, element); + + if (change->post_change_function) + change->post_change_function(x, y); +#else if (next_element != EL_UNDEFINED) - ChangeElementDoIt(x, y, next_element); + ChangeElementNow(x, y, next_element); else - ChangeElementDoIt(x, y, element_info[element].change.successor); + ChangeElementNow(x, y, element_info[element].change.target_element); if (changing_element[element].post_change_function) changing_element[element].post_change_function(x, y); +#endif } } -static void CheckTriggeredElementChange(int trigger_element, int trigger_event) +static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element, + int trigger_event) { int i, x, y; if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event))) - return; + return FALSE; for (i=0; iactive) RemoveHero(player); @@ -6171,6 +6320,75 @@ void ScrollScreen(struct PlayerInfo *player, int mode) ScreenMovDir = MV_NO_MOVING; } +void TestIfPlayerTouchesCustomElement(int x, int y) +{ + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + boolean center_is_player = (IS_PLAYER(x, y)); + int i; + + for (i=0; i<4; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (center_is_player) + { + CheckTriggeredElementChange(xx, yy, Feld[xx][yy], CE_OTHER_GETS_TOUCHED); + CheckElementChange(xx, yy, Feld[xx][yy], CE_TOUCHED_BY_PLAYER); + } + else if (IS_PLAYER(xx, yy)) + { + CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_GETS_TOUCHED); + CheckElementChange(x, y, Feld[x][y], CE_TOUCHED_BY_PLAYER); + + break; + } + } +} + +void TestIfElementTouchesCustomElement(int x, int y) +{ + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + boolean center_is_custom = (IS_CUSTOM_ELEMENT(Feld[x][y])); + int i; + + for (i=0; i<4; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (center_is_custom && + Feld[xx][yy] == element_info[Feld[x][y]].change.trigger_element) + { + CheckElementChange(x, y, Feld[x][y], CE_OTHER_IS_TOUCHING); + } + + if (IS_CUSTOM_ELEMENT(Feld[xx][yy]) && + Feld[x][y] == element_info[Feld[xx][yy]].change.trigger_element) + { + CheckElementChange(xx, yy, Feld[xx][yy], CE_OTHER_IS_TOUCHING); + } + } +} + void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; @@ -6904,10 +7122,10 @@ int DigField(struct PlayerInfo *player, el2edimg(EL_KEY_1 + key_nr)); redraw_mask |= REDRAW_DOOR_1; } - else if (element_info[element].collect_gem_count > 0) + else if (element_info[element].gem_count > 0) { local_player->gems_still_needed -= - element_info[element].collect_gem_count; + element_info[element].gem_count; if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; @@ -6918,7 +7136,7 @@ int DigField(struct PlayerInfo *player, RaiseScoreElement(element); PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED); break; } @@ -7008,14 +7226,15 @@ int DigField(struct PlayerInfo *player, if (game.engine_version < RELEASE_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); - CheckTriggeredElementChange(element, CE_OTHER_PUSHING); - CheckPlayerElementChange(x, y, element, CE_PUSHED_BY_PLAYER); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PUSHED); + CheckElementChange(x, y, element, CE_PUSHED_BY_PLAYER); break; } else { - CheckPlayerElementChange(x, y, element, CE_PRESSED_BY_PLAYER); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PRESSED); + CheckElementChange(x, y, element, CE_PRESSED_BY_PLAYER); } return MF_NO_ACTION; @@ -7326,7 +7545,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_KEY]); break; default: - RaiseScore(element_info[element].collect_score); + RaiseScore(element_info[element].score); break; } }