rnd-20030618-2-src
[rocksndiamonds.git] / src / game.c
index 2f6eb82fa5fca0e17ac4bcc28076501e3fb8a99c..218c47f0b59bfc26ce7ec4e5e3ec8a2070500e3b 100644 (file)
@@ -114,6 +114,7 @@ static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void KillHeroUnlessProtected(int, int);
 
+static void CheckTriggeredElementChange(int, int);
 static void ChangeElementDoIt(int, int, int);
 
 static void PlaySoundLevel(int, int, int);
@@ -302,8 +303,13 @@ static struct ChangingElementInfo changing_element_list[] =
 };
 
 static struct ChangingElementInfo 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_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()
@@ -684,6 +690,16 @@ static void InitGameEngine()
     changing_element[element].change_delay = (change->delay_fixed *
                                              change->delay_frames);
   }
+
+  /* initialize trigger events information */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    trigger_events[i] = EP_BITMASK_DEFAULT;
+
+  /* add trigger events from element change event properties */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    if (HAS_CHANGE_EVENT(i, CE_BY_OTHER))
+      trigger_events[element_info[i].change.trigger] |=
+       element_info[i].change.events;
 }
 
 
@@ -1791,7 +1807,11 @@ void Explode(int ex, int ey, int phase, int mode)
       }
 
       Feld[x][y] = EL_EXPLOSION;
+#if 1
+      GfxElement[x][y] = center_element;
+#else
       GfxElement[x][y] = EL_UNDEFINED;
+#endif
       MovDir[x][y] = MovPos[x][y] = 0;
       AmoebaNr[x][y] = 0;
       ExplodePhase[x][y] = 1;
@@ -1846,6 +1866,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
+    GfxElement[x][y] = EL_UNDEFINED;
 
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
       element = Feld[x][y] = Back[x][y];
@@ -1862,10 +1883,14 @@ void Explode(int ex, int ey, int phase, int mode)
   }
   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
+#if 1
+    int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
+#else
     int stored = Store[x][y];
     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
                   stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
                   IMG_SP_EXPLOSION);
+#endif
     int frame = getGraphicAnimationFrame(graphic, phase - delay);
 
     if (phase == delay)
@@ -1942,10 +1967,22 @@ void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
+  if (IS_PLAYER(x, y))
+  {
+    struct PlayerInfo *player = PLAYERINFO(x, y);
+
+    element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
+                           player->element_nr);
+  }
+
+#if 1
+  PlaySoundLevelAction(x, y, ACTION_EXPLODING);
+#else
   if (game.emulation == EMU_SUPAPLEX)
     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
   else
     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
+#endif
 
 #if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
@@ -1987,6 +2024,8 @@ void Bang(int x, int y)
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
   }
+
+  CheckTriggeredElementChange(element, CE_OTHER_EXPLODING);
 }
 
 void SplashAcid(int x, int y)
@@ -4812,10 +4851,12 @@ static void ChangeActiveTrap(int x, int y)
     DrawLevelFieldCrumbledSand(x, y);
 }
 
-static void ChangeElementDoIt(int x, int y, int element)
+static void ChangeElementDoIt(int x, int y, int element_new)
 {
+  CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING);
+
   RemoveField(x, y);
-  Feld[x][y] = element;
+  Feld[x][y] = element_new;
 
   ResetGfxAnimation(x, y);
   ResetRandomAnimationValue(x, y);
@@ -4893,6 +4934,8 @@ static void ChangeElement(int x, int y)
   }
   else                                 /* finish element change */
   {
+    int next_element = changing_element[element].next_element;
+
     if (IS_MOVING(x, y))               /* never change a running system ;-) */
     {
       ChangeDelay[x][y] = 1;           /* try change after next move step */
@@ -4900,53 +4943,37 @@ static void ChangeElement(int x, int y)
       return;
     }
 
-#if 1
-    ChangeElementDoIt(x, y, changing_element[element].next_element);
-#else
-    RemoveField(x, y);
-    Feld[x][y] = changing_element[element].next_element;
+    if (next_element != EL_UNDEFINED)
+      ChangeElementDoIt(x, y, next_element);
+    else
+      ChangeElementDoIt(x, y, element_info[element].change.successor);
 
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
+    if (changing_element[element].post_change_function)
+      changing_element[element].post_change_function(x, y);
+  }
+}
 
