#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
+#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
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) +\
+#define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e) ( (element_info[e].collect_score))
#define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
RND((c)->delay_random * (c)->delay_frames))
#define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
RND((c)->delay_random))
+
#if 1
#define GET_VALID_RUNTIME_ELEMENT(e) \
((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
#endif
-#define GET_TARGET_ELEMENT(e, ch) \
- ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
- (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
- (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
+#define GET_TARGET_ELEMENT(e, ch, cv, cs) \
+ ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
+ (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
+ (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
+ (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
+ (e) == EL_CURRENT_CE_VALUE ? (cv) : \
+ (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
#define CAN_GROW_INTO(e) \
((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
boolean SnapField(struct PlayerInfo *, int, int);
boolean DropElement(struct PlayerInfo *);
+static int getInvisibleActiveFromInvisibleElement(int);
+static int getInvisibleFromInvisibleActiveElement(int);
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
return belt_move_dir[belt_dir_nr];
}
+static int get_element_from_group_element(int element)
+{
+ if (IS_GROUP_ELEMENT(element))
+ {
+ struct ElementGroupInfo *group = element_info[element].group;
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+ element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+ group->choice_mode, 0,
+ group->choice_pos);
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+
+ group->choice_pos++;
+
+ element = group->element_resolved[element_pos];
+ }
+
+ return element;
+}
+
static void InitPlayerField(int x, int y, int element, boolean init_game)
{
if (element == EL_SP_MURPHY)
game.light_time_left = level.time_light * FRAMES_PER_SECOND;
break;
+ case EL_INVISIBLE_STEELWALL:
+ case EL_INVISIBLE_WALL:
+ case EL_INVISIBLE_SAND:
+ if (game.light_time_left > 0 ||
+ game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ break;
+
case EL_EMC_MAGIC_BALL:
if (game.ball_state)
Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
#if USE_NEW_CUSTOM_VALUE
if (!element_info[element].use_last_ce_value || init_game)
- CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
#endif
}
#else
#endif
else if (IS_GROUP_ELEMENT(element))
{
+#if 1
+ Feld[x][y] = get_element_from_group_element(element);
+
+ InitField(x, y, init_game);
+#else
struct ElementGroupInfo *group = element_info[element].group;
int last_anim_random_frame = gfx.anim_random_frame;
int element_pos;
Feld[x][y] = group->element_resolved[element_pos];
InitField(x, y, init_game);
+#endif
}
break;
}
#if USE_NEW_CUSTOM_VALUE
#if 1
- CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
#else
CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
#endif
inline void DrawGameValue_Emeralds(int value)
{
- DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+ int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+
+ DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
}
inline void DrawGameValue_Dynamite(int value)
{
- DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+ int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+
+ DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
}
inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
inline void DrawGameValue_Score(int value)
{
- DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+ int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
+
+ DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
}
inline void DrawGameValue_Time(int value)
{
+ int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+ int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
+
+ /* clear background if value just changed its size */
+ if (value == 999 || value == 1000)
+ ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+
if (value < 1000)
- DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+ DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
else
- DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+ DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
}
inline void DrawGameValue_Level(int value)
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;
+ ei->change_page[j].actual_trigger_ce_score = 0;
}
}
game.envelope_active = FALSE;
- game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+ /* set focus to local player for network games, else to all players */
+ game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+ game.centered_player_nr_next = game.centered_player_nr;
game.set_centered_player = FALSE;
+ if (network_playing && tape.recording)
+ {
+ /* store client dependent player focus when recording network games */
+ tape.centered_player_nr_next = game.centered_player_nr_next;
+ tape.set_centered_player = TRUE;
+ }
+
+#if 0
+ printf("::: focus set to player %d [%d]\n",
+ game.centered_player_nr, local_player->index_nr);
+#endif
+
for (i = 0; i < NUM_BELTS; i++)
{
game.belt_dir[i] = MV_NONE;
LoadScore(level_nr);
- if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
+ if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
return -1;
#ifdef ONE_PER_NAME
for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (!strcmp(setup.player_name, highscore[l].Name))
+ if (strEqual(setup.player_name, highscore[l].Name))
m = l;
if (m == k) /* player's new highscore overwrites his old one */
goto put_into_list;
}
}
-static void ResetRandomAnimationValue(int x, int y)
+#if USE_GFX_RESET_GFX_ANIMATION
+static void ResetGfxFrame(int x, int y, boolean redraw)
{
- GfxRandom[x][y] = INIT_GFX_RANDOM();
+ int element = Feld[x][y];
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ int last_gfx_frame = GfxFrame[x][y];
+
+ if (graphic_info[graphic].anim_global_sync)
+ GfxFrame[x][y] = FrameCounter;
+ else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+ GfxFrame[x][y] = CustomValue[x][y];
+ else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ GfxFrame[x][y] = element_info[element].collect_score;
+
+ if (redraw && GfxFrame[x][y] != last_gfx_frame)
+ DrawLevelGraphicAnimation(x, y, graphic);
}
+#endif
static void ResetGfxAnimation(int x, int y)
{
int element, graphic;
#endif
- GfxFrame[x][y] = 0;
GfxAction[x][y] = ACTION_DEFAULT;
GfxDir[x][y] = MovDir[x][y];
+ GfxFrame[x][y] = 0;
#if 0
element = Feld[x][y];
else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
GfxFrame[x][y] = element_info[element].collect_score;
#endif
+
+#if USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxFrame(x, y, FALSE);
+#endif
+}
+
+static void ResetRandomAnimationValue(int x, int y)
+{
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
}
void InitMovingField(int x, int y, int direction)
static void TurnRound(int x, int y)
{
int direction = MovDir[x][y];
-#if 1
+#if 0
int element, graphic;
#endif
GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
#if 1
+ ResetGfxFrame(x, y, FALSE);
+#else
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
{
struct ElementInfo *ei = &element_info[element];
struct ElementChangeInfo *change = &ei->change_page[page];
+ int target_element = change->target_element;
int action_type = change->action_type;
int action_mode = change->action_mode;
int action_arg = change->action_arg;
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 :
+ action_type == CA_SET_CE_SCORE ? 9999 :
CA_ARG_MAX);
int action_arg_number_reset =
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) :
+ action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
#else
action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
#endif
+ action_type == CA_SET_CE_SCORE ? 0 :
0);
int action_arg_number =
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 :
#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_SCORE ? ei->collect_score :
action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(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_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+ action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+ action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
+ action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
-1);
(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] :
+ action_type == CA_SET_CE_SCORE ? ei->collect_score :
0);
int action_arg_number_new =
/* ---------- CE actions ---------------------------------------------- */
- case CA_SET_CE_SCORE:
- {
- ei->collect_score = action_arg_number_new;
-
- break;
- }
-
case CA_SET_CE_VALUE:
{
#if USE_NEW_CUSTOM_VALUE
- int last_custom_value = CustomValue[x][y];
+ int last_ce_value = CustomValue[x][y];
CustomValue[x][y] = action_arg_number_new;
#if 0
- printf("::: Count == %d\n", CustomValue[x][y]);
+ printf("::: CE value == %d\n", CustomValue[x][y]);
#endif
- if (CustomValue[x][y] == 0 && last_custom_value > 0)
+ if (CustomValue[x][y] == 0 && last_ce_value > 0)
{
#if 0
printf("::: CE_VALUE_GETS_ZERO\n");
CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+#if 0
+ printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+#endif
+ }
+#endif
+
+ break;
+ }
+
+ case CA_SET_CE_SCORE:
+ {
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_score = ei->collect_score;
+
+ ei->collect_score = action_arg_number_new;
+
+#if 0
+ printf("::: CE score == %d\n", ei->collect_score);
+#endif
+
+ if (ei->collect_score == 0 && last_ce_score > 0)
+ {
+#if 0
+ printf("::: CE_SCORE_GETS_ZERO\n");
+#endif
+
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+
#if 0
printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
#endif
static void CreateFieldExt(int x, int y, int element, boolean is_change)
{
+ int old_element = Feld[x][y];
+ int new_element = get_element_from_group_element(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(element) &&
- IS_WALKABLE(Feld[x][y]));
+ boolean add_player = (ELEM_IS_PLAYER(new_element) &&
+ IS_WALKABLE(old_element));
+#if 0
/* check if element under player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
- IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
{
Bang(x, y);
return;
}
+#endif
if (!add_player)
{
else
RemoveField(x, y);
- Feld[x][y] = element;
+ Feld[x][y] = new_element;
+#if !USE_GFX_RESET_GFX_ANIMATION
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
+#endif
- if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ if (element_info[new_element].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)
+ if (element_info[new_element].use_last_ce_value)
CustomValue[x][y] = last_ce_value;
#endif
InitField_WithBug1(x, y, FALSE);
+ new_element = Feld[x][y]; /* element may have changed */
+
+#if USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+#endif
+
DrawLevelField(x, y);
- if (GFX_CRUMBLED(Feld[x][y]))
+ if (GFX_CRUMBLED(new_element))
DrawLevelFieldCrumbledSandNeighbours(x, y);
}
+#if 1
+ /* check if element under player changes from accessible to unaccessible
+ (needed for special case of dropping element which then changes) */
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#endif
+
/* "ChangeCount" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(element))
- RelocatePlayer(x, y, element);
+ if (ELEM_IS_PLAYER(new_element))
+ RelocatePlayer(x, y, new_element);
if (is_change)
ChangeCount[x][y]++; /* count number of changes in the same frame */
static boolean ChangeElement(int x, int y, int element, int page)
{
- struct ElementChangeInfo *change = &element_info[element].change_page[page];
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int ce_value = CustomValue[x][y];
+ int ce_score = ei->collect_score;
int target_element;
int old_element = Feld[x][y];
change->actual_trigger_player = EL_PLAYER_1;
change->actual_trigger_side = CH_SIDE_NONE;
change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
}
/* do not change elements more than a specified maximum number of changes */
ChangeEvent[ex][ey] = ChangeEvent[x][y];
content_element = change->target_content.e[xx][yy];
- target_element = GET_TARGET_ELEMENT(content_element, change);
+ target_element = GET_TARGET_ELEMENT(content_element, change,
+ ce_value, ce_score);
CreateElementFromChange(ex, ey, target_element);
}
else
{
- target_element = GET_TARGET_ELEMENT(change->target_element, change);
+ target_element = GET_TARGET_ELEMENT(change->target_element, change,
+ ce_value, ce_score);
if (element == EL_DIAGONAL_GROWING ||
element == EL_DIAGONAL_SHRINKING)
change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
change->actual_trigger_side = trigger_side;
change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
if ((change->can_change && !change_done) || change->has_action)
{
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];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
/* special case: trigger element not at (x,y) position for some events */
if (check_trigger_element)
int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
change->actual_trigger_ce_value = CustomValue[xx][yy];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
}
if (change->can_change && !change_done)
printf("::: Yo man! Rocks can fall!\n");
#endif
+#if 1
+ ResetGfxFrame(x, y, TRUE);
+#else
if (graphic_info[graphic].anim_global_sync)
GfxFrame[x][y] = FrameCounter;
else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
#endif
DrawLevelGraphicAnimation(x, y, graphic);
}
+#endif
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(GfxFrame[x][y], graphic))
switch (id)
{
case GAME_CTRL_ID_STOP:
- RequestQuitGame(TRUE);
+ if (tape.playing)
+ TapeStop();
+ else
+ RequestQuitGame(TRUE);
break;
case GAME_CTRL_ID_PAUSE:
#endif
{
tape.pausing = FALSE;
- DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+ DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
}
}
break;