rnd-20080802-1-src
[rocksndiamonds.git] / src / game.c
index 451c167fffe2cbd25c86fd8ad349912e123e7a47..a32fcfa7f5e4111302fe2a814b7363248861660a 100644 (file)
@@ -65,6 +65,8 @@
 
 #define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
 
+#define USE_NEW_PLAYER_ASSIGNMENTS     (USE_NEW_STUFF          * 1)
+
 #define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
 
 #if USE_DELAYED_GFX_REDRAW
@@ -1791,8 +1793,8 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     int player_nr = GET_PLAYER_NR(element);
     struct PlayerInfo *player = &stored_player[player_nr];
 
-    if (player->active)
-      player->killed = FALSE;  /* if player was just killed, reanimate him */
+    if (player->active && player->killed)
+      player->reanimated = TRUE; /* if player was just killed, reanimate him */
   }
 #endif
 }
@@ -3451,8 +3453,8 @@ static void InitGameEngine()
     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_PLAYER_1;
-      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
+      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;
@@ -3707,6 +3709,7 @@ void InitGame()
     player->present = FALSE;
     player->active = FALSE;
     player->killed = FALSE;
+    player->reanimated = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
@@ -4037,6 +4040,52 @@ void InitGame()
     if (game.belt_dir[i] == MV_NONE)
       game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
 
+#if USE_NEW_PLAYER_ASSIGNMENTS
+  /* check if any connected player was not found in playfield */
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->connected && !player->present)
+    {
+      for (j = 0; j < MAX_PLAYERS; j++)
+      {
+       struct PlayerInfo *some_player = &stored_player[j];
+       int jx = some_player->jx, jy = some_player->jy;
+
+       /* assign first free player found that is present in the playfield */
+       if (some_player->present && !some_player->connected)
+       {
+         player->present = FALSE;
+         player->active = FALSE;
+
+         some_player->present = TRUE;
+         some_player->active = TRUE;
+
+         /*
+         player->initial_element = some_player->initial_element;
+         player->artwork_element = some_player->artwork_element;
+
+         player->block_last_field       = some_player->block_last_field;
+         player->block_delay_adjustment = some_player->block_delay_adjustment;
+         */
+
+         StorePlayer[jx][jy] = some_player->element_nr;
+
+         some_player->jx = some_player->last_jx = jx;
+         some_player->jy = some_player->last_jy = jy;
+
+         if (local_player == player)
+           local_player = some_player;
+
+         break;
+       }
+      }
+    }
+  }
+
+#else
+
   /* check if any connected player was not found in playfield */
   for (i = 0; i < MAX_PLAYERS; i++)
   {
@@ -4065,6 +4114,7 @@ void InitGame()
          player->block_delay_adjustment = some_player->block_delay_adjustment;
 
          StorePlayer[jx][jy] = player->element_nr;
+
          player->jx = player->last_jx = jx;
          player->jy = player->last_jy = jy;
 
@@ -4073,6 +4123,7 @@ void InitGame()
       }
     }
   }
+#endif
 
   if (tape.playing)
   {
@@ -10168,12 +10219,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (level.time > 0 ? TimeLeft :
      TimePlayed);
 
-  int action_arg_element =
+  int action_arg_element_raw =
     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
+     action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
+     action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
+     action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
      EL_EMPTY);
+  int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
+
+#if 0
+  if (action_arg_element_raw == EL_GROUP_START)
+    printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
+#endif
 
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
@@ -10253,7 +10313,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_arg_number_min, action_arg_number_max);
 
 #if 1
-  int trigger_player_bits = change->actual_trigger_player_bits;
+  int trigger_player_bits =
+    (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+     change->actual_trigger_player_bits : change->trigger_player);
 #else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
@@ -10368,6 +10430,33 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_LEVEL_RANDOM_SEED:
+    {
+#if 1
+      /* ensure that setting a new random seed while playing is predictable */
+      InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
+#else
+      InitRND(action_arg_number_new);
+#endif
+
+#if 0
+      printf("::: %d -> %d\n", action_arg_number_new, RND(10));
+#endif
+
+#if 0
+      {
+       int i;
+
+       printf("::: ");
+       for (i = 0; i < 9; i++)
+         printf("%d, ", RND(2));
+       printf("\n");
+      }
+#endif
+
+      break;
+    }
+
     /* ---------- player actions  ------------------------------------------ */
 
     case CA_MOVE_PLAYER:
