232 lines
6.4 KiB
C
232 lines
6.4 KiB
C
// The mouse (PLAN.md decision 4). Sierra's DOS interpreters had none; its
|
|
// Amiga, ST, IIgs, and Macintosh ones did, and this follows them as
|
|
// ScummVM describes them:
|
|
//
|
|
// - A pointer is drawn over the screen at every present while the machine
|
|
// has a mouse and the game has not hidden it (hide.mouse), kept to the
|
|
// box fence.mouse gives.
|
|
// - A left click on the menu bar opens the menus (as menu.input would);
|
|
// in the picture, with the player in control, ego walks to it: its
|
|
// baseline's middle to the click (objMouseWalk). A direction key, or in
|
|
// AGI 3 a new room, stops the walk.
|
|
// - In the menus the pointer picks the menu and the item, and in a message
|
|
// window a click is Enter (agiText.c, agiInput.c).
|
|
|
|
#include "agi.h"
|
|
|
|
#include "joey/core.h"
|
|
#include "joey/input.h"
|
|
#include "joey/sprite.h"
|
|
#include "joey/tile.h"
|
|
|
|
|
|
// The pointer: a 16x16 arrow, its point the hot spot. '.' is
|
|
// transparent, 'k' its outline (dark grey: color 0, black, is the
|
|
// sprite's transparent one), 'w' its white.
|
|
#define POINTER_TILES 2u
|
|
#define POINTER_PX (POINTER_TILES * JL_TILE_PIXELS_PER_SIDE)
|
|
#define POINTER_OUTLINE 8u
|
|
#define POINTER_FILL 15u
|
|
#define PIXELS_PER_BYTE 2u
|
|
#define NIBBLE_BITS 4u
|
|
#define FENCE_X1 0u
|
|
#define FENCE_Y1 1u
|
|
#define FENCE_X2 2u
|
|
#define FENCE_Y2 3u
|
|
|
|
|
|
// ----- Prototypes -----
|
|
|
|
static void pointerAt(int16_t *x, int16_t *y);
|
|
static bool pointerShown(void);
|
|
|
|
|
|
// ----- Tables -----
|
|
|
|
static const char *const kPointerArt[POINTER_PX] = {
|
|
"k...............",
|
|
"kk..............",
|
|
"kwk.............",
|
|
"kwwk............",
|
|
"kwwwk...........",
|
|
"kwwwwk..........",
|
|
"kwwwwwk.........",
|
|
"kwwwwwwk........",
|
|
"kwwwwwwwk.......",
|
|
"kwwwwwkkkk......",
|
|
"kwwkwwk.........",
|
|
"kwk.kwwk........",
|
|
"kk..kwwk........",
|
|
"k....kwwk.......",
|
|
".....kwwk.......",
|
|
"......kk........"
|
|
};
|
|
|
|
|
|
// ----- Module state -----
|
|
|
|
static uint8_t gPointerTiles[POINTER_TILES * POINTER_TILES * JL_TILE_BYTES];
|
|
static jlSpriteT *gPointer;
|
|
static uint16_t gPointerUnder[jlSpriteBackupLength(POINTER_TILES, POINTER_TILES)];
|
|
static jlSpriteBackupT gPointerSaved;
|
|
static bool gPointerDrawn;
|
|
|
|
|
|
// ----- Internal helpers (alphabetical) -----
|
|
|
|
// Where the pointer is on the screen, kept inside fence.mouse's box (in
|
|
// picture coordinates; all zero is no fence).
|
|
static void pointerAt(int16_t *x, int16_t *y) {
|
|
const uint8_t *f;
|
|
int16_t left;
|
|
int16_t top;
|
|
int16_t right;
|
|
int16_t bottom;
|
|
|
|
*x = jlMouseX();
|
|
*y = jlMouseY();
|
|
f = gAgi->mouseFence;
|
|
if (f[FENCE_X1] == 0u && f[FENCE_Y1] == 0u && f[FENCE_X2] == 0u && f[FENCE_Y2] == 0u) {
|
|
return;
|
|
}
|
|
left = (int16_t)(f[FENCE_X1] * AGI_SCREEN_PX_PER_AGI);
|
|
right = (int16_t)(f[FENCE_X2] * AGI_SCREEN_PX_PER_AGI);
|
|
top = (int16_t)(f[FENCE_Y1] + gAgi->picRow * AGI_CELL_PX);
|
|
bottom = (int16_t)(f[FENCE_Y2] + gAgi->picRow * AGI_CELL_PX);
|
|
if (*x < left) {
|
|
*x = left;
|
|
} else if (*x > right) {
|
|
*x = right;
|
|
}
|
|
if (*y < top) {
|
|
*y = top;
|
|
} else if (*y > bottom) {
|
|
*y = bottom;
|
|
}
|
|
}
|
|
|
|
|
|
static bool pointerShown(void) {
|
|
return gPointer != NULL && jlMousePresent() && !gAgi->mouseHidden;
|
|
}
|
|
|
|
|
|
// ----- Public API (alphabetical) -----
|
|
|
|
// A left click in a cycle (inputProcess): the menus on the menu bar, else
|
|
// a walk for ego.
|
|
void mouseClick(void) {
|
|
int16_t x;
|
|
int16_t y;
|
|
uint8_t ax;
|
|
uint8_t ay;
|
|
|
|
if (!pointerShown()) {
|
|
return;
|
|
}
|
|
pointerAt(&x, &y);
|
|
if (y < (int16_t)AGI_CELL_PX) {
|
|
if (gAgi->flags[FLAG_MENU] != 0u) {
|
|
gAgi->menuRequested = true;
|
|
}
|
|
return;
|
|
}
|
|
if (gAgi->playerControl && !gAgi->textMode) {
|
|
mousePosition(&ax, &ay);
|
|
objMouseWalk(ax, ay);
|
|
}
|
|
}
|
|
|
|
|
|
// The pointer onto the stage just before a present, what it covers kept.
|
|
void mouseDrawPointer(void) {
|
|
int16_t x;
|
|
int16_t y;
|
|
|
|
if (!pointerShown()) {
|
|
return;
|
|
}
|
|
pointerAt(&x, &y);
|
|
jlSpriteSaveAndDraw(jlStageGet(), gPointer, x, y, &gPointerSaved);
|
|
gPointerDrawn = true;
|
|
}
|
|
|
|
|
|
// And off again just after, so nothing else ever draws under it.
|
|
void mouseErasePointer(void) {
|
|
if (!gPointerDrawn) {
|
|
return;
|
|
}
|
|
jlSpriteRestoreUnder(jlStageGet(), &gPointerSaved);
|
|
gPointerDrawn = false;
|
|
}
|
|
|
|
|
|
void mouseInit(void) {
|
|
uint8_t tx;
|
|
uint8_t ty;
|
|
uint8_t r;
|
|
uint8_t b;
|
|
uint8_t pixel[PIXELS_PER_BYTE];
|
|
uint8_t p;
|
|
char c;
|
|
|
|
for (ty = 0u; ty < POINTER_TILES; ty++) {
|
|
for (tx = 0u; tx < POINTER_TILES; tx++) {
|
|
uint8_t *tile = &gPointerTiles[(ty * POINTER_TILES + tx) * JL_TILE_BYTES];
|
|
|
|
for (r = 0u; r < JL_TILE_PIXELS_PER_SIDE; r++) {
|
|
for (b = 0u; b < JL_TILE_BYTES_PER_ROW; b++) {
|
|
for (p = 0u; p < PIXELS_PER_BYTE; p++) {
|
|
c = kPointerArt[ty * JL_TILE_PIXELS_PER_SIDE + r][tx * JL_TILE_PIXELS_PER_SIDE + b * PIXELS_PER_BYTE + p];
|
|
pixel[p] = (c == 'k') ? POINTER_OUTLINE : ((c == 'w') ? POINTER_FILL : 0u);
|
|
}
|
|
tile[r * JL_TILE_BYTES_PER_ROW + b] = (uint8_t)((pixel[0] << NIBBLE_BITS) | pixel[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
gPointerSaved.bytes = (uint8_t *)gPointerUnder;
|
|
gPointer = jlSpriteCreate(gPointerTiles, POINTER_TILES, POINTER_TILES);
|
|
if (gPointer != NULL) {
|
|
(void)jlSpriteCompile(gPointer);
|
|
}
|
|
}
|
|
|
|
|
|
// The text cell under the pointer, when one is shown.
|
|
bool mouseOver(int16_t *col, int16_t *row) {
|
|
int16_t x;
|
|
int16_t y;
|
|
|
|
if (!pointerShown()) {
|
|
return false;
|
|
}
|
|
pointerAt(&x, &y);
|
|
*col = (int16_t)(x / AGI_CELL_PX);
|
|
*row = (int16_t)(y / AGI_CELL_PX);
|
|
return true;
|
|
}
|
|
|
|
|
|
// get.mse.posn: the pointer in picture coordinates, kept to the picture.
|
|
void mousePosition(uint8_t *x, uint8_t *y) {
|
|
int16_t sx;
|
|
int16_t sy;
|
|
int16_t mx;
|
|
int16_t my;
|
|
|
|
pointerAt(&sx, &sy);
|
|
mx = (int16_t)(sx / AGI_SCREEN_PX_PER_AGI);
|
|
my = (int16_t)(sy - (int16_t)gAgi->picRow * AGI_CELL_PX);
|
|
*x = (uint8_t)(mx < 0 ? 0 : (mx > AGI_WIDTH - 1 ? AGI_WIDTH - 1 : mx));
|
|
*y = (uint8_t)(my < 0 ? 0 : (my > AGI_MAX_Y ? AGI_MAX_Y : my));
|
|
}
|
|
|
|
|
|
void mouseShutdown(void) {
|
|
if (gPointer != NULL) {
|
|
jlSpriteDestroy(gPointer);
|
|
gPointer = NULL;
|
|
}
|
|
}
|