added tile selection cursor for playing MM levels with keyboard or joystick
authorHolger Schemel <info@artsoft.org>
Wed, 13 Dec 2017 07:39:30 +0000 (08:39 +0100)
committerHolger Schemel <info@artsoft.org>
Fri, 23 Mar 2018 22:21:17 +0000 (23:21 +0100)
src/conf_gfx.c
src/events.c
src/game_mm/mm_game.c
src/init.c
src/libgame/sdl.c
src/libgame/system.c
src/libgame/system.h
src/tools.c
src/tools.h

index 4be0ac3..8daac32 100644 (file)
@@ -7127,6 +7127,11 @@ struct ConfigInfo image_config[] =
   { "global.busy.frames_per_line",             "7"                     },
   { "global.busy.delay",                       "2"                     },
 
+  { "global.tile_cursor",                      "RocksMore.png"         },
+  { "global.tile_cursor.xpos",                 "10"                    },
+  { "global.tile_cursor.ypos",                 "7"                     },
+  { "global.tile_cursor.frames",               "1"                     },
+
   { "background",                              UNDEFINED_FILENAME      },
   { "background.TITLE_INITIAL",                        UNDEFINED_FILENAME      },
   { "background.TITLE",                                UNDEFINED_FILENAME      },
index 6648677..65b20da 100644 (file)
@@ -482,6 +482,9 @@ void HandleButtonEvent(ButtonEvent *event)
        event->x, event->y);
 #endif
 
+  // for any mouse button event, disable playfield tile cursor
+  SetTileCursorEnabled(FALSE);
+
 #if defined(HAS_SCREEN_KEYBOARD)
   if (video.shifted_up)
     event->y += video.shifted_up_pos;
@@ -1858,6 +1861,10 @@ void HandleKey(Key key, int key_status)
        if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
          TapeTogglePause(TAPE_TOGGLE_MANUAL);
       }
+
+      // for MM style levels, handle in-game keyboard input in HandleJoystick()
+      if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+       joy |= key_action;
     }
   }
   else
@@ -2166,6 +2173,37 @@ void HandleEventActions()
   }
 }
 
