added support for CE change pages triggering global animations
[rocksndiamonds.git] / src / game.c
index 666d992d5c535f572dde2a4298f63211da230566..299fd72bdd934957fc06ca6e9343f1ad315c60aa 100644 (file)
@@ -3195,6 +3195,17 @@ static void InitGameEngine(void)
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
   }
 
+  // ---------- initialize if element can trigger global animations -----------
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    struct ElementInfo *ei = &element_info[i];
+
+    ei->has_anim_event = FALSE;
+  }
+
+  InitGlobalAnimEventsForCustomElements();
+
   // ---------- initialize internal run-time variables ------------------------
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -3260,12 +3271,14 @@ static void InitGameEngine(void)
 
     for (j = 0; j < ei->num_change_pages; j++)
     {
-      ei->change_page[j].actual_trigger_element = EL_EMPTY;
-      ei->change_page[j].actual_trigger_player = EL_EMPTY;
-      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
-      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;
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      change->actual_trigger_element = EL_EMPTY;
+      change->actual_trigger_player = EL_EMPTY;
+      change->actual_trigger_player_bits = CH_PLAYER_NONE;
+      change->actual_trigger_side = CH_SIDE_NONE;
+      change->actual_trigger_ce_value = 0;
+      change->actual_trigger_ce_score = 0;
     }
   }
 
