mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-28 20:55:19 +00:00
94edf540ef
svn-id: r11025
1368 lines
33 KiB
C++
1368 lines
33 KiB
C++
/* Copyright (C) 1994-2003 Revolution Software Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "sword2/driver/driver96.h"
|
|
#include "sword2/build_display.h"
|
|
#include "sword2/console.h"
|
|
#include "sword2/controls.h"
|
|
#include "sword2/defs.h"
|
|
#include "sword2/events.h"
|
|
#include "sword2/icons.h"
|
|
#include "sword2/interpreter.h"
|
|
#include "sword2/logic.h"
|
|
#include "sword2/layers.h"
|
|
#include "sword2/maketext.h"
|
|
#include "sword2/mouse.h"
|
|
#include "sword2/object.h"
|
|
#include "sword2/protocol.h"
|
|
#include "sword2/resman.h"
|
|
#include "sword2/sound.h"
|
|
#include "sword2/sword2.h"
|
|
|
|
namespace Sword2 {
|
|
|
|
// pointer resource id's
|
|
|
|
#define CROSHAIR 18
|
|
#define EXIT0 788
|
|
#define EXIT1 789
|
|
#define EXIT2 790
|
|
#define EXIT3 791
|
|
#define EXIT4 792
|
|
#define EXIT5 793
|
|
#define EXIT6 794
|
|
#define EXIT7 795
|
|
#define EXITDOWN 796
|
|
#define EXITUP 797
|
|
#define MOUTH 787
|
|
#define NORMAL 17
|
|
#define PICKUP 3099
|
|
#define SCROLL_L 1440
|
|
#define SCROLL_R 1441
|
|
#define USE 3100
|
|
|
|
//the mouse list stuff
|
|
|
|
uint32 cur_mouse;
|
|
Mouse_unit mouse_list[TOTAL_mouse_list];
|
|
|
|
// set by Check_mouse_list
|
|
uint32 mouse_touching = 0;
|
|
static uint32 old_mouse_touching = 0;
|
|
|
|
static uint32 menu_selected_pos;
|
|
uint8 examining_menu_icon = 0;
|
|
|
|
// if it's NORMAL_MOUSE_ID (ie. normal pointer) then it's over a floor area
|
|
// (or hidden hot-zone)
|
|
|
|
static uint32 mouse_pointer_res = 0;
|
|
|
|
// 0 normal in game
|
|
// 1 menu chooser
|
|
// 2 dragging luggage
|
|
// 3 system menu chooser
|
|
// 4 speech chooser
|
|
|
|
uint32 mouse_mode = 0;
|
|
|
|
static uint32 menu_status; // 0 available - 1 unavailable
|
|
uint32 mouse_status; // human 0 on/1 off
|
|
|
|
// 0 not !0 mode cannot be changed from normal mouse to menu (i.e. when
|
|
// carrying big objects)
|
|
|
|
uint32 mouse_mode_locked = 0;
|
|
static uint32 current_luggage_resource = 0;
|
|
|
|
// for the re-click stuff - must be same button you see
|
|
static uint32 old_button = 0;
|
|
static uint32 button_click = 0;
|
|
|
|
static uint32 pointer_text_bloc_no = 0;
|
|
|
|
static uint32 player_activity_delay = 0; // player activity delay counter
|
|
|
|
uint32 real_luggage_item = 0; // last minute for pause mode
|
|
|
|
void CreatePointerText(uint32 TextId, uint32 pointerRes);
|
|
void Monitor_player_activity(void);
|
|
|
|
void No_human(void);
|
|
|
|
void Reset_mouse_list(void) {
|
|
// call at beginning of gameloop
|
|
cur_mouse = 1;
|
|
}
|
|
|
|
/**
|
|
* This function is called every game cycle.
|
|
*/
|
|
|
|
void Mouse_engine(void) {
|
|
Monitor_player_activity();
|
|
ClearPointerText();
|
|
|
|
// If George is dead, the system menu is visible all the time, and is
|
|
// the only thing that can be used.
|
|
|
|
if (DEAD) {
|
|
if (mouse_mode != MOUSE_system_menu) {
|
|
mouse_mode = MOUSE_system_menu;
|
|
|
|
if (mouse_touching) {
|
|
old_mouse_touching = 0;
|
|
mouse_touching = 0;
|
|
}
|
|
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
Build_system_menu();
|
|
}
|
|
System_menu_mouse();
|
|
return;
|
|
}
|
|
|
|
// If the mouse is not visible, do nothing
|
|
|
|
if (mouse_status)
|
|
return;
|
|
|
|
switch (mouse_mode) {
|
|
case MOUSE_normal:
|
|
Normal_mouse();
|
|
break;
|
|
case MOUSE_menu:
|
|
Menu_mouse();
|
|
break;
|
|
case MOUSE_drag:
|
|
Drag_mouse();
|
|
break;
|
|
case MOUSE_system_menu:
|
|
System_menu_mouse();
|
|
break;
|
|
case MOUSE_holding:
|
|
if (g_display->_mouseY < 400) {
|
|
mouse_mode = MOUSE_normal;
|
|
debug(5, " releasing");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void System_menu_mouse(void) {
|
|
uint32 safe_looping_music_id;
|
|
_mouseEvent *me;
|
|
int j, hit;
|
|
uint8 *icon;
|
|
int32 pars[2];
|
|
uint32 icon_list[5] = {
|
|
OPTIONS_ICON,
|
|
QUIT_ICON,
|
|
SAVE_ICON,
|
|
RESTORE_ICON,
|
|
RESTART_ICON
|
|
};
|
|
|
|
// can't close when player is dead
|
|
if (g_display->_mouseY > 0 && !DEAD) {
|
|
// close menu
|
|
mouse_mode = MOUSE_normal;
|
|
g_display->hideMenu(RDMENU_TOP);
|
|
return;
|
|
}
|
|
|
|
me = MouseEvent();
|
|
|
|
if (me && (me->buttons & RD_LEFTBUTTONDOWN)) {
|
|
// clicked on a top mouse pointer?
|
|
|
|
if (g_display->_mouseX >= 24 && g_display->_mouseX < 640 - 24 && g_display->_mouseY < 0) {
|
|
// which are we over?
|
|
hit = (g_display->_mouseX - 24) / 40;
|
|
|
|
// no save when dead
|
|
if (icon_list[hit] == SAVE_ICON && DEAD)
|
|
return;
|
|
|
|
// there are 5 system menu icons
|
|
if (hit < ARRAYSIZE(icon_list)) {
|
|
// build them all high in full colour - when
|
|
// one is clicked on all the rest will grey out
|
|
for (j = 0; j < ARRAYSIZE(icon_list); j++) {
|
|
// change all others to grey
|
|
if (j != hit) {
|
|
icon = res_man.open(icon_list[j]) + sizeof(_standardHeader);
|
|
g_display->setMenuIcon(RDMENU_TOP, j, icon);
|
|
res_man.close( icon_list[j] );
|
|
}
|
|
}
|
|
|
|
g_sound->pauseFx();
|
|
|
|
// NB. Need to keep a safe copy of
|
|
// 'looping_music_id' for savegame & for
|
|
// playing when returning from control panels
|
|
// because control panel music will overwrite
|
|
// it!
|
|
|
|
safe_looping_music_id = looping_music_id;
|
|
|
|
pars[0] = 221; // SystemM234 (M234.wav)
|
|
pars[1] = FX_LOOP;
|
|
g_logic.fnPlayMusic(pars);
|
|
|
|
// restore proper looping_music_id
|
|
looping_music_id = safe_looping_music_id;
|
|
|
|
// clear the screen & set up the new palette
|
|
// for the menus
|
|
|
|
g_display->clearScene();
|
|
g_display->processMenu();
|
|
g_display->resetRenderEngine();
|
|
|
|
// call the relevent screen
|
|
switch (hit) {
|
|
case 0:
|
|
gui.optionControl();
|
|
break;
|
|
case 1:
|
|
gui.quitControl();
|
|
break;
|
|
case 2:
|
|
gui.saveControl();
|
|
break;
|
|
case 3:
|
|
gui.restoreControl();
|
|
break;
|
|
case 4:
|
|
gui.restartControl();
|
|
break;
|
|
}
|
|
|
|
// Menu stays open on death screen
|
|
if (!DEAD) {
|
|
mouse_mode = MOUSE_normal;
|
|
g_display->hideMenu(RDMENU_TOP);
|
|
} else {
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
Build_system_menu();
|
|
}
|
|
|
|
// clear the screen & restore the location
|
|
// palette
|
|
|
|
g_display->clearScene();
|
|
g_display->processMenu();
|
|
|
|
// reset game palette, but not after a
|
|
// successful restore or restart!
|
|
|
|
// see RestoreFromBuffer() in save_rest.cpp
|
|
if (this_screen.new_palette != 99) {
|
|
// '0' means put back game screen
|
|
// palette; see Build_display.cpp
|
|
|
|
g_sword2->setFullPalette(0);
|
|
|
|
// stop the engine fading in the
|
|
// restored screens palette
|
|
|
|
this_screen.new_palette = 0;
|
|
} else
|
|
this_screen.new_palette = 1;
|
|
|
|
g_sound->unpauseFx();
|
|
|
|
// If there was looping music before coming
|
|
// into the control panels then restart it!
|
|
// NB. This will also start music required
|
|
// when a game has been restored
|
|
|
|
if (looping_music_id) {
|
|
pars[0] = looping_music_id;
|
|
pars[1] = FX_LOOP;
|
|
g_logic.fnPlayMusic(pars);
|
|
|
|
// cross-fades into the required music:
|
|
// - either a restored game tune
|
|
// - or music playing prior to
|
|
// entering control panels
|
|
} else {
|
|
// stop the control panel music
|
|
g_logic.fnStopMusic(NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Drag_mouse(void) {
|
|
_mouseEvent *me;
|
|
uint32 pos;
|
|
|
|
if (g_display->_mouseY < 400 && !menu_status) {
|
|
// close menu
|
|
mouse_mode = MOUSE_normal;
|
|
g_display->hideMenu(RDMENU_BOTTOM);
|
|
return;
|
|
}
|
|
|
|
// handles cursors and the luggage on/off according to type
|
|
Mouse_on_off();
|
|
|
|
// now do the normal click stuff
|
|
|
|
me = MouseEvent();
|
|
|
|
// we only care about left clicks when the mouse is over an object
|
|
// we ignore mouse releases
|
|
|
|
if (me && (me->buttons & RD_LEFTBUTTONDOWN)) {
|
|
// there's a mouse event to be processed
|
|
|
|
// could be clicking on an on screen object or on the menu
|
|
// which is currently displayed
|
|
|
|
if (mouse_touching) {
|
|
// mouse is over an on screen object - and we have
|
|
// luggage
|
|
|
|
// depending on type we'll maybe kill the object_held
|
|
// - like for exits
|
|
|
|
// Set global script variable 'button'. We know that
|
|
// it was the left button, not the right one.
|
|
|
|
LEFT_BUTTON = 1;
|
|
RIGHT_BUTTON = 0;
|
|
|
|
// these might be required by the action script about
|
|
// to be run
|
|
|
|
MOUSE_X = (uint32) g_display->_mouseX + this_screen.scroll_offset_x;
|
|
MOUSE_Y = (uint32) g_display->_mouseY + this_screen.scroll_offset_y;
|
|
|
|
// for scripts to know what's been clicked (21jan97).
|
|
// First used for 'room_13_turning_script' in object
|
|
// 'biscuits_13'
|
|
|
|
CLICKED_ID = mouse_touching;
|
|
|
|
Set_player_action_event(CUR_PLAYER_ID, mouse_touching);
|
|
|
|
debug(5, "USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID));
|
|
|
|
// Hide menu - back to normal menu mode
|
|
|
|
g_display->hideMenu(RDMENU_BOTTOM);
|
|
mouse_mode = MOUSE_normal;
|
|
} else {
|
|
// better check for combine/cancel
|
|
// cancel puts us back in Menu_mouse mode
|
|
if (g_display->_mouseX >= 24 && g_display->_mouseX < 640 - 24) {
|
|
// which are we over?
|
|
pos = (g_display->_mouseX - 24) / 40;
|
|
|
|
//clicked on something - what button?
|
|
if (master_menu_list[pos].icon_resource) {
|
|
// always back into menu mode
|
|
mouse_mode = MOUSE_menu;
|
|
|
|
// remove luggage
|
|
Set_luggage(0);
|
|
|
|
// if we've clicked on the same icon
|
|
// as the one we're dragging
|
|
|
|
if (pos == menu_selected_pos) {
|
|
// reset first icon
|
|
OBJECT_HELD = 0;
|
|
menu_selected_pos = 0;
|
|
} else {
|
|
// combine the 2 icons
|
|
debug(5, "combine");
|
|
|
|
//what we clicked on, not what
|
|
// we're dragging
|
|
|
|
COMBINE_BASE = master_menu_list[pos].icon_resource;
|
|
Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT);
|
|
|
|
// turn off mouse now, to
|
|
// prevent player trying to
|
|
// click elsewhere BUT leave
|
|
// the bottom menu open
|
|
|
|
No_human();
|
|
|
|
debug(5, "USED \"%s\" ICON ON \"%s\" ICON", FetchObjectName(OBJECT_HELD), FetchObjectName(COMBINE_BASE));
|
|
}
|
|
|
|
// refresh the menu
|
|
Build_menu();
|
|
debug(5, "switch to menu mode");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Menu_mouse(void) {
|
|
// menu is down
|
|
|
|
_mouseEvent *me;
|
|
uint32 pos;
|
|
|
|
if (g_display->_mouseY < 400 && !menu_status) {
|
|
// close menu
|
|
mouse_mode = MOUSE_normal;
|
|
g_display->hideMenu(RDMENU_BOTTOM);
|
|
return;
|
|
}
|
|
|
|
me = MouseEvent();
|
|
|
|
// we only care about left clicks when the mouse is over an object
|
|
// we ignore mouse releases
|
|
|
|
if (me) {
|
|
// there's a mouse event to be processed
|
|
// now check if we've clicked on an actual icon
|
|
|
|
if (g_display->_mouseX >= 24 && g_display->_mouseX < 640 - 24) {
|
|
// which are we over?
|
|
pos = (g_display->_mouseX - 24) / 40;
|
|
|
|
// clicked on something - what button?
|
|
if (master_menu_list[pos].icon_resource) {
|
|
if (me->buttons & RD_RIGHTBUTTONDOWN) {
|
|
// right button look
|
|
examining_menu_icon = 1;
|
|
|
|
// id the object via its graphic
|
|
OBJECT_HELD = master_menu_list[pos].icon_resource;
|
|
|
|
// Must clear this so next click on
|
|
// exit becomes 1st click again
|
|
|
|
EXIT_CLICK_ID = 0;
|
|
|
|
debug(5, "RIGHT-CLICKED ON \"%s\" ICON", FetchObjectName(OBJECT_HELD));
|
|
|
|
Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT);
|
|
|
|
// refresh the menu
|
|
Build_menu();
|
|
|
|
// turn off mouse now, to prevent
|
|
// player trying to click elsewhere
|
|
// BUT leave the bottom menu open
|
|
|
|
No_human();
|
|
} else if (me->buttons & RD_LEFTBUTTONDOWN) {
|
|
// left button - highlight the object
|
|
// and bung us into drag luggage mode
|
|
|
|
// menu slot we clicked on - derive
|
|
// luggage resource from this in
|
|
// mouse_on_off()
|
|
|
|
menu_selected_pos = pos;
|
|
current_luggage_resource = master_menu_list[pos].luggage_resource;
|
|
|
|
mouse_mode = MOUSE_drag;
|
|
debug(5, "setting OH in menu");
|
|
|
|
// id the object via its graphic
|
|
OBJECT_HELD = master_menu_list[pos].icon_resource;
|
|
|
|
// must clear this so next click on
|
|
// exit becomes 1st click again
|
|
|
|
EXIT_CLICK_ID = 0;
|
|
|
|
// refresh the menu
|
|
Build_menu();
|
|
|
|
Set_luggage(master_menu_list[pos].luggage_resource);
|
|
debug(5, "switch to drag mode");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Normal_mouse(void) {
|
|
// The gane is playing and none of the menus are activated - but, we
|
|
// need to check if a menu is to start. Note, won't have luggage
|
|
|
|
_mouseEvent *me;
|
|
|
|
// no save in big-object menu lock situation
|
|
if (g_display->_mouseY < 0 && !menu_status && !mouse_mode_locked && !OBJECT_HELD) {
|
|
mouse_mode = MOUSE_system_menu;
|
|
|
|
if (mouse_touching) {
|
|
// We were on something, but not anymore
|
|
old_mouse_touching = 0;
|
|
mouse_touching = 0;
|
|
}
|
|
|
|
// reset mouse cursor - in case we're between mice
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
Build_system_menu();
|
|
return;
|
|
}
|
|
|
|
if (g_display->_mouseY > 399 && !menu_status && !mouse_mode_locked) {
|
|
// If an object is being held, i.e. if the mouse cursor has a
|
|
// luggage, we should be use dragging mode instead of inventory
|
|
// menu mode.
|
|
//
|
|
// That way, we can still use an object on another inventory
|
|
// object, even if the inventory menu was closed after the
|
|
// first object was selected.
|
|
|
|
if (!OBJECT_HELD)
|
|
mouse_mode = MOUSE_menu;
|
|
else
|
|
mouse_mode = MOUSE_drag;
|
|
|
|
// If mouse is moving off an object and onto the menu then do a
|
|
// standard get-off
|
|
|
|
if (mouse_touching) {
|
|
// We were on something, but not anymore
|
|
|
|
old_mouse_touching = 0;
|
|
mouse_touching = 0;
|
|
}
|
|
|
|
// reset mouse cursor
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
Build_menu();
|
|
return;
|
|
}
|
|
|
|
// check for moving the mouse on or off things
|
|
|
|
Mouse_on_off();
|
|
|
|
// now do the normal click stuff
|
|
|
|
me = MouseEvent();
|
|
|
|
if (definingRectangles) {
|
|
if (draggingRectangle == 0) {
|
|
// not yet dragging a rectangle, so need click to start
|
|
|
|
if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN))) {
|
|
// set both (x1,y1) and (x2,y2) to this point
|
|
rect_x1 = rect_x2 = (uint32) g_display->_mouseX + this_screen.scroll_offset_x;
|
|
rect_y1 = rect_y2 = (uint32) g_display->_mouseY + this_screen.scroll_offset_y;
|
|
draggingRectangle = 1;
|
|
}
|
|
} else if (draggingRectangle == 1) {
|
|
// currently dragging a rectangle - click means reset
|
|
|
|
if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN))) {
|
|
// lock rectangle, so you can let go of mouse
|
|
// to type in the coords
|
|
draggingRectangle = 2;
|
|
} else {
|
|
// drag rectangle
|
|
rect_x2 = (uint32) g_display->_mouseX + this_screen.scroll_offset_x;
|
|
rect_y2 = (uint32) g_display->_mouseY + this_screen.scroll_offset_y;
|
|
}
|
|
} else {
|
|
// currently locked to avoid knocking out of place
|
|
// while reading off the coords
|
|
|
|
if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN))) {
|
|
// click means reset - back to start again
|
|
draggingRectangle = 0;
|
|
}
|
|
}
|
|
} else {
|
|
// We only care about down clicks when the mouse is over an
|
|
// object. We ignore mouse releases
|
|
|
|
if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)) && mouse_touching) {
|
|
// there's a mouse event to be processed and the mouse
|
|
// is on something
|
|
|
|
// ok, there are no menus about so its nice and simple
|
|
// this is as close to the old advisor_188 script as we
|
|
// get, I'm sorry to say.
|
|
|
|
// if player is walking or relaxing then those need to
|
|
// terminate correctly
|
|
|
|
// otherwise set player run the targets action script
|
|
// or, do a special walk if clicking on the scroll-more
|
|
// icon
|
|
|
|
// PLAYER_ACTION script variable - whatever catches
|
|
// this must reset to 0 again
|
|
// PLAYER_ACTION = mouse_touching;
|
|
|
|
// idle or router-anim will catch it
|
|
|
|
// set global script variable 'button'
|
|
|
|
if (me->buttons & RD_LEFTBUTTONDOWN) {
|
|
LEFT_BUTTON = 1;
|
|
RIGHT_BUTTON = 0;
|
|
button_click = 0; // for re-click
|
|
} else {
|
|
LEFT_BUTTON = 0;
|
|
RIGHT_BUTTON = 1;
|
|
button_click = 1; // for re-click
|
|
}
|
|
|
|
// these might be required by the action script about
|
|
// to be run
|
|
MOUSE_X = (uint32) g_display->_mouseX + this_screen.scroll_offset_x;
|
|
MOUSE_Y = (uint32) g_display->_mouseY + this_screen.scroll_offset_y;
|
|
|
|
// only left button
|
|
if (mouse_touching == EXIT_CLICK_ID && (me->buttons & RD_LEFTBUTTONDOWN)) {
|
|
// its the exit double click situation
|
|
// let the existing interaction continue and
|
|
// start fading down - switch the human off too
|
|
|
|
g_logic.fnNoHuman(NULL);
|
|
g_logic.fnFadeDown(NULL);
|
|
EXIT_FADING = 1; // tell the walker
|
|
} else if (old_button == button_click && mouse_touching == CLICKED_ID && mouse_pointer_res != NORMAL_MOUSE_ID) {
|
|
// re-click - do nothing - except on floors
|
|
} else {
|
|
// allow the click
|
|
old_button = button_click; // for re-click
|
|
|
|
// for scripts to know what's been clicked
|
|
// First used for 'room_13_turning_script' in
|
|
// object 'biscuits_13'
|
|
|
|
CLICKED_ID = mouse_touching;
|
|
|
|
// must clear these two double-click control
|
|
// flags - do it here so reclicks after exit
|
|
// clicks are cleared up
|
|
|
|
EXIT_CLICK_ID = 0;
|
|
EXIT_FADING = 0;
|
|
|
|
Set_player_action_event(CUR_PLAYER_ID, mouse_touching);
|
|
|
|
if (OBJECT_HELD)
|
|
debug(5, "USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID));
|
|
else if (LEFT_BUTTON)
|
|
debug(5, "LEFT-CLICKED ON %s", FetchObjectName(CLICKED_ID));
|
|
else // RIGHT BUTTON
|
|
debug(5, "RIGHT-CLICKED ON %s", FetchObjectName(CLICKED_ID));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mouse_on_off(void) {
|
|
// this handles the cursor graphic when moving on and off mouse areas
|
|
// it also handles the luggage thingy
|
|
|
|
uint32 pointer_type;
|
|
static uint8 mouse_flicked_off = 0;
|
|
|
|
old_mouse_touching = mouse_touching;
|
|
|
|
// don't detect objects that are hidden behind the menu bars (ie. in
|
|
// the scrolled-off areas of the screen)
|
|
|
|
if (g_display->_mouseY < 0 || g_display->_mouseY > 399) {
|
|
pointer_type = 0;
|
|
mouse_touching = 0;
|
|
} else {
|
|
// set 'mouse_touching' & return pointer_type
|
|
pointer_type = Check_mouse_list();
|
|
}
|
|
|
|
// same as previous cycle?
|
|
if (!mouse_flicked_off && old_mouse_touching == mouse_touching) {
|
|
// yes, so nothing to do
|
|
// BUT CARRY ON IF MOUSE WAS FLICKED OFF!
|
|
return;
|
|
}
|
|
|
|
// can reset this now
|
|
mouse_flicked_off = 0;
|
|
|
|
//the cursor has moved onto something
|
|
if (!old_mouse_touching && mouse_touching) {
|
|
// make a copy of the object we've moved onto because one day
|
|
// we'll move back off again! (but the list positioning could
|
|
// theoretically have changed)
|
|
|
|
// we can only move onto something from being on nothing - we
|
|
// stop the system going from one to another when objects
|
|
// overlap
|
|
|
|
old_mouse_touching = mouse_touching;
|
|
|
|
// run get on
|
|
|
|
if (pointer_type) {
|
|
// 'pointer_type' holds the resource id of the
|
|
// pointer anim
|
|
|
|
Set_mouse(pointer_type);
|
|
|
|
// setup luggage icon
|
|
if (OBJECT_HELD) {
|
|
// Set_luggage(master_menu_list[menu_selected_pos].luggage_resource);
|
|
Set_luggage(current_luggage_resource);
|
|
}
|
|
} else
|
|
error("ERROR: mouse.pointer==0 for object %d (%s) - update logic script!", mouse_touching, FetchObjectName(mouse_touching));
|
|
} else if (old_mouse_touching && !mouse_touching) {
|
|
// the cursor has moved off something - reset cursor to
|
|
// normal pointer
|
|
|
|
old_mouse_touching = 0;
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
|
|
// reset luggage only when necessary
|
|
} else if (old_mouse_touching && mouse_touching) {
|
|
// The cursor has moved off something and onto something
|
|
// else. Flip to a blank cursor for a cycle.
|
|
|
|
// ignore the new id this cycle - should hit next cycle
|
|
mouse_touching = 0;
|
|
old_mouse_touching = 0;
|
|
Set_mouse(0);
|
|
|
|
// so we know to set the mouse pointer back to normal if 2nd
|
|
// hot-spot doesn't register because mouse pulled away
|
|
// quickly (onto nothing)
|
|
|
|
mouse_flicked_off = 1;
|
|
|
|
// reset luggage only when necessary
|
|
} else {
|
|
// Mouse was flicked off for one cycle, but then moved onto
|
|
// nothing before 2nd hot-spot registered
|
|
|
|
// both 'old_mouse_touching' & 'mouse_touching' will be zero
|
|
// reset cursor to normal pointer
|
|
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
}
|
|
|
|
// possible check for edge of screen more-to-scroll here on large
|
|
// screens
|
|
}
|
|
|
|
void Set_mouse(uint32 res) {
|
|
uint8 *icon;
|
|
uint32 len;
|
|
|
|
// high level - whats the mouse - for the engine
|
|
mouse_pointer_res = res;
|
|
|
|
if (res) {
|
|
icon = res_man.open(res) + sizeof(_standardHeader);
|
|
len = res_man._resList[res]->size - sizeof(_standardHeader);
|
|
|
|
// don't pulse the normal pointer - just do the regular anim
|
|
// loop
|
|
|
|
if (res == NORMAL_MOUSE_ID)
|
|
g_display->setMouseAnim(icon, len, RDMOUSE_NOFLASH);
|
|
else
|
|
g_display->setMouseAnim(icon, len, RDMOUSE_FLASH);
|
|
|
|
res_man.close(res);
|
|
} else {
|
|
// blank cursor
|
|
g_display->setMouseAnim(NULL, 0, 0);
|
|
}
|
|
}
|
|
|
|
void Set_luggage(uint32 res) {
|
|
uint8 *icon;
|
|
uint32 len;
|
|
|
|
real_luggage_item = res;
|
|
|
|
if (res) {
|
|
icon = res_man.open(res) + sizeof(_standardHeader);
|
|
len = res_man._resList[res]->size - sizeof(_standardHeader);
|
|
|
|
g_display->setLuggageAnim(icon, len);
|
|
|
|
res_man.close(res);
|
|
} else
|
|
g_display->setLuggageAnim(NULL, 0);
|
|
}
|
|
|
|
uint32 Check_mouse_list(void) {
|
|
int32 priority = 0;
|
|
uint32 j = 1;
|
|
|
|
if (cur_mouse > 1) {
|
|
//number of priorities subject to implementation needs
|
|
while (priority < 10) {
|
|
// if the mouse pointer is over this
|
|
// mouse-detection-box
|
|
|
|
if (mouse_list[j].priority == priority &&
|
|
g_display->_mouseX + this_screen.scroll_offset_x >= mouse_list[j].x1 &&
|
|
g_display->_mouseX + this_screen.scroll_offset_x <= mouse_list[j].x2 &&
|
|
g_display->_mouseY + this_screen.scroll_offset_y >= mouse_list[j].y1 &&
|
|
g_display->_mouseY + this_screen.scroll_offset_y <= mouse_list[j].y2) {
|
|
// record id
|
|
mouse_touching = mouse_list[j].id;
|
|
|
|
// change all COGS pointers to CROSHAIR
|
|
if (mouse_list[j].pointer == USE)
|
|
mouse_list[j].pointer = CROSHAIR;
|
|
|
|
CreatePointerText(mouse_list[j].pointer_text, mouse_list[j].pointer);
|
|
|
|
// return pointer type
|
|
return mouse_list[j].pointer;
|
|
}
|
|
|
|
j++;
|
|
if (j == cur_mouse) {
|
|
j = 0;
|
|
// next priority - 0 being the highest, 9 the
|
|
// lowest
|
|
priority++;
|
|
}
|
|
}
|
|
}
|
|
|
|
mouse_touching = 0; // touching nothing
|
|
return 0; // no pointer to return
|
|
}
|
|
|
|
#define POINTER_TEXT_WIDTH 640 // just in case!
|
|
#define POINTER_TEXT_PEN 184 // white
|
|
|
|
void CreatePointerText(uint32 textId, uint32 pointerRes) {
|
|
uint32 local_text;
|
|
uint32 text_res;
|
|
uint8 *text;
|
|
// offsets for pointer text sprite from pointer position
|
|
int16 xOffset, yOffset;
|
|
uint8 justification;
|
|
|
|
if (gui._pointerTextSelected) {
|
|
if (textId) {
|
|
// check what the pointer is, to set offsets
|
|
// correctly for text position
|
|
|
|
switch (pointerRes) {
|
|
case CROSHAIR:
|
|
yOffset = -7;
|
|
xOffset = +10;
|
|
break;
|
|
case EXIT0:
|
|
yOffset = +15;
|
|
xOffset = +20;
|
|
break;
|
|
case EXIT1:
|
|
yOffset = +16;
|
|
xOffset = -10;
|
|
break;
|
|
case EXIT2:
|
|
yOffset = +10;
|
|
xOffset = -22;
|
|
break;
|
|
case EXIT3:
|
|
yOffset = -16;
|
|
xOffset = -10;
|
|
break;
|
|
case EXIT4:
|
|
yOffset = -15;
|
|
xOffset = +15;
|
|
break;
|
|
case EXIT5:
|
|
yOffset = -12;
|
|
xOffset = +10;
|
|
break;
|
|
case EXIT6:
|
|
yOffset = +10;
|
|
xOffset = +25;
|
|
break;
|
|
case EXIT7:
|
|
yOffset = +16;
|
|
xOffset = +20;
|
|
break;
|
|
case EXITDOWN:
|
|
yOffset = -20;
|
|
xOffset = -10;
|
|
break;
|
|
case EXITUP:
|
|
yOffset = +20;
|
|
xOffset = +20;
|
|
break;
|
|
case MOUTH:
|
|
yOffset = -10;
|
|
xOffset = +15;
|
|
break;
|
|
case NORMAL:
|
|
yOffset = -10;
|
|
xOffset = +15;
|
|
break;
|
|
case PICKUP:
|
|
yOffset = -40;
|
|
xOffset = +10;
|
|
break;
|
|
case SCROLL_L:
|
|
yOffset = -20;
|
|
xOffset = +20;
|
|
break;
|
|
case SCROLL_R:
|
|
yOffset = -20;
|
|
xOffset = -20;
|
|
break;
|
|
case USE:
|
|
yOffset = -8;
|
|
xOffset = +20;
|
|
break;
|
|
default:
|
|
// shouldn't happen if we cover all the
|
|
// different mouse pointers above
|
|
yOffset = -10;
|
|
xOffset = +10;
|
|
break;
|
|
}
|
|
|
|
// set up justification for text sprite, based on its
|
|
// offsets from the pointer position
|
|
|
|
if (yOffset < 0) {
|
|
// above pointer
|
|
if (xOffset < 0) {
|
|
// above left
|
|
justification = POSITION_AT_RIGHT_OF_BASE;
|
|
} else if (xOffset > 0) {
|
|
// above right
|
|
justification = POSITION_AT_LEFT_OF_BASE;
|
|
} else {
|
|
// above centre
|
|
justification = POSITION_AT_CENTRE_OF_BASE;
|
|
}
|
|
} else if (yOffset > 0) {
|
|
// below pointer
|
|
if (xOffset < 0) {
|
|
// below left
|
|
justification = POSITION_AT_RIGHT_OF_TOP;
|
|
} else if (xOffset > 0) {
|
|
// below right
|
|
justification = POSITION_AT_LEFT_OF_TOP;
|
|
} else {
|
|
// below centre
|
|
justification = POSITION_AT_CENTRE_OF_TOP;
|
|
}
|
|
} else {
|
|
// same y-coord as pointer
|
|
if (xOffset < 0) {
|
|
// centre left
|
|
justification = POSITION_AT_RIGHT_OF_CENTRE;
|
|
} else if (xOffset > 0) {
|
|
// centre right
|
|
justification = POSITION_AT_LEFT_OF_CENTRE;
|
|
} else {
|
|
// centre centre - shouldn't happen
|
|
// anyway!
|
|
justification = POSITION_AT_LEFT_OF_CENTRE;
|
|
}
|
|
}
|
|
|
|
// text resource number, and line number within the
|
|
// resource
|
|
|
|
text_res = textId / SIZE;
|
|
local_text = textId & 0xffff;
|
|
|
|
// open text file & get the line
|
|
text = FetchTextLine(res_man.open(text_res), local_text);
|
|
|
|
// 'text+2' to skip the first 2 bytes which form the
|
|
// line reference number
|
|
|
|
pointer_text_bloc_no = fontRenderer.buildNewBloc(
|
|
text + 2, g_display->_mouseX + xOffset,
|
|
g_display->_mouseY + yOffset,
|
|
POINTER_TEXT_WIDTH, POINTER_TEXT_PEN,
|
|
RDSPR_TRANS | RDSPR_DISPLAYALIGN,
|
|
g_sword2->_speechFontId, justification);
|
|
|
|
// now ok to close the text file
|
|
res_man.close(text_res);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClearPointerText(void) {
|
|
if (pointer_text_bloc_no) {
|
|
fontRenderer.killTextBloc(pointer_text_bloc_no);
|
|
pointer_text_bloc_no = 0;
|
|
}
|
|
}
|
|
|
|
int32 Logic::fnNoHuman(int32 *params) {
|
|
// params: none
|
|
|
|
// for logic scripts
|
|
MOUSE_AVAILABLE = 0;
|
|
|
|
ClearPointerText();
|
|
|
|
// human/mouse off
|
|
mouse_status = 1;
|
|
|
|
Set_mouse(0);
|
|
Set_luggage(0);
|
|
|
|
// must be normal mouse situation or a largely neutral situation -
|
|
// special menus use No_human
|
|
|
|
// dont hide menu in conversations
|
|
if (TALK_FLAG == 0)
|
|
g_display->hideMenu(RDMENU_BOTTOM);
|
|
|
|
if (mouse_mode == MOUSE_system_menu) {
|
|
// close menu
|
|
mouse_mode = MOUSE_normal;
|
|
g_display->hideMenu(RDMENU_TOP);
|
|
}
|
|
|
|
// script continue
|
|
return IR_CONT;
|
|
}
|
|
|
|
void No_human(void) {
|
|
// leaves the menus open
|
|
// used by the system when clicking right on a menu item to examine
|
|
// it and when combining objects
|
|
|
|
// for logic scripts
|
|
MOUSE_AVAILABLE = 0;
|
|
|
|
// human/mouse off
|
|
mouse_status = 1;
|
|
|
|
Set_mouse(0);
|
|
Set_luggage(0);
|
|
}
|
|
|
|
int32 Logic::fnAddHuman(int32 *params) {
|
|
// params: none
|
|
|
|
// for logic scripts
|
|
MOUSE_AVAILABLE = 1;
|
|
|
|
// off
|
|
if (mouse_status) {
|
|
mouse_status = 0; // on
|
|
mouse_touching = 1; // forces engine to choose a cursor
|
|
}
|
|
|
|
//clear this to reset no-second-click system
|
|
CLICKED_ID = 0;
|
|
|
|
// this is now done outside the OBJECT_HELD check in case it's set to
|
|
// zero before now!
|
|
|
|
// unlock the mouse from possible large object lock situtations - see
|
|
// syphon in rm 3
|
|
|
|
mouse_mode_locked = 0;
|
|
|
|
if (OBJECT_HELD) {
|
|
// was dragging something around
|
|
// need to clear this again
|
|
OBJECT_HELD = 0;
|
|
|
|
// and these may also need clearing, just in case
|
|
examining_menu_icon = 0;
|
|
COMBINE_BASE = 0;
|
|
|
|
Set_luggage(0);
|
|
}
|
|
|
|
// if mouse is over menu area
|
|
if (g_display->_mouseY > 399) {
|
|
if (mouse_mode != MOUSE_holding) {
|
|
// VITAL - reset things & rebuild the menu
|
|
mouse_mode = MOUSE_normal;
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
} else
|
|
Set_mouse(NORMAL_MOUSE_ID);
|
|
}
|
|
|
|
// enabled/disabled from console; status printed with on-screen debug
|
|
// info
|
|
|
|
if (testingSnR) {
|
|
uint8 black[4] = { 0, 0, 0, 0 };
|
|
uint8 white[4] = { 255, 255, 255, 0 };
|
|
|
|
// testing logic scripts by simulating an instant Save &
|
|
// Restore
|
|
|
|
g_display->setPalette(0, 1, white, RDPAL_INSTANT);
|
|
|
|
// stops all fx & clears the queue - eg. when leaving a
|
|
// location
|
|
|
|
Clear_fx_queue();
|
|
|
|
// Trash all object resources so they load in fresh & restart
|
|
// their logic scripts
|
|
|
|
res_man.killAllObjects(false);
|
|
|
|
g_display->setPalette(0, 1, black, RDPAL_INSTANT);
|
|
}
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnRegisterMouse(int32 *params) {
|
|
// this call would be made from an objects service script 0
|
|
// the object would be one with no graphic but with a mouse - i.e. a
|
|
// floor or one whose mouse area is manually defined rather than
|
|
// intended to fit sprite shape
|
|
|
|
// params: 0 pointer to Object_mouse or 0 for no write to mouse
|
|
// list
|
|
|
|
debug(5, "cur_mouse = %d", cur_mouse);
|
|
|
|
Object_mouse *ob_mouse = (Object_mouse *) params[0];
|
|
|
|
// only if 'pointer' isn't NULL
|
|
if (ob_mouse->pointer) {
|
|
#ifdef _SWORD2_DEBUG
|
|
if (cur_mouse == TOTAL_mouse_list)
|
|
error("ERROR: mouse_list full");
|
|
#endif
|
|
|
|
mouse_list[cur_mouse].x1 = ob_mouse->x1;
|
|
mouse_list[cur_mouse].y1 = ob_mouse->y1;
|
|
mouse_list[cur_mouse].x2 = ob_mouse->x2;
|
|
mouse_list[cur_mouse].y2 = ob_mouse->y2;
|
|
|
|
mouse_list[cur_mouse].priority = ob_mouse->priority;
|
|
mouse_list[cur_mouse].pointer = ob_mouse->pointer;
|
|
|
|
// (James17jun97) check if pointer text field is set due to
|
|
// previous object using this slot (ie. not correct for this
|
|
// one)
|
|
|
|
// if 'pointer_text' field is set, but the 'id' field isn't
|
|
// same is current id, then we don't want this "left over"
|
|
// pointer text
|
|
|
|
if (mouse_list[cur_mouse].pointer_text && mouse_list[cur_mouse].id != (int32) ID)
|
|
mouse_list[cur_mouse].pointer_text = 0;
|
|
|
|
// get id from system variable 'id' which is correct for
|
|
// current object
|
|
|
|
mouse_list[cur_mouse].id = ID;
|
|
|
|
// not using sprite as mask - this is only done from
|
|
// fnRegisterFrame()
|
|
|
|
mouse_list[cur_mouse].anim_resource = 0;
|
|
mouse_list[cur_mouse].anim_pc = 0;
|
|
|
|
debug(5, "mouse id %d", mouse_list[cur_mouse].id);
|
|
cur_mouse++;
|
|
}
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
// use this in the object's service script prior to registering the mouse area
|
|
// ie. before fnRegisterMouse or fnRegisterFrame
|
|
// - best if kept at very top of service script
|
|
|
|
int32 Logic::fnRegisterPointerText(int32 *params) {
|
|
// params: 0 local id of text line to use as pointer text
|
|
|
|
#ifdef _SWORD2_DEBUG
|
|
if (cur_mouse == TOTAL_mouse_list)
|
|
error("ERROR: mouse_list full");
|
|
#endif
|
|
|
|
// current object id - used for checking pointer_text when mouse area
|
|
// registered (in fnRegisterMouse and fnRegisterFrame)
|
|
|
|
mouse_list[cur_mouse].id = ID;
|
|
mouse_list[cur_mouse].pointer_text = params[0];
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnInitFloorMouse(int32 *params) {
|
|
// params: 0 pointer to object's mouse structure
|
|
|
|
Object_mouse *ob_mouse = (Object_mouse *) params[0];
|
|
|
|
// floor is always lowest priority
|
|
|
|
ob_mouse->x1 = 0;
|
|
ob_mouse->y1 = 0;
|
|
ob_mouse->x2 = this_screen.screen_wide - 1;
|
|
ob_mouse->y2 = this_screen.screen_deep - 1;
|
|
ob_mouse->priority = 9;
|
|
ob_mouse->pointer = NORMAL_MOUSE_ID;
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
#define SCROLL_MOUSE_WIDTH 20
|
|
|
|
int32 Logic::fnSetScrollLeftMouse(int32 *params) {
|
|
// params: 0 pointer to object's mouse structure
|
|
|
|
Object_mouse *ob_mouse = (Object_mouse *) params[0];
|
|
|
|
// Highest priority
|
|
|
|
ob_mouse->x1 = 0;
|
|
ob_mouse->y1 = 0;
|
|
ob_mouse->x2 = this_screen.scroll_offset_x + SCROLL_MOUSE_WIDTH;
|
|
ob_mouse->y2 = this_screen.screen_deep - 1;
|
|
ob_mouse->priority = 0;
|
|
|
|
if (this_screen.scroll_offset_x > 0) {
|
|
// not fully scrolled to the left
|
|
ob_mouse->pointer = SCROLL_LEFT_MOUSE_ID;
|
|
} else {
|
|
// so the mouse area doesn't get registered
|
|
ob_mouse->pointer = 0;
|
|
}
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnSetScrollRightMouse(int32 *params) {
|
|
// params: 0 pointer to object's mouse structure
|
|
|
|
Object_mouse *ob_mouse = (Object_mouse *) params[0];
|
|
|
|
// Highest priority
|
|
|
|
ob_mouse->x1 = this_screen.scroll_offset_x + g_display->_screenWide - SCROLL_MOUSE_WIDTH;
|
|
ob_mouse->y1 = 0;
|
|
ob_mouse->x2 = this_screen.screen_wide - 1;
|
|
ob_mouse->y2 = this_screen.screen_deep - 1;
|
|
ob_mouse->priority = 0;
|
|
|
|
if (this_screen.scroll_offset_x < this_screen.max_scroll_offset_x) {
|
|
// not fully scrolled to the right
|
|
ob_mouse->pointer = SCROLL_RIGHT_MOUSE_ID;
|
|
} else {
|
|
// so the mouse area doesn't get registered
|
|
ob_mouse->pointer = 0;
|
|
}
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnSetObjectHeld(int32 *params) {
|
|
// params: 0 luggage icon to set
|
|
|
|
Set_luggage(params[0]);
|
|
|
|
OBJECT_HELD = params[0];
|
|
current_luggage_resource = params[0];
|
|
|
|
// mode locked - no menu available
|
|
mouse_mode_locked = 1;
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
// called from speech scripts to remove the chooser bar when it's not
|
|
// appropriate to keep it displayed
|
|
|
|
int32 Logic::fnRemoveChooser(int32 *params) {
|
|
// params: none
|
|
|
|
g_display->hideMenu(RDMENU_BOTTOM);
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnCheckPlayerActivity(int32 *params) {
|
|
// Used to decide when to trigger music cues described as "no player
|
|
// activity for a while"
|
|
|
|
// params: 0 threshold delay in seconds, ie. what we want to
|
|
// check the actual delay against
|
|
|
|
uint32 threshold = params[0] * 12; // in game cycles
|
|
|
|
// if the actual delay is at or above the given threshold
|
|
if (player_activity_delay >= threshold) {
|
|
// reset activity delay counter, now that we've got a
|
|
// positive check
|
|
|
|
player_activity_delay = 0;
|
|
RESULT = 1;
|
|
} else
|
|
RESULT = 0;
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnResetPlayerActivityDelay(int32 *params) {
|
|
// Use if you want to deliberately reset the "no player activity"
|
|
// counter for any reason
|
|
|
|
// params: none
|
|
|
|
player_activity_delay = 0;
|
|
return IR_CONT;
|
|
}
|
|
|
|
void Monitor_player_activity(void) {
|
|
// if there is at least one mouse event outstanding
|
|
if (CheckForMouseEvents()) {
|
|
// reset activity delay counter
|
|
player_activity_delay = 0;
|
|
} else {
|
|
// no. of game cycles since mouse event queue last empty
|
|
player_activity_delay++;
|
|
}
|
|
}
|
|
|
|
} // End of namespace Sword2
|