+static void HandleTileCursor(int dx, int dy, int button)
+{
+  if (!dx || !button)
+    ClearPlayerMouseAction();
+
+  if (!dx && !dy)
+    return;
+
+  if (button)
+  {
+    SetPlayerMouseAction(tile_cursor.x, tile_cursor.y,
+                        (dx < 0 ? MB_LEFTBUTTON :
+                         dx > 0 ? MB_RIGHTBUTTON : MB_RELEASED));
+  }
+  else
+  {
+    int old_xpos = tile_cursor.xpos;
+    int old_ypos = tile_cursor.ypos;
+    int new_xpos = old_xpos;
+    int new_ypos = old_ypos;
+
+    if (IN_LEV_FIELD(old_xpos + dx, old_ypos))
+      new_xpos = old_xpos + dx;
+
+    if (IN_LEV_FIELD(old_xpos, old_ypos + dy))
+      new_ypos = old_ypos + dy;
+
+    SetTileCursorTargetXY(new_xpos, new_ypos);
+  }
+}
+
 static int HandleJoystickForAllPlayers()
 {
   int i;
@@ -2201,9 +2239,13 @@ static int HandleJoystickForAllPlayers()
 
 void HandleJoystick()
 {
+  static unsigned int joytest_delay = 0;
+  static unsigned int joytest_delay_value = GADGET_FRAME_DELAY;
+  static int joytest_last = 0;
   int joystick = HandleJoystickForAllPlayers();
   int keyboard = key_joystick_mapping;
   int joy      = (joystick | keyboard);
+  int joytest   = joystick;
   int left     = joy & JOY_LEFT;
   int right    = joy & JOY_RIGHT;
   int up       = joy & JOY_UP;
@@ -2219,6 +2261,31 @@ void HandleJoystick()
     return;
   }
 
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  {
+    // when playing MM style levels, also use delay for keyboard events
+    if (game_status == GAME_MODE_PLAYING)
+      joytest |= keyboard;
+
+    // for any joystick or keyboard event, enable playfield tile cursor
+    if (dx || dy || button)
+      SetTileCursorEnabled(TRUE);
+  }
+
+  if (joytest && !button && !DelayReached(&joytest_delay, joytest_delay_value))
+  {
+    /* delay joystick/keyboard actions if axes/keys continually pressed */
+    newbutton = dx = dy = 0;
+  }
+  else
+  {
+    /* first start with longer delay, then continue with shorter delay */
+    joytest_delay_value =
+      (joytest != joytest_last ? GADGET_FRAME_DELAY_FIRST : GADGET_FRAME_DELAY);
+  }
+
+  joytest_last = joytest;
+
   switch (game_status)
   {
     case GAME_MODE_TITLE:
@@ -2229,25 +2296,6 @@ void HandleJoystick()
     case GAME_MODE_INFO:
     case GAME_MODE_SCORES:
     {
-      static unsigned int joystickmove_delay = 0;
-      static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
-      static int joystick_last = 0;
-
-      if (joystick && !button &&
-         !DelayReached(&joystickmove_delay, joystickmove_delay_value))
-      {
-       /* delay joystick actions if buttons/axes continually pressed */
-       newbutton = dx = dy = 0;
-      }
-      else
-      {
-       /* start with longer delay, then continue with shorter delay */
-       if (joystick != joystick_last)
-         joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
-       else
-         joystickmove_delay_value = GADGET_FRAME_DELAY;
-      }
-
       if (game_status == GAME_MODE_TITLE)
        HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
       else if (game_status == GAME_MODE_MAIN)
@@ -2263,8 +2311,6 @@ void HandleJoystick()
       else if (game_status == GAME_MODE_SCORES)
        HandleHallOfFame(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
 
-      joystick_last = joystick;
-
       break;
     }
 
@@ -2285,6 +2331,9 @@ void HandleJoystick()
          TapeTogglePause(TAPE_TOGGLE_MANUAL);
       }
 
+      if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+       HandleTileCursor(dx, dy, button);
+
       break;
 
     default:
index aff897b..ec206fa 100644 (file)
@@ -744,6 +744,9 @@ void InitGameActions_MM()
 
   if (game_mm.kettles_still_needed == 0)
     CheckExitMM();
+
+  SetTileCursorXY(laser.start_edge.x, laser.start_edge.y);
+  SetTileCursorActive(TRUE);
 }
 
 void AddLaserEdge(int lx, int ly)
@@ -1965,8 +1968,12 @@ boolean HitLaserDestination(int element, int hit_mask)
   AddDamagedField(ELX, ELY);
 
   if (game_mm.lights_still_needed == 0)
+  {
     game_mm.level_solved = TRUE;
 
+    SetTileCursorActive(FALSE);
+  }
+
   return TRUE;
 }
 
@@ -2474,6 +2481,8 @@ static void Explode_MM(int x, int y, int phase, int mode)
       game_mm.game_over = TRUE;
       game_mm.game_over_cause = GAME_OVER_BOMB;
 
+      SetTileCursorActive(FALSE);
+
       laser.overloaded = FALSE;
     }
     else if (IS_MCDUFFIN(Store[x][y]))
@@ -3155,6 +3164,8 @@ static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
       game_mm.game_over = TRUE;
       game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
 
+      SetTileCursorActive(FALSE);
+
 #if 0
       if (Request("Out of magic energy ! Play it again ?",
                  REQ_ASK | REQ_STAY_CLOSED))
@@ -3285,6 +3296,8 @@ static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
       game_mm.game_over = TRUE;
       game_mm.game_over_cause = GAME_OVER_OVERLOADED;
 
+      SetTileCursorActive(FALSE);
+
 #if 0
       if (Request("Magic spell hit Mc Duffin ! Play it again ?",
                  REQ_ASK | REQ_STAY_CLOSED))