@@ -3283,16 +3296,18 @@ static void InitGameEngine(void)
 
     for (j = 0; j < ei->num_change_pages; j++)
     {
-      if (!ei->change_page[j].can_change_or_has_action)
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      if (!change->can_change_or_has_action)
        continue;
 
-      if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
+      if (change->has_event[CE_BY_OTHER_ACTION])
       {
-       int trigger_element = ei->change_page[j].trigger_element;
+       int trigger_element = change->trigger_element;
 
        for (k = 0; k < NUM_CHANGE_EVENTS; k++)
        {
-         if (ei->change_page[j].has_event[k])
+         if (change->has_event[k])
          {
            if (IS_GROUP_ELEMENT(trigger_element))
            {
@@ -3501,10 +3516,10 @@ static void InitGameEngine(void)
     {
       int element = EL_CUSTOM_START + i;
 
-      if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
-         HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
-         HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
-         HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
+      if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
+         HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
+         HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
+         HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
        game.use_mouse_actions = TRUE;
     }
   }
@@ -5838,6 +5853,8 @@ static void Explode(int ex, int ey, int phase, int mode)
   if (phase == EX_PHASE_START)         // initialize 'Store[][]' field
   {
     int center_element = Tile[ex][ey];
+    int ce_value = CustomValue[ex][ey];
+    int ce_score = element_info[center_element].collect_score;
     int artwork_element, explosion_element;    // set these values later
 
     // remove things displayed in background while burning dynamite
@@ -5976,6 +5993,14 @@ static void Explode(int ex, int ey, int phase, int mode)
       else
        Store[x][y] = EL_EMPTY;
 
+      if (IS_CUSTOM_ELEMENT(center_element))
+       Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
+                      Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
+                      Store[x][y] >= EL_PREV_CE_8 &&
+                      Store[x][y] <= EL_NEXT_CE_8 ?
+                      RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
+                      Store[x][y]);
+
       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
          center_element == EL_AMOEBA_TO_DIAMOND)
        Store2[x][y] = element;
@@ -10690,6 +10715,9 @@ static boolean ChangeElement(int x, int y, int element, int page)
 
   ChangeCount[x][y]++;         // count number of changes in the same frame
 
+  if (ei->has_anim_event)
+    HandleGlobalAnimEventByElementChange(element, page);
+
   if (change->explode)
   {
     Bang(x, y);
@@ -10924,13 +10952,14 @@ static void HandleElementChange(int x, int y, int page)
 
   if (ChangeDelay[x][y] != 0)          // continue element change
   {
-    if (change->can_change)
-    {
-      int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 
-      if (IS_ANIMATED(graphic))
-       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+    // also needed if CE can not change, but has CE delay with CE action
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+    if (change->can_change)
+    {
       if (change->change_function)
        change->change_function(x, y);
     }
@@ -11698,6 +11727,49 @@ void AdvanceGfxFrame(void)
   }
 }
 
+static void HandleMouseAction(struct MouseActionInfo *mouse_action,
+                             struct MouseActionInfo *mouse_action_last)
+{
+  if (mouse_action->button)
+  {
+    int new_button = (mouse_action->button && mouse_action_last->button == 0);
+    int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
+    int x = mouse_action->lx;
+    int y = mouse_action->ly;
+    int element = Tile[x][y];
+
+    if (new_button)
+    {
+      CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
+      CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
+                                        ch_button);
+    }
+
+    CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
+    CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
+                                      ch_button);
+
+    if (level.use_step_counter)
+    {
+      boolean counted_click = FALSE;
+
+      // element clicked that can change when clicked/pressed
+      if (CAN_CHANGE_OR_HAS_ACTION(element) &&
+         (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
+          HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
+       counted_click = TRUE;
+
+      // element clicked that can trigger change when clicked/pressed
+      if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
+         trigger_events[element][CE_MOUSE_PRESSED_ON_X])
+       counted_click = TRUE;
+
+      if (new_button && counted_click)
+       CheckLevelTime_StepCounter();
+    }
+  }
+}
+
 void StartGameActions(boolean init_network_game, boolean record_tape,
                      int random_seed)
 {
@@ -12264,45 +12336,7 @@ void GameActions_RND(void)
 #endif
   }
 
-  if (mouse_action.button)
-  {
-    int new_button = (mouse_action.button && mouse_action_last.button == 0);
-    int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
-
-    x = mouse_action.lx;
-    y = mouse_action.ly;
-    element = Tile[x][y];
-
-    if (new_button)
-    {
-      CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
-      CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
-                                        ch_button);
-    }
-
-    CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
-    CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
-                                      ch_button);
-
-    if (level.use_step_counter)
-    {
-      boolean counted_click = FALSE;
-
-      // element clicked that can change when clicked/pressed
-      if (CAN_CHANGE_OR_HAS_ACTION(element) &&
-         (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
-          HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
-       counted_click = TRUE;
-
-      // element clicked that can trigger change when clicked/pressed
-      if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
-         trigger_events[element][CE_MOUSE_PRESSED_ON_X])
-       counted_click = TRUE;
-
-      if (new_button && counted_click)
-       CheckLevelTime_StepCounter();
-    }
-  }
+  HandleMouseAction(&mouse_action, &mouse_action_last);
 
   SCAN_PLAYFIELD(x, y)
   {
@@ -13217,13 +13251,18 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_LEAVES_X,
                                          player->index_bit, leave_side);
 
-      if (IS_CUSTOM_ELEMENT(new_element))
-       CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
-                                  player->index_bit, enter_side);
+      // needed because pushed element has not yet reached its destination,
+      // so it would trigger a change event at its previous field location
+      if (!player->is_pushing)
+      {
+       if (IS_CUSTOM_ELEMENT(new_element))
+         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
+                                    player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
-                                         CE_PLAYER_ENTERS_X,
-                                         player->index_bit, enter_side);
+       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+                                           CE_PLAYER_ENTERS_X,
+                                           player->index_bit, enter_side);
+      }
 
       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
                                        CE_MOVE_OF_X, move_direction);
@@ -13234,8 +13273,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       TestIfPlayerTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
 
-      /* needed because pushed element has not yet reached its destination,
-        so it would trigger a change event at its previous field location */
+      // needed because pushed element has not yet reached its destination,
+      // so it would trigger a change event at its previous field location
       if (!player->is_pushing)
        TestIfElementTouchesCustomElement(jx, jy);      // for empty space
 
@@ -15231,10 +15270,12 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action)
 
 static int getLevelMusicNr(void)
 {
+  int level_pos = level_nr - leveldir_current->first_level;
+
   if (levelset.music[level_nr] != MUS_UNDEFINED)
     return levelset.music[level_nr];           // from config file
   else
-    return MAP_NOCONF_MUSIC(level_nr);         // from music dir
+    return MAP_NOCONF_MUSIC(level_pos);                // from music dir
 }
 
 static void FadeLevelSounds(void)