added customizability of gray ball content for MM engine
authorHolger Schemel <info@artsoft.org>
Sun, 8 Jan 2023 20:10:53 +0000 (21:10 +0100)
committerHolger Schemel <info@artsoft.org>
Sun, 8 Jan 2023 20:10:53 +0000 (21:10 +0100)
src/editor.c
src/engines.h
src/files.c
src/game_mm/export.h
src/game_mm/mm_files.c
src/game_mm/mm_game.c
src/init.c
src/main.h

index 671f6bf9385c9d7caa22b4692feb17b5e50920a3..6df9c2eda5286351891676cf6881bf850fe95376 100644 (file)
@@ -459,6 +459,9 @@ enum
   GADGET_ID_INVENTORY_SIZE_DOWN,
   GADGET_ID_INVENTORY_SIZE_TEXT,
   GADGET_ID_INVENTORY_SIZE_UP,
+  GADGET_ID_MM_BALL_CONTENT_DOWN,
+  GADGET_ID_MM_BALL_CONTENT_TEXT,
+  GADGET_ID_MM_BALL_CONTENT_UP,
   GADGET_ID_CUSTOM_SCORE_DOWN,
   GADGET_ID_CUSTOM_SCORE_TEXT,
   GADGET_ID_CUSTOM_SCORE_UP,
@@ -539,6 +542,7 @@ enum
   GADGET_ID_ARTWORK_ELEMENT,
   GADGET_ID_EXPLOSION_ELEMENT,
   GADGET_ID_INVENTORY_CONTENT,
+  GADGET_ID_MM_BALL_CONTENT,
   GADGET_ID_CUSTOM_GRAPHIC,
   GADGET_ID_CUSTOM_CONTENT,
   GADGET_ID_CUSTOM_MOVE_ENTER,
@@ -570,6 +574,7 @@ enum
   GADGET_ID_LEVELSET_SAVE_MODE,
   GADGET_ID_WIND_DIRECTION,
   GADGET_ID_PLAYER_SPEED,
+  GADGET_ID_MM_BALL_CHOICE_MODE,
   GADGET_ID_CUSTOM_WALK_TO_ACTION,
   GADGET_ID_CUSTOM_EXPLOSION_TYPE,
   GADGET_ID_CUSTOM_DEADLINESS,
@@ -750,6 +755,7 @@ enum
   ED_COUNTER_ID_ENVELOPE_XSIZE,
   ED_COUNTER_ID_ENVELOPE_YSIZE,
   ED_COUNTER_ID_INVENTORY_SIZE,
+  ED_COUNTER_ID_MM_BALL_CONTENT,
   ED_COUNTER_ID_CUSTOM_SCORE,
   ED_COUNTER_ID_CUSTOM_GEMCOUNT,
   ED_COUNTER_ID_CUSTOM_VALUE_FIX,
@@ -856,6 +862,7 @@ enum
   ED_SELECTBOX_ID_LEVELSET_SAVE_MODE,
   ED_SELECTBOX_ID_WIND_DIRECTION,
   ED_SELECTBOX_ID_PLAYER_SPEED,
+  ED_SELECTBOX_ID_MM_BALL_CHOICE_MODE,
   ED_SELECTBOX_ID_CUSTOM_ACCESS_TYPE,
   ED_SELECTBOX_ID_CUSTOM_ACCESS_LAYER,
   ED_SELECTBOX_ID_CUSTOM_ACCESS_PROTECTED,
@@ -1079,6 +1086,7 @@ enum
   ED_DRAWING_ID_ARTWORK_ELEMENT,
   ED_DRAWING_ID_EXPLOSION_ELEMENT,
   ED_DRAWING_ID_INVENTORY_CONTENT,
+  ED_DRAWING_ID_MM_BALL_CONTENT,
   ED_DRAWING_ID_CUSTOM_GRAPHIC,
   ED_DRAWING_ID_CUSTOM_CONTENT,
   ED_DRAWING_ID_CUSTOM_MOVE_ENTER,
@@ -1533,6 +1541,14 @@ static struct
     &level.initial_inventory_size[0],
     NULL,                              NULL, "number of inventory elements"
   },