index ff9f034..dc92912 100644 (file)
@@ -5431,6 +5431,7 @@ void InitGfx()
   InitGfxDrawBusyAnimFunction(DrawInitAnim);
   InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
   InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
+  InitGfxDrawTileCursorFunction(DrawTileCursor);
 
   gfx.fade_border_source_status = global.border_status;
   gfx.fade_border_target_status = global.border_status;
@@ -6008,6 +6009,7 @@ void OpenAll()
   InitVideoDisplay();
   InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
 
+  InitTileCursorInfo();
   InitOverlayInfo();
 
   print_timestamp_time("[init video stuff]");
index 9d2dc17..34c4004 100644 (file)
@@ -62,6 +62,10 @@ static void FinalizeScreen(int draw_target)
   // copy global animations to render target buffer, if defined (above border)
   if (gfx.draw_global_anim_function != NULL)
     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
+
+  // copy tile selection cursor to render target buffer, if defined (above all)
+  if (gfx.draw_tile_cursor_function != NULL)
+    gfx.draw_tile_cursor_function(draw_target);
 }
 
 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
index 6c2e968..dea7258 100644 (file)
@@ -33,6 +33,7 @@ struct OptionInfo     options;
 struct VideoSystemInfo video;
 struct AudioSystemInfo audio;
 struct GfxInfo         gfx;
+struct TileCursorInfo  tile_cursor;
 struct OverlayInfo     overlay;
 struct ArtworkInfo     artwork;
 struct JoystickInfo    joystick;
@@ -286,6 +287,11 @@ void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
   gfx.draw_global_border_function = draw_global_border_function;
 }
 
+void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
+{
+  gfx.draw_tile_cursor_function = draw_tile_cursor_function;
+}
+
 void InitGfxCustomArtworkInfo()
 {
   gfx.override_level_graphics = FALSE;
@@ -300,6 +306,19 @@ void InitGfxOtherSettings()
   gfx.cursor_mode = CURSOR_DEFAULT;
 }
 
+void InitTileCursorInfo()
+{
+  tile_cursor.enabled = FALSE;
+  tile_cursor.active = FALSE;
+
+  tile_cursor.xpos = 0;
+  tile_cursor.ypos = 0;
+  tile_cursor.x = 0;
+  tile_cursor.y = 0;
+  tile_cursor.target_x = 0;
+  tile_cursor.target_y = 0;
+}
+
 void InitOverlayInfo()
 {
   overlay.enabled = FALSE;
@@ -311,6 +330,37 @@ void InitOverlayInfo()
 #endif
 }
 
+void SetTileCursorEnabled(boolean enabled)
+{
+  tile_cursor.enabled = enabled;
+}
+
+void SetTileCursorActive(boolean active)
+{
+  tile_cursor.active = active;
+}
+
+void SetTileCursorTargetXY(int x, int y)
+{
+  // delayed placement of tile selection cursor at target position
+  // (tile cursor will be moved to target position step by step)
+
+  tile_cursor.xpos = x;
+  tile_cursor.ypos = y;
+  tile_cursor.target_x = gfx.sx + x * gfx.game_tile_size;
+  tile_cursor.target_y = gfx.sy + y * gfx.game_tile_size;
+}
+
+void SetTileCursorXY(int x, int y)
+{
+  // immediate placement of tile selection cursor at target position
+
+  SetTileCursorTargetXY(x, y);
+
+  tile_cursor.x = tile_cursor.target_x;
+  tile_cursor.y = tile_cursor.target_y;
+}
+
 void SetOverlayEnabled(boolean enabled)
 {
   overlay.enabled = enabled;
index 871fcd8..a2226a7 100644 (file)
@@ -949,10 +949,21 @@ struct GfxInfo
   void (*draw_busy_anim_function)(void);
   void (*draw_global_anim_function)(int, int);
   void (*draw_global_border_function)(int);
+  void (*draw_tile_cursor_function)(int);
 
   int cursor_mode;
 };
 