@@ -10422,6 +10511,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_PLAYER_SPEED:
     {
+#if 0
+      printf("::: trigger_player_bits == %d\n", trigger_player_bits);
+#endif
+
       for (i = 0; i < MAX_PLAYERS; i++)
       {
        if (trigger_player_bits & (1 << i))
@@ -10539,6 +10632,104 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_PLAYER_INVENTORY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int j, k;
+
+       if (trigger_player_bits & (1 << i))
+       {
+         int inventory_element = action_arg_element;
+
+         if (action_arg == CA_ARG_ELEMENT_TARGET ||
+             action_arg == CA_ARG_ELEMENT_TRIGGER ||
+             action_arg == CA_ARG_ELEMENT_ACTION)
+         {
+           int element = inventory_element;
+           int collect_count = element_info[element].collect_count_initial;
+
+           if (!IS_CUSTOM_ELEMENT(element))
+             collect_count = 1;
+
+           if (collect_count == 0)
+             player->inventory_infinite_element = element;
+           else
+             for (k = 0; k < collect_count; k++)
+               if (player->inventory_size < MAX_INVENTORY_SIZE)
+                 player->inventory_element[player->inventory_size++] =
+                   element;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+                  action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+                  action_arg == CA_ARG_INVENTORY_RM_ACTION)
+         {
+           if (player->inventory_infinite_element != EL_UNDEFINED &&
+               IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+                                    action_arg_element_raw))
+             player->inventory_infinite_element = EL_UNDEFINED;
+
+           for (k = 0, j = 0; j < player->inventory_size; j++)
+           {
+             if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+                                       action_arg_element_raw))
+               player->inventory_element[k++] = player->inventory_element[j];
+           }
+
+           player->inventory_size = k;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+         {
+           if (player->inventory_size > 0)
+           {
+             for (j = 0; j < player->inventory_size - 1; j++)
+               player->inventory_element[j] = player->inventory_element[j + 1];
+
+             player->inventory_size--;
+           }
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+         {
+           if (player->inventory_size > 0)
+             player->inventory_size--;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RESET)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+
+           if (level.use_initial_inventory[i])
+           {
+             for (j = 0; j < level.initial_inventory_size[i]; j++)
+             {
+               int element = level.initial_inventory_content[i][j];
+               int collect_count = element_info[element].collect_count_initial;
+
+               if (!IS_CUSTOM_ELEMENT(element))
+                 collect_count = 1;
+
+               if (collect_count == 0)
+                 player->inventory_infinite_element = element;
+               else
+                 for (k = 0; k < collect_count; k++)
+                   if (player->inventory_size < MAX_INVENTORY_SIZE)
+                     player->inventory_element[player->inventory_size++] =
+                       element;
+             }
+           }
+         }
+       }
+      }
+
+      break;
+    }
+
     /* ---------- CE actions  ---------------------------------------------- */
 
     case CA_SET_CE_VALUE:
@@ -10607,6 +10798,38 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_CE_ARTWORK:
+    {
+      int artwork_element = action_arg_element;
+      boolean reset_frame = FALSE;
+      int xx, yy;
+
+      if (action_arg == CA_ARG_ELEMENT_RESET)
+       artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+                          element);
+
+      if (ei->gfx_element != artwork_element)
+       reset_frame = TRUE;
+
+      ei->gfx_element = artwork_element;
+
+      SCAN_PLAYFIELD(xx, yy)
+      {
+       if (Feld[xx][yy] == element)
+       {
+         if (reset_frame)
+         {
+           ResetGfxAnimation(xx, yy);
+           ResetRandomAnimationValue(xx, yy);
+         }
+
+         TEST_DrawLevelField(xx, yy);
+       }
+      }
+
+      break;
+    }
+
     /* ---------- engine actions  ------------------------------------------ */
 
     case CA_SET_ENGINE_SCAN_MODE:
@@ -10766,8 +10989,8 @@ static boolean ChangeElement(int x, int y, int element, int page)
   {
     /* reset actual trigger element, trigger player and action element */
     change->actual_trigger_element = EL_EMPTY;
-    change->actual_trigger_player = EL_PLAYER_1;
-    change->actual_trigger_player_bits = CH_PLAYER_1;
+    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;
@@ -10927,6 +11150,7 @@ static void HandleElementChange(int x, int y, int page)
   int element = MovingOrBlocked2Element(x, y);
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
+  boolean handle_action_before_change = FALSE;
 
 #ifdef DEBUG
   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
@@ -11009,6 +11233,15 @@ static void HandleElementChange(int x, int y, int page)
       return;
     }
 
+#if 1
+    /* special case: set new level random seed before changing element */
+    if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
+      handle_action_before_change = TRUE;
+
+    if (change->has_action && handle_action_before_change)
+      ExecuteCustomElementAction(x, y, element, page);
+#endif
+
     if (change->can_change)
     {
       if (ChangeElement(x, y, element, page))
@@ -11018,7 +11251,7 @@ static void HandleElementChange(int x, int y, int page)
       }
     }
 
-    if (change->has_action)
+    if (change->has_action && !handle_action_before_change)
       ExecuteCustomElementAction(x, y, element, page);
   }
 }
@@ -11169,6 +11402,15 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
            {
              if (change->can_change && !change_done)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
 
 #if 0
                printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
@@ -11276,6 +11518,10 @@ static boolean CheckElementChangeExt(int x, int y,
 
   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
 
+#if 0
+  printf("::: X: trigger_player_bits == %d\n", trigger_player);
+#endif
+
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
     struct ElementChangeInfo *change = &element_info[element].change_page[p];
@@ -11801,7 +12047,7 @@ void GameActions()
   }
 
   if (game.restart_level)
-    StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
+    StartGameActions(options.network, setup.autorecord, level.random_seed);
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -14192,6 +14438,11 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 0
+  printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   /* the following code was introduced to prevent an infinite loop when calling
      -> Bang()
      -> CheckTriggeredElementChangeExt()
@@ -14216,11 +14467,28 @@ void KillPlayer(struct PlayerInfo *player)
   player->shield_normal_time_left = 0;
   player->shield_deadly_time_left = 0;
 
+#if 0
+  printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   Bang(jx, jy);
 
+#if 0
+  printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
 #if USE_PLAYER_REANIMATION
+#if 1
+  if (player->reanimated)      /* killed player may have been reanimated */
+    player->killed = player->reanimated = FALSE;
+  else
+    BuryPlayer(player);
+#else
   if (player->killed)          /* player may have been reanimated */
     BuryPlayer(player);
+#endif
 #else
   BuryPlayer(player);
 #endif