+  {
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(3),
+    MIN_ELEMENTS_IN_GROUP,             MAX_MM_BALL_CONTENTS,
+    GADGET_ID_MM_BALL_CONTENT_DOWN,    GADGET_ID_MM_BALL_CONTENT_UP,
+    GADGET_ID_MM_BALL_CONTENT_TEXT,    GADGET_ID_NONE,
+    &level.num_mm_ball_contents,
+    NULL,                              NULL, "number of content elements"
+  },
 
   // ---------- element settings: configure 1 (custom elements) ---------------
 
@@ -2555,6 +2571,14 @@ static struct
     &level.initial_player_stepsize[0],
     NULL, "initial player speed:", NULL,       "initial player speed"
   },
+  {
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(4),
+    GADGET_ID_MM_BALL_CHOICE_MODE,     GADGET_ID_NONE,
+    -1,
+    options_group_choice_mode,
+    &level.mm_ball_choice_mode,
+    NULL, "choice type:", NULL,                "type of content choice"
+  },
 
   // ---------- element settings: configure 1 (custom elements) ---------------
 
@@ -3740,6 +3764,16 @@ static struct
     NULL, NULL, NULL, NULL,            "content for initial inventory"
   },
 
+  // ---------- gray ball content -----------------------------------------
+
+  {
+    ED_AREA_1X1_SETTINGS_XPOS(0),      ED_AREA_1X1_SETTINGS_YPOS(2),
+    ED_AREA_1X1_SETTINGS_XOFF,         ED_AREA_1X1_SETTINGS_YOFF,
+    GADGET_ID_MM_BALL_CONTENT,         GADGET_ID_NONE,
+    &level.mm_ball_content[0],         MAX_MM_BALL_CONTENTS, 1,
+    "content:", NULL, NULL, NULL,      "content for gray ball"
+  },
+
   // ---------- element settings: configure 1 (custom elements) ---------------
 
   // ---------- custom graphic ------------------------------------------------
@@ -9577,6 +9611,31 @@ static void DrawPlayerInitialInventoryArea(int element)
   MapDrawingArea(id);
 }
 
+static void DrawMMBallContentArea(void)
+{
+  int id = ED_DRAWING_ID_MM_BALL_CONTENT;
+  int num_elements = level.num_mm_ball_contents;
+  int border_size = ED_DRAWINGAREA_BORDER_SIZE;
+  int sx = SX + ED_AREA_SETTINGS_X(drawingarea_info[id]) - border_size;
+  int sy = SY + ED_AREA_SETTINGS_Y(drawingarea_info[id]) - border_size;
+  int xsize = MAX_MM_BALL_CONTENTS;
+  int ysize = 1;
+
+  if (drawingarea_info[id].text_left != NULL)
+    sx += getTextWidthForDrawingArea(drawingarea_info[id].text_left);
+
+  UnmapDrawingArea(id);
+
+  ModifyEditorDrawingArea(id, num_elements, 1);
+
+  // delete content areas in case of reducing number of them
+  DrawBackground(sx, sy,
+                xsize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size,
+                ysize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size);
+
+  MapDrawingArea(id);
+}
+
 static void DrawEnvelopeTextArea(int envelope_nr)
 {
   int id = ED_TEXTAREA_ID_ENVELOPE_INFO;
@@ -10060,6 +10119,13 @@ static void DrawPropertiesConfig(void)
     }
     else if (properties_element == EL_EMC_ANDROID)
       DrawAndroidElementArea();
+    else if (properties_element == EL_MM_GRAY_BALL)
+    {
+      MapCounterButtons(ED_COUNTER_ID_MM_BALL_CONTENT);
+      MapSelectboxGadget(ED_SELECTBOX_ID_MM_BALL_CHOICE_MODE);
+
+      DrawMMBallContentArea();
+    }
   }
 
   if (IS_PLAYER_ELEMENT(properties_element))
@@ -13325,6 +13391,10 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
       DrawPlayerInitialInventoryArea(properties_element);
       break;
 
+    case ED_COUNTER_ID_MM_BALL_CONTENT:
+      DrawMMBallContentArea();
+      break;
+
     case ED_COUNTER_ID_ENVELOPE_XSIZE:
     case ED_COUNTER_ID_ENVELOPE_YSIZE:
       DrawEnvelopeTextArea(-1);
index dadfa21dfed5c8bc604736a7ff78697e269692ee..4f9aa1e0b889322fc829c000507807919c8175ce 100644 (file)
@@ -75,5 +75,6 @@ void getSizedGraphicSource(int, int, int, Bitmap **, int *, int *);
 void AdvanceFrameCounter(void);
 void AdvanceGfxFrame(void);
 