+struct TileCursorInfo
+{
+  boolean enabled;             /* tile cursor generally enabled or disabled */
+  boolean active;              /* tile cursor activated (depending on game) */
+
+  int xpos, ypos;              /* tile cursor level playfield position */
+  int x, y;                    /* tile cursor current screen position */
+  int target_x, target_y;      /* tile cursor target screen position */
+};
+
 struct OverlayInfo
 {
   boolean enabled;             /* overlay generally enabled or disabled */
@@ -1457,6 +1468,7 @@ extern struct OptionInfo  options;
 extern struct VideoSystemInfo  video;
 extern struct AudioSystemInfo  audio;
 extern struct GfxInfo          gfx;
+extern struct TileCursorInfo   tile_cursor;
 extern struct OverlayInfo      overlay;
 extern struct AnimInfo         anim;
 extern struct ArtworkInfo      artwork;
@@ -1511,9 +1523,15 @@ void InitGfxClipRegion(boolean, int, int, int, int);
 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void));
 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int));
 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int));
+void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int));
 void InitGfxCustomArtworkInfo();
 void InitGfxOtherSettings();
+void InitTileCursorInfo();
 void InitOverlayInfo();
+void SetTileCursorEnabled(boolean);
+void SetTileCursorActive(boolean);
+void SetTileCursorTargetXY(int, int);
+void SetTileCursorXY(int, int);
 void SetOverlayEnabled(boolean);
 void SetOverlayActive(boolean);
 boolean GetOverlayActive();
index 55c757a..3c301a3 100644 (file)
@@ -635,6 +635,61 @@ void DrawMaskedBorderToTarget(int draw_target)
   }
 }
 
+void DrawTileCursor(int draw_target)
+{
+  Bitmap *fade_bitmap;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int dst_x, dst_y;
+  int graphic = IMG_GLOBAL_TILE_CURSOR;
+  int frame = 0;
+  int tilesize = TILESIZE_VAR;
+  int width = tilesize;
+  int height = tilesize;
+
+  if (game_status != GAME_MODE_PLAYING)
+    return;
+
+  if (!tile_cursor.enabled ||
+      !tile_cursor.active)
+    return;
+
+  if (tile_cursor.x != tile_cursor.target_x ||
+      tile_cursor.y != tile_cursor.target_y)
+  {
+    int step = TILESIZE_VAR / (GADGET_FRAME_DELAY / video.frame_delay_value);
+    int dx = tile_cursor.target_x - tile_cursor.x;
+    int dy = tile_cursor.target_y - tile_cursor.y;
+
+    if (ABS(dx) < step)
+      tile_cursor.x = tile_cursor.target_x;
+    else
+      tile_cursor.x += SIGN(dx) * step;
+
+    if (ABS(dy) < step)
+      tile_cursor.y = tile_cursor.target_y;
+    else
+      tile_cursor.y += SIGN(dy) * step;
+  }
+
+  dst_x = tile_cursor.x;
+  dst_y = tile_cursor.y;
+
+  frame = getGraphicAnimationFrame(graphic, -1);
+
+  getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+
+  fade_bitmap =
+    (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
+     draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
+
+  if (draw_target == DRAW_TO_SCREEN)
+    BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
+  else
+    BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
+                    dst_x, dst_y);
+}
+
 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
 {
   int fx = getFieldbufferOffsetX_RND();
@@ -1005,6 +1060,7 @@ void FadeOut(int fade_mask)
 
   SetScreenStates_BeforeFadingOut();
 
+  SetTileCursorActive(FALSE);
   SetOverlayActive(FALSE);
 
 #if 0
index 37c67dd..5d9bdcc 100644 (file)
@@ -82,6 +82,7 @@ void DrawMaskedBorder_DOOR_3();
 void DrawMaskedBorder_ALL();
 void DrawMaskedBorder(int);
 void DrawMaskedBorderToTarget(int);
+void DrawTileCursor(int);
 
 void SetDrawtoField(int);
 void RedrawPlayfield();