-    InitField(x, y, FALSE);
-    if (CAN_MOVE(Feld[x][y]))
-      InitMovDir(x, y);
+static void CheckTriggeredElementChange(int trigger_element, int trigger_event)
+{
+  int i, x, y;
 
-    DrawLevelField(x, y);
+  if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+    return;
 
-    if (CAN_BE_CRUMBLED(Feld[x][y]))
-    {
-      int sx = SCREENX(x), sy = SCREENY(y);
-      static int xy[4][2] =
-      {
-       { 0, -1 },
-       { -1, 0 },
-       { +1, 0 },
-       { 0, +1 }
-      };
-      int i;
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  {
+    if (!HAS_CHANGE_EVENT(i, trigger_event) ||
+       element_info[i].change.trigger != trigger_element)
+      continue;
 
-      for(i=0; i<4; i++)
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    {
+      if (Feld[x][y] == i)
       {
-       int xx = x + xy[i][0];
-       int yy = y + xy[i][1];
-       int sxx = sx + xy[i][0];
-       int syy = sy + xy[i][1];
-
-       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);
+       ChangeDelay[x][y] = 1;
+       ChangeElement(x, y);
       }
     }
-#endif
-
-    if (changing_element[element].post_change_function)
-      changing_element[element].post_change_function(x, y);
   }
 }
 
@@ -5182,7 +5209,7 @@ void GameActions()
 
 #if 1
     /* this may take place after moving, so 'element' may have changed */
-    if (IS_AUTO_CHANGING(element))
+    if (IS_CHANGING(x, y))
     {
       ChangeElement(x, y);
       element = Feld[x][y];
@@ -5272,7 +5299,7 @@ void GameActions()
 #endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
+    else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
 #if 0
@@ -6210,7 +6237,11 @@ void BuryHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 1
+  PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
+#else
   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
+#endif
   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
@@ -6349,7 +6380,13 @@ int DigField(struct PlayerInfo *player,
 
   switch (element)
   {
+#if 0
     case EL_EMPTY:
+      PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
+      break;
+#endif
+
+#if 0
     case EL_SAND:
     case EL_INVISIBLE_SAND:
     case EL_INVISIBLE_SAND_ACTIVE:
@@ -6358,15 +6395,16 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_BUGGY_BASE:
     case EL_SP_BUGGY_BASE_ACTIVATING:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP && element != EL_EMPTY)
       {
        GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
        player->is_digging = TRUE;
       }
-#endif
+
       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
       break;
+#endif
 
     case EL_EMERALD:
     case EL_BD_DIAMOND:
@@ -6378,13 +6416,13 @@ int DigField(struct PlayerInfo *player,
     case EL_PEARL:
     case EL_CRYSTAL:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP)
       {
        GfxElement[x][y] = element;
        player->is_collecting = TRUE;
       }
-#endif
+
       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
                                          element == EL_PEARL ? 5 :
                                          element == EL_CRYSTAL ? 8 : 1);
@@ -6394,18 +6432,31 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
+#if 0
     case EL_ENVELOPE:
       Feld[x][y] = EL_EMPTY;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
+#endif
 
     case EL_EXTRA_TIME:
       RemoveField(x, y);
@@ -6414,20 +6465,35 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
       }
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_NORMAL:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_DEADLY:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
       player->shield_deadly_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNAMITE:
@@ -6439,6 +6505,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
               FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_NUMBER:
@@ -6446,21 +6513,36 @@ int DigField(struct PlayerInfo *player,
       player->dynabomb_count++;
       player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_NUMBER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_SIZE:
       RemoveField(x, y);
       player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_SIZE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_POWER:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_POWER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_KEY_1:
@@ -6478,7 +6560,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6497,7 +6584,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6669,6 +6761,9 @@ int DigField(struct PlayerInfo *player,
 
       DrawLevelField(x + dx, y + dy);
       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
+
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_GATE_1:
@@ -6855,8 +6950,10 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+#if 0
     case EL_SOKOBAN_FIELD_EMPTY:
       break;
+#endif
 
     case EL_SOKOBAN_OBJECT:
     case EL_SOKOBAN_FIELD_FULL:
@@ -6954,6 +7051,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
 
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_PENGUIN:
@@ -6965,19 +7064,20 @@ int DigField(struct PlayerInfo *player,
 
       if (IS_WALKABLE(element))
       {
+       PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
        break;
       }
       else if (IS_DIGGABLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] =
            (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
          player->is_digging = TRUE;
        }
-#endif
+
        PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
 
        break;
@@ -6985,15 +7085,17 @@ int DigField(struct PlayerInfo *player,
       else if (IS_COLLECTIBLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] = element;
          player->is_collecting = TRUE;
        }
-#endif
+
        PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
+
        break;
       }
       else if (IS_PUSHABLE(element))
@@ -7036,6 +7138,8 @@ int DigField(struct PlayerInfo *player,
        DrawLevelField(x + dx, y + dy);
        PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
        break;
       }