+int getAnimationFrame(int, int, int, int, int);
 
 #endif // ENGINES_H
index 3f1b200e693e8f3e83d3a43466dc1b2dd0786f8c..926ce926d71ff247a157b8ad9a47c381cc28e490 100644 (file)
@@ -904,11 +904,24 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
     &li.mm_time_bomb,                  75
   },
+
   {
     EL_MM_GRAY_BALL,                   -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
     &li.mm_time_ball,                  75
   },
+  {
+    EL_MM_GRAY_BALL,                   -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(1),
+    &li.mm_ball_choice_mode,           ANIM_RANDOM
+  },
+  {
+    EL_MM_GRAY_BALL,                   -1,
+    TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(1),
+    &li.mm_ball_content,               EL_EMPTY, NULL,
+    &li.num_mm_ball_contents,          8, MAX_MM_BALL_CONTENTS
+  },
+
   {
     EL_MM_STEEL_BLOCK,                 -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
@@ -1841,6 +1854,16 @@ static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
     int element = i;
     struct ElementInfo *ei = &element_info[element];
 
+    if (element == EL_MM_GRAY_BALL)
+    {
+      struct LevelInfo_MM *level_mm = level->native_mm_level;
+      int j;
+
+      for (j = 0; j < level->num_mm_ball_contents; j++)
+       level->mm_ball_content[j] =
+         map_element_MM_to_RND(level_mm->ball_content[j]);
+    }
+
     // never initialize clipboard elements after the very first time
     // (to be able to use clipboard elements between several levels)
     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
@@ -4129,7 +4152,7 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
 {
   struct LevelInfo_MM *level_mm = level->native_mm_level;
-  int x, y;
+  int i, x, y;
 
   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
@@ -4157,6 +4180,13 @@ static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
   level_mm->time_ball    = level->mm_time_ball;
   level_mm->time_block   = level->mm_time_block;
 
+  level_mm->num_ball_contents = level->num_mm_ball_contents;
+  level_mm->ball_choice_mode = level->mm_ball_choice_mode;
+
+  for (i = 0; i < level->num_mm_ball_contents; i++)
+    level_mm->ball_content[i] =
+      map_element_RND_to_MM(level->mm_ball_content[i]);
+
   for (x = 0; x < level->fieldx; x++)
     for (y = 0; y < level->fieldy; y++)
       Ur[x][y] =
@@ -4166,7 +4196,7 @@ static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
 {
   struct LevelInfo_MM *level_mm = level->native_mm_level;
-  int x, y;
+  int i, x, y;
 
   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
@@ -4197,6 +4227,13 @@ static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
   level->mm_time_ball  = level_mm->time_ball;
   level->mm_time_block = level_mm->time_block;
 
+  level->num_mm_ball_contents = level_mm->num_ball_contents;
+  level->mm_ball_choice_mode = level_mm->ball_choice_mode;
+
+  for (i = 0; i < level->num_mm_ball_contents; i++)
+    level->mm_ball_content[i] =
+      map_element_MM_to_RND(level_mm->ball_content[i]);
+
   for (x = 0; x < level->fieldx; x++)
     for (y = 0; y < level->fieldy; y++)
       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
index f69946245731da069b9024720821c2a837b4c9e2..e9d84fda44a870cfea5940571bdbf5e6595275c8 100644 (file)
@@ -27,6 +27,8 @@
 
 #define MM_LEVEL_SCORE_ELEMENTS                16
 
+#define MM_MAX_BALL_CONTENTS           16
+
 #define MM_MAX_LEVEL_NAME_LEN          32
 #define MM_MAX_LEVEL_AUTHOR_LEN                32
 
@@ -138,6 +140,7 @@ struct GameInfo_MM
   int kettles_still_needed;
   int lights_still_needed;
   int num_keys;
+  int ball_choice_pos;         // current content element choice position
 
   boolean level_solved;
   boolean game_over;
@@ -171,6 +174,10 @@ struct LevelInfo_MM
   int time_ball;
   int time_block;
 
+  int num_ball_contents;
+  int ball_choice_mode;
+  int ball_content[MM_MAX_BALL_CONTENTS];
+
   short field[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
 };
 
index 542c847db9e86c7d85d32fd37b6e2cbdd5be4ca9..d334b8a0b3fb8932bb289172f9012c55c76be3b4 100644 (file)
@@ -127,6 +127,25 @@ void setLevelInfoToDefaults_MM(void)
   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     native_mm_level.score[i] = 10;
 
+  int ball_content[] =
+  {
+    EL_MIRROR_START,
+    EL_MIRROR_FIXED_START,
+    EL_POLAR_START,
+    EL_POLAR_CROSS_START,
+    EL_PACMAN_START,
+    EL_KETTLE,
+    EL_BOMB,
+    EL_PRISM
+  };
+  int num_ball_contents = sizeof(ball_content) / sizeof(int);
+
+  native_mm_level.num_ball_contents = num_ball_contents;
+  native_mm_level.ball_choice_mode = ANIM_RANDOM;
+
+  for (i = 0; i < num_ball_contents; i++)
+    native_mm_level.ball_content[i] = ball_content[i];
+
   native_mm_level.field[0][0] = Ur[0][0] = EL_MCDUFFIN_RIGHT;
   native_mm_level.field[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
index e817b1187613c8cb76445be6ba5b48137bd8267f..a68a64b692193e54d820fcf94fb79805200877b7 100644 (file)
@@ -625,6 +625,7 @@ void InitGameEngine_MM(void)
     (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
   game_mm.lights_still_needed = 0;
   game_mm.num_keys = 0;
+  game_mm.ball_choice_pos = 0;
 
   game_mm.level_solved = FALSE;
   game_mm.game_over = FALSE;
@@ -3333,19 +3334,22 @@ static void GameActions_MM_Ext(void)
 
   if (element == EL_BALL_GRAY && CT > native_mm_level.time_ball)
   {
-    static int new_elements[] =
-    {
-      EL_MIRROR_START,
-      EL_MIRROR_FIXED_START,
-      EL_POLAR_START,
-      EL_POLAR_CROSS_START,
-      EL_PACMAN_START,
-      EL_KETTLE,
-      EL_BOMB,
-      EL_PRISM
-    };
-    int num_new_elements = sizeof(new_elements) / sizeof(int);
-    int new_element = new_elements[RND(num_new_elements)];
+    int last_anim_random_frame = gfx.anim_random_frame;
+    int element_pos;
+
+    if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = RND(native_mm_level.num_ball_contents);
+
+    element_pos = getAnimationFrame(native_mm_level.num_ball_contents, 1,
+                                    native_mm_level.ball_choice_mode, 0,
+                                    game_mm.ball_choice_pos);
+
+    if (native_mm_level.ball_choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = last_anim_random_frame;
+
+    game_mm.ball_choice_pos++;
+
+    int new_element = native_mm_level.ball_content[element_pos];
 
     Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
     Tile[ELX][ELY] = EL_GRAY_BALL_OPENING;
index 6fd4ae087a8e5b4135de7de6cded6a737fc2020b..f07be1d395e341c05e9b0dc7ac0c796e49cea4f5 100644 (file)
@@ -4240,6 +4240,7 @@ void InitElementPropertiesStatic(void)
     EL_BD_AMOEBA,
     EL_EMC_MAGIC_BALL,
     EL_EMC_ANDROID,
+    EL_MM_GRAY_BALL,
 
     -1
   };
index 5800f5ef5dbdca6e94fffe4c32862d6ec4ca1117..a943547a875a67819374a679b030e06ac8c38cd3 100644 (file)
 #define STD_ELEMENT_CONTENTS   4
 #define MAX_ELEMENT_CONTENTS   8
 
+#define MIN_MM_BALL_CONTENTS   1
+#define STD_MM_BALL_CONTENTS   8
+#define MAX_MM_BALL_CONTENTS   16
+
 // values for initial player inventory
 #define MIN_INITIAL_INVENTORY_SIZE     1
 #define MAX_INITIAL_INVENTORY_SIZE     8
@@ -3290,6 +3294,10 @@ struct LevelInfo
   int mm_time_ball;
   int mm_time_block;
 
+  int num_mm_ball_contents;
+  int mm_ball_choice_mode;
+  int mm_ball_content[MAX_MM_BALL_CONTENTS];
+
   // ('int' instead of 'boolean' because used as selectbox value in editor)
   int use_step_counter;                // count steps instead of seconds for level