scummvm/sword2/mouse.cpp

1463 lines
43 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 "driver/driver96.h"
#include "build_display.h"
#include "console.h"
#include "controls.h"
#include "debug.h"
#include "defs.h"
#include "events.h"
#include "icons.h"
#include "interpreter.h"
#include "layers.h"
#include "maketext.h"
#include "mouse.h" //assure integrety
#include "object.h"
#include "protocol.h"
#include "resman.h"
#include "sound.h"
#include "sword2.h" // for PauseGame() & UnpauseGame()
//------------------------------------------------------------------------------------
// 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];
uint32 mouse_touching=0; //set by Check_mouse_list
uint32 old_mouse_touching=0;
uint32 menu_selected_pos;
uint8 examining_menu_icon=0;
uint32 mouse_pointer_res=0; // if it's NORMAL_MOUSE_ID (ie. normal pointer) then it's over a floor area (or hidden hot-zone
uint32 mouse_mode=0; //0 normal in game
//1 top menu down (bottom!)
//2 dragging luggage
//3 system menu chooser (top)
//4 speech chooser
//Object_mouse old_mouse_object; //copy structure from list when moving onto a mouse area
uint32 menu_status; //0 available - 1 unavailable
uint32 mouse_status; //human 0 on/1 off
uint32 mouse_mode_locked=0; //0 not !0 mode cannot be changed from normal mouse to top menu (i.e. when carrying big objects)
uint32 current_luggage_resource=0;
uint32 subject_status; //0 off 1 on
uint32 old_button=0; //for the re-click stuff - must be same button you see
uint32 button_click=0;
uint32 pointer_text_bloc_no=0;
uint32 pointerTextSelected=0;
uint32 player_activity_delay=0; // player activity delay counter
uint32 real_luggage_item=0; //last minute for pause mode
//------------------------------------------------------------------------------------
/*
#define RD_LEFTBUTTONDOWN 0x01
#define RD_LEFTBUTTONUP 0x02
#define RD_RIGHTBUTTONDOWN 0x04
#define RD_RIGHTBUTTONUP 0x08
*/
//------------------------------------------------------------------------------------
// local function prototypes
//uint8 Check_sprite_pixel( uint32 j );
void CreatePointerText(uint32 TextId, uint32 pointerRes); // James16jun97
void Monitor_player_activity(void); // James23july97
void No_human(void);
//------------------------------------------------------------------------------------
void Reset_mouse_list(void) //Tony26Sept96
{
//call at beginning of gameloop
cur_mouse=1;
}
//------------------------------------------------------------------------------------
void Mouse_engine(void) //Tony30Sept96
{
Monitor_player_activity(); // James23july97
ClearPointerText(); // James16jun97
if (DEAD) //George is dead ;)
{
if (mouse_mode!=MOUSE_system_menu)
{
mouse_mode=MOUSE_system_menu;
if (mouse_touching)
{
// get off
old_mouse_touching=0; //we've moved off
mouse_touching=0; //we were on something but not anymore
}
Set_mouse(NORMAL_MOUSE_ID);
Build_system_menu(); //Tony19Mar97
}
System_menu();
return;
}
if (mouse_status) //no human
return;
switch(mouse_mode)
{
case MOUSE_normal: //0 normal in game
Normal_mouse();
break;
case MOUSE_top: //1 top menu down
Top_menu_mouse();
break;
case MOUSE_drag: //2 dragging luggage
Drag_mouse();
break;
case MOUSE_system_menu: //3 in game bottom menu - save/restore, etc?
System_menu();
break;
case MOUSE_holding: //4 wait for mouse to move off bottom menu - after speech
if (mousey<400)
{ mouse_mode=MOUSE_normal;
Zdebug(" releasing");
}
break;
default:
break;
}
}
//------------------------------------------------------------------------------------
void System_menu(void) //Tony19Mar97
{
uint32 safe_looping_music_id;
_mouseEvent *me;
int j,hit;
uint8 *icon;
uint32 rv; // for drivers return value
int32 pars[2];
uint32 icon_list[5] =
{
OPTIONS_ICON,
QUIT_ICON,
SAVE_ICON,
RESTORE_ICON,
RESTART_ICON
};
if ((mousey>0)&&(!DEAD)) //can't close when player is dead
{ mouse_mode=MOUSE_normal; //close menu
// start the menu coming down
HideMenu(RDMENU_TOP);
return;
}
me = MouseEvent(); //get mouse event
if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed
{
// clicked on a top mouse pointer?
if ((mousex>=24)&&(mousex<640-24)&&(mousey<0))
{
hit=(mousex-24)/40; //which are we over?
if ((hit==2)&&(DEAD))
return; //no save when dead
if (hit<5) //there are 5 system menus
{ for (j=0;j<5;j++) //build them all high in full colour - when on eis clicked on all the rest will grey out
if (j!=hit) //change all others to grey
{
icon = res_man.Res_open( icon_list[j] ) + sizeof(_standardHeader);
SetMenuIcon(RDMENU_TOP, j, icon);
res_man.Res_close( icon_list[j] );
}
//------------------------
rv = g_sound->PauseFx();
if (rv != RD_OK)
Zdebug("ERROR: PauseFx() returned %.8x in SystemMenu()", rv);
//------------------------
// 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;
FN_play_music(pars);
looping_music_id = safe_looping_music_id; // restore proper looping_music_id
//------------------------
//------------------------
// clear the screen & set up the new palette for the menus
EraseBackBuffer();
ProcessMenu(); // drivers to redraw menu over this blank screen!
ResetRenderEngine();
//------------------------
switch(hit) //call the relevent screen
{
case 0: // options
Option_control(); // game options
break;
case 1: // quit
Quit_control(); // quit to windows
break;
case 2: // save
Save_control(); // save the game
break;
case 3: // restore
Restore_control(); // restore a game
break;
case 4: // restart
Restart_control(); // restart the game
break;
}
//------------------------
// start the menu coming down
if (!DEAD) //not death screen
{ mouse_mode=MOUSE_normal; //close menu
HideMenu(RDMENU_TOP); //but not when dead
}
else
{ Set_mouse(NORMAL_MOUSE_ID);
Build_system_menu(); //reset top menu
}
//------------------------
// clear the screen & restore the location palette
EraseBackBuffer();
ProcessMenu(); // drivers to redraw menu over this blank screen!
//------------------------
// reset game palette, but not after a successful restore or restart!
if (this_screen.new_palette != 99) // see RestoreFromBuffer() in save_rest.cpp
{
SetFullPalette(0); // '0' means put back game screen palette; see Build_display.cpp (James17jun97)
this_screen.new_palette=0; //stop the engine fading in the restored screens palette
}
else
this_screen.new_palette=1;
//------------------------
rv = g_sound->UnpauseFx();
if (rv != RD_OK)
Zdebug("ERROR: UnpauseFx() returned %.8x in SystemMenu()", rv);
//------------------------
// 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;
FN_play_music(pars);
// cross-fades into the required music:
// - either a restored game tune
// - or music playing prior to entering control panels
}
else
FN_stop_music(NULL); // stop the control panel music
//------------------------
}
}
}
}
//------------------------------------------------------------------------------------
void Drag_mouse(void) //Tony21Nov96
{
_mouseEvent *me;
uint32 pos;
// uint32 null_pc=1; //script 1 is combine script
if ((mousey<400)&&(!menu_status))
{ mouse_mode=MOUSE_normal; //close menu
// start the menu coming down
HideMenu(RDMENU_BOTTOM);
return;
}
Mouse_on_off(); //handles cursors and the luggage on/off according to type
// now do the normal click stuff
me = MouseEvent(); //get mouse event
// we only care about left clicks when the mouse is over an object
// we ignore mouse releases
if ((me!=NULL)&&(me->buttons&RD_LEFTBUTTONDOWN)) //there's a mouse event to be processed
{
// could be clicking on an on screen object or on the top 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
if (me->buttons&RD_LEFTBUTTONDOWN) // set global script variable 'button'
{
LEFT_BUTTON = 1;
RIGHT_BUTTON = 0;
}
else
{
LEFT_BUTTON = 0;
RIGHT_BUTTON = 1;
}
MOUSE_X=(uint32)mousex+this_screen.scroll_offset_x;
MOUSE_Y=(uint32)mousey+this_screen.scroll_offset_y; //these might be required by the action script about to be run
CLICKED_ID = mouse_touching; // for scripts to know what's been clicked (21jan97). First used for 'room_13_turning_script' in object 'biscuits_13'
Set_player_action_event(CUR_PLAYER_ID, mouse_touching); //Tony4Dec96
//--------------------------------------
// Write to walkthrough file (zebug0.txt)
#ifdef _SWORD2_DEBUG
Zdebug(0,"USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID));
#endif
//--------------------------------------
HideMenu(RDMENU_BOTTOM); //hide menu too
mouse_mode=MOUSE_normal; //back to normal menu mode
}
else //better check for combine/cancel cancel puts us back in Top_menu_mouse mode
{
if ((mousex>=24)&&(mousex<640-24))
{
pos=(mousex-24)/40; //which are we over?
if (master_menu_list[pos].icon_resource) //clicked on something - what button?
{
mouse_mode=MOUSE_top; //always back into top menu mode
Set_luggage(0); // remove luggage
if (pos==menu_selected_pos) // if we've clicked on the same icon as the one we're dragging
{
OBJECT_HELD=0; // reset first icon
menu_selected_pos=0;
}
else // combine the 2 icons
{
// Zdebug("combine");
COMBINE_BASE=master_menu_list[pos].icon_resource; //what we clicked on, not what we're dragging
Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT); //Tony4Dec96
No_human(); // turn off mouse now, to prevent player trying to click elsewhere BUT leave the bottom menu open
//--------------------------------------
// Write to walkthrough file (zebug0.txt)
#ifdef _SWORD2_DEBUG
Zdebug(0,"USED \"%s\" ICON ON \"%s\" ICON", FetchObjectName(OBJECT_HELD), FetchObjectName(COMBINE_BASE));
#endif
//--------------------------------------
}
Build_top_menu(); // refresh the menu
// Zdebug("switch to top mode");
}
}
}
}
}
//------------------------------------------------------------------------------------
void Top_menu_mouse(void) //Tony3Oct96
{
//top menu is down
_mouseEvent *me;
uint32 pos;
if ((mousey<400)&&(!menu_status))
{ mouse_mode=MOUSE_normal; //close menu
// start the menu coming down
HideMenu(RDMENU_BOTTOM);
return;
}
me=MouseEvent(); //get mouse event
// we only care about left clicks when the mouse is over an object
// we ignore mouse releases
if (me!=NULL) //there's a mouse event to be processed
{
// now check if we've clicked on an actual icon
if ((mousex>=24)&&(mousex<640-24))
{
pos=(mousex-24)/40; //which are we over?
if (master_menu_list[pos].icon_resource) //clicked on something - what button?
{
if (me->buttons&RD_RIGHTBUTTONDOWN) //right button look
{
examining_menu_icon=1;
OBJECT_HELD = master_menu_list[pos].icon_resource; //id the object via its graphic
EXIT_CLICK_ID=0; // (JEL09oct97) must clear this so next click on exit becomes 1st click again
//--------------------------------------
// Write to walkthrough file (zebug0.txt)
#ifdef _SWORD2_DEBUG
Zdebug(0,"RIGHT-CLICKED ON \"%s\" ICON", FetchObjectName(OBJECT_HELD));
#endif
//--------------------------------------
Set_player_action_event(CUR_PLAYER_ID, MENU_MASTER_OBJECT); //Tony4Dec96
Build_top_menu(); // refresh the menu
No_human(); // turn off mouse now, to prevent player trying to click elsewhere BUT leave the bottom menu open
}
else if (me->buttons&RD_LEFTBUTTONDOWN) //left button - highlight the object and bung us into drag luggage mode
{
menu_selected_pos=pos; //menu slot we clicked on - derive luggage resource from this in mouse_on_off()
current_luggage_resource=master_menu_list[pos].luggage_resource;
mouse_mode=MOUSE_drag;
// Zdebug("setting OH in top menu");
OBJECT_HELD = master_menu_list[pos].icon_resource; //id the object via its graphic
EXIT_CLICK_ID=0; // (JEL09oct97) must clear this so next click on exit becomes 1st click again
Build_top_menu(); // refresh the menu
Set_luggage(master_menu_list[pos].luggage_resource);
// Zdebug("switch to drag mode");
}
}
}
}
}
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
void Normal_mouse(void) //Tony30Sept96
{
//the gane is playing and none of the menus are activated - but, we need to check if the top menu is to start
//note, wont have luggage
_mouseEvent *me;
if ((mousey<0)&&(!menu_status)&&(!mouse_mode_locked)&&(!OBJECT_HELD)) //no save in big-object menu lock situation
{
mouse_mode=MOUSE_system_menu;
if (mouse_touching)
{
// get off
old_mouse_touching=0; //we've moved off
mouse_touching=0; //we were on something but not anymore
}
// reset mouse cursor - in case we're between mice
Set_mouse(NORMAL_MOUSE_ID);
Build_system_menu(); //Tony19Mar97
}
if ((mousey>399)&&(!menu_status)&&(!mouse_mode_locked))
{
if (!OBJECT_HELD) //why are we testing for this?
{
mouse_mode=MOUSE_top; //bring down top menu
}
else
{
mouse_mode=MOUSE_drag;
}
// if mouse is moving off an object and onto the top menu then do a standard get-off
if (mouse_touching)
{
// get off
old_mouse_touching=0; //we've moved off
mouse_touching=0; //we were on something but not anymore
}
// reset mouse cursor
Set_mouse(NORMAL_MOUSE_ID);
// build menu and start the menu coming down
Build_top_menu();
return;
}
//check also for bringing the bottom menu up
Mouse_on_off(); //handles
// now do the normal click stuff
me = MouseEvent(); //get mouse event
//-----------------------------------------------------
#ifdef _SWORD2_DEBUG
if (definingRectangles)
{
if (draggingRectangle==0) // not yet dragging a rectangle, so need click to start
{
if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) )
{
rect_x1 = rect_x2 = (uint32)mousex+this_screen.scroll_offset_x; // set both (x1,y1)
rect_y1 = rect_y2 = (uint32)mousey+this_screen.scroll_offset_y; // & (x2,y2) to this point
draggingRectangle=1;
}
}
else if (draggingRectangle==1) // currently dragging a rectangle
{
if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) ) // click means reset
{
draggingRectangle=2; // lock rectangle, so you can let go of mouse to type in the coords
}
else // drag rectangle
{
rect_x2 = (uint32)mousex+this_screen.scroll_offset_x;
rect_y2 = (uint32)mousey+this_screen.scroll_offset_y;
}
}
else // currently locked to avoid knocking out of place while reading off the coords
{
if ( (me!=NULL) && ((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&RD_RIGHTBUTTONDOWN)) ) // click means reset
{
draggingRectangle=0; // back to start again
}
}
}
else
#endif // _SWORD2_DEBUG
//-----------------------------------------------------
{
// we only care about down clicks when the mouse is over an object
// we ignore mouse releases
if ((me!=NULL)&&((me->buttons&RD_LEFTBUTTONDOWN)||(me->buttons&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 = mouse_touching; //PLAYER_ACTION script variable - whatever catches this must reset to 0 again
//idle or router-anim will catch it
if (me->buttons&RD_LEFTBUTTONDOWN) // set global script variable 'button'
{
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
}
MOUSE_X=(uint32)mousex+this_screen.scroll_offset_x;
MOUSE_Y=(uint32)mousey+this_screen.scroll_offset_y; //these might be required by the action script about to be run
if ((mouse_touching==EXIT_CLICK_ID)&&(me->buttons&RD_LEFTBUTTONDOWN)) //only left button
{
// its the exit double click situation
// let the existing interaction continue and start fading down - switch the human off too
FN_no_human(NULL);
FN_fade_down(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
CLICKED_ID = mouse_touching; // for scripts to know what's been clicked (21jan97). First used for 'room_13_turning_script' in object 'biscuits_13'
EXIT_CLICK_ID=0; //must clear these two double-click control flags - do it here so reclicks after exit clicks are cleared up
EXIT_FADING=0;
Set_player_action_event(CUR_PLAYER_ID, mouse_touching); //Tony4Dec96
//--------------------------------------
// Write to walkthrough file (zebug0.txt)
#ifdef _SWORD2_DEBUG
if (OBJECT_HELD)
Zdebug(0,"USED \"%s\" ICON ON %s", FetchObjectName(OBJECT_HELD), FetchObjectName(CLICKED_ID));
else if (LEFT_BUTTON)
Zdebug(0,"LEFT-CLICKED ON %s", FetchObjectName(CLICKED_ID));
else // RIGHT BUTTON
Zdebug(0,"RIGHT-CLICKED ON %s", FetchObjectName(CLICKED_ID));
#endif
//--------------------------------------
}
}
}
}
//------------------------------------------------------------------------------------
void Mouse_on_off(void) //Tony30Sept96
{
//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;
if ((mousey<0)||(mousey>399)) // don't detect objects that are hidden behind the menu bars (ie. in the scrolled-off areas of the screen)
{
pointer_type=0;
mouse_touching=0;
}
else
pointer_type = Check_mouse_list(); // set 'mouse_touching' & return pointer_type
if ((!mouse_flicked_off)&&(old_mouse_touching==mouse_touching)) //same as previous cycle?
return; //yes, so nothing to do BUT CARRY ON IF MOUSE WAS FLICKED OFF!
mouse_flicked_off=0; // can reset this now
if ((!old_mouse_touching)&&(mouse_touching)) //the cursor has moved onto something
{
// 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
// memcpy( &old_mouse_object, &mouse_list[mouse_touching], sizeof(Object_mouse));
old_mouse_touching=mouse_touching; //
// run get on
if (pointer_type)
{
Set_mouse(pointer_type); // 'pointer_type' holds the resource id of the pointer anim
if (OBJECT_HELD) // setup luggage icon
// Set_luggage(master_menu_list[menu_selected_pos].luggage_resource);
Set_luggage(current_luggage_resource);
}
else
Con_fatal_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
{
old_mouse_touching=0; // we've moved off
Set_mouse(NORMAL_MOUSE_ID); // reset cursor to normal pointer
// 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
mouse_touching=0; // ignore the new id this cycle - should hit next cycle
old_mouse_touching=0; // we've moved off
Set_mouse(0); // blank cursor
mouse_flicked_off=1; // 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)
// reset luggage only when necessary
}
else // for when mouse was flicked off for one cycle, but then moved onot nothing before 2nd hot-spot registered
{
// both 'old_mouse_touching' & 'mouse_touching' will be zero
Set_mouse(NORMAL_MOUSE_ID); // reset cursor to normal pointer
}
// possible check for edge of screen more-to-scroll here on large screens
}
//------------------------------------------------------------------------------------
void Set_mouse(uint32 res) // (4dec96 JEL)
{
uint8 *icon;
uint32 len;
mouse_pointer_res=res; //high level - whats the mouse - for the engine
if (res) // if it's not NULL
{
icon = res_man.Res_open( res ) + sizeof(_standardHeader);
len = res_man.resList[res]->size - sizeof(_standardHeader);
if (res == NORMAL_MOUSE_ID) // don't pulse the normal pointer
SetMouseAnim(icon, len, RDMOUSE_NOFLASH); // 0 means don't pulse this pointer, just do the regular anim loop
else
SetMouseAnim(icon, len, RDMOUSE_FLASH); // 1 mean pulse before starting regular anim loop
res_man.Res_close( res );
}
else
SetMouseAnim(NULL,0,0); // blank cursor
}
//------------------------------------------------------------------------------------
void Set_luggage(uint32 res) //Tony26Nov96
{
uint8 *icon;
uint32 len;
if (res) // if not NULL
{
real_luggage_item=res;
icon = res_man.Res_open( res ) + sizeof(_standardHeader);
len = res_man.resList[res]->size - sizeof(_standardHeader);
SetLuggageAnim(icon, len);
res_man.Res_close( res );
}
else
{ real_luggage_item=0;
SetLuggageAnim(NULL, 0);
}
}
//------------------------------------------------------------------------------------
uint32 Check_mouse_list(void) //Tony30Sept96
{
int32 priority=0;
uint32 j=1;
if (cur_mouse>1)
{
while(priority<10) //number of priorities subject to implementation needs
{
if ((mouse_list[j].priority==priority) && // if the mouse pointer is over this mouse-detection-box
(mousex+this_screen.scroll_offset_x >= mouse_list[j].x1) &&
(mousex+this_screen.scroll_offset_x <= mouse_list[j].x2) &&
(mousey+this_screen.scroll_offset_y >= mouse_list[j].y1) &&
(mousey+this_screen.scroll_offset_y <= mouse_list[j].y2))
{
/*
if (mouse_list[j].anim_resource) // want to use sprite as a mouse mask, for better accuracy of detection (25oct96 JEL)
{
// only works for uncompressed sprite data!!
// THIS IS NEVER USED IN SWORD2
if (Check_sprite_pixel(j)) // if the mouse is touching a non-zero pixel of the sprite
{
mouse_touching=mouse_list[j].id; // record id
CreatePointerText(mouse_list[j].pointer_text); // James16jun97
return(mouse_list[j].pointer); //return pointer type
}
}
else // ok, we're touching the detection-box
*/
{
mouse_touching=mouse_list[j].id; // record 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); // James16jun97
return(mouse_list[j].pointer); //return pointer type
}
}
j++; //next
if (j==cur_mouse)
{
j=0;
priority++; //next priority - 0 being the highest, 9 the lowest
}
}
}
mouse_touching=0; // touching nothing
return(0); // no pointer to return
}
//------------------------------------------------------------------------------------
void CreatePointerText(uint32 textId, uint32 pointerRes) // James16jun97
{
uint32 local_text;
uint32 text_res;
uint8 *text;
int16 xOffset, yOffset; // offsets for pointer text sprite from pointer position
uint8 justification;
#define POINTER_TEXT_WIDTH 640 // just in case!
#define POINTER_TEXT_PEN 184 // white
if (pointerTextSelected)
{
if (textId)
{
//-------------------------------------------
// check what the pointer is, to set offsets correctly for text position
switch(pointerRes)
{
case CROSHAIR:
yOffset = -7; // above (above & to the right of the pointer coordinate)
xOffset = +10; // right
break;
case EXIT0:
yOffset = +15; // below
xOffset = +20; // right
break;
case EXIT1:
yOffset = +16; // below
xOffset = -10; // left
break;
case EXIT2:
yOffset = +10; // below
xOffset = -22; // left
break;
case EXIT3:
yOffset = -16; // above
xOffset = -10; // left
break;
case EXIT4:
yOffset = -15; // above
xOffset = +15; // right
break;
case EXIT5:
yOffset = -12; // above
xOffset = +10; // right
break;
case EXIT6:
yOffset = +10; // below
xOffset = +25; // right
break;
case EXIT7:
yOffset = +16; // below
xOffset = +20; // right
break;
case EXITDOWN:
yOffset = -20; // above
xOffset = -10; // left
break;
case EXITUP:
yOffset = +20; // below
xOffset = +20; // right
break;
case MOUTH:
yOffset = -10; // above
xOffset = +15; // right
break;
case NORMAL:
yOffset = -10; // above
xOffset = +15; // right
break;
case PICKUP:
yOffset = -40; // above
xOffset = +10; // right
break;
case SCROLL_L:
yOffset = -20; // above
xOffset = +20; // right
break;
case SCROLL_R:
yOffset = -20; // above
xOffset = -20; // left
break;
case USE:
yOffset = -8; // above
xOffset = +20; // right
break;
default: // shouldn't happen if we cover all the different mouse pointers above
yOffset = -10; // above
xOffset = +10; // right
}
//-------------------------------------------
// set up justification for text sprite,
// based on it's offsets from the pointer position
// from maketext.h
//#define NO_JUSTIFICATION 0 // only for debug text, since it doesn't keep text inside the screen margin!
//#define POSITION_AT_CENTRE_OF_BASE 1 // these all force text inside the screen edge margin when necessary
//#define POSITION_AT_CENTRE_OF_TOP 2
//#define POSITION_AT_LEFT_OF_TOP 3
//#define POSITION_AT_RIGHT_OF_TOP 4
//#define POSITION_AT_LEFT_OF_BASE 5
//#define POSITION_AT_RIGHT_OF_BASE 6
//#define POSITION_AT_LEFT_OF_CENTRE 7
//#define POSITION_AT_RIGHT_OF_CENTRE 8
//-----------------------------------------
if (yOffset < 0) // if 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; // text sprite is justified from it's bottom-left corner
}
else // (xOffset==0) // above centre
{
justification = POSITION_AT_CENTRE_OF_BASE;
}
}
//-----------------------------------------
else if (yOffset > 0) // if 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 // (xOffset==0) // below centre
{
justification = POSITION_AT_CENTRE_OF_TOP;
}
}
//-----------------------------------------
else // if at 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 // (xOffset==0) // centre centre
{
justification = POSITION_AT_LEFT_OF_CENTRE; // shouldn't happen anyway!
}
}
//-------------------------------------------
text_res = textId/SIZE; // text resource number
local_text = textId&0xffff; // text line number within the resource
text = FetchTextLine( res_man.Res_open(text_res), local_text ); // open text file & get the line
// 'text+2' to skip the first 2 bytes which form the line reference number
pointer_text_bloc_no = Build_new_block(text+2, mousex+xOffset, mousey+yOffset, POINTER_TEXT_WIDTH, POINTER_TEXT_PEN, RDSPR_TRANS+RDSPR_DISPLAYALIGN, speech_font_id, justification);
res_man.Res_close(text_res); // now ok to close the text file
}
}
}
//------------------------------------------------------------------------------------
void ClearPointerText(void) // James16jun97
{
if (pointer_text_bloc_no)
{
Kill_text_bloc(pointer_text_bloc_no);
pointer_text_bloc_no=0;
}
}
//------------------------------------------------------------------------------------
/* NOT USED IN SWORD2
uint8 Check_sprite_pixel( uint32 j ) // (25oct96 JEL)
{
// only works for uncompressed sprite data!!
uint8 *file;
uint8 hit;
_frameHeader *frame_head;
int16 sprite_x, sprite_y;
int16 in_sprite_x, in_sprite_y;
uint8 *sprite_data;
sprite_x = mouse_list[j].x1; // sprite coords have been copied to mouse area coords by FN_register_X_frame (X = sort, back or fore)
sprite_y = mouse_list[j].y1; // so easier to get them from there than from the anim file again
file = res_man.Res_open(mouse_list[j].anim_resource); // open the anim file & point to start of it
frame_head = FetchFrameHeader(file,mouse_list[j].anim_pc); // point to frame header of current frame
sprite_data = (uint8 *)(frame_head + 1); // point to start of frame data
in_sprite_x = mousex + this_screen.scroll_offset_x - sprite_x; // x-coord of mouse from origin at top-left of sprite
in_sprite_y = mousey + this_screen.scroll_offset_y - sprite_y; // y-coord of mouse from origin at top-left of sprite
hit = sprite_data[in_sprite_y * frame_head->width + in_sprite_x]; // hit = value of pixel to which the mouse is pointing
res_man.Res_close(mouse_list[j].anim_resource); // close anim file
return (hit); // we are touching the sprite if 'hit' is non-zero
}
*/
//------------------------------------------------------------------------------------
int32 FN_no_human(int32 *params) //Tony30Sept96
{
//param none
MOUSE_AVAILABLE = 0; // for logic scripts (James21may97)
ClearPointerText();
mouse_status=1; //human/mouse off
Set_mouse(0); // blank cursor
Set_luggage(0); // blank cursor
//must be normal mouse situation or a largely neutral situation - special menus use No_human
if (TALK_FLAG==0) //dont hide menu in conversations
HideMenu(RDMENU_BOTTOM);
if (mouse_mode==MOUSE_system_menu)
{
mouse_mode=MOUSE_normal; //close menu
HideMenu(RDMENU_TOP); // start the menu coming down
}
if (params);
return(1); //script continue
}
//------------------------------------------------------------------------------------
void No_human(void) //Tony4June97
{
//leaves the menus open
//used by the system when clicking right on a menu item to examine it and
//when combining objects
MOUSE_AVAILABLE = 0; // for logic scripts (James21may97)
mouse_status=1; //human/mouse off
Set_mouse(0); // blank cursor
Set_luggage(0); // blank cursor
}
//------------------------------------------------------------------------------------
int32 FN_add_human(int32 *params) //Tony30Sept96
{
//param none
#ifdef _SWORD2_DEBUG
uint8 black[4] = {0,0,0,0};
uint8 white[4] = {255,255,255,0};
#endif // ('0' means don't print to console, but console isn't up anyway)
MOUSE_AVAILABLE = 1; // for logic scripts (James21may97)
if (mouse_status) //off
{
mouse_status=0; //on
mouse_touching=1; //forces engine to choose a cursor
}
CLICKED_ID=0; //clear this to reset no-second-click system
// this is now done outside the OBJECT_HELD check in case it's set to zero before now! (James 10july97)
mouse_mode_locked=0; //unlock the mouse from possible large object lock situtations - see syphon in rm 3
if (OBJECT_HELD) //was dragging something around
{
OBJECT_HELD=0; // need to clear this again
examining_menu_icon=0; // and these may also need clearing
COMBINE_BASE=0; // - just in case
Set_luggage(0); // blank cursor
}
if ((mousey>399)&&(mouse_mode!=MOUSE_holding)) // if mouse is over menu area
{
mouse_mode=MOUSE_normal; // VITAL - reset things & rebuild the menu
Set_mouse(NORMAL_MOUSE_ID);
}
else if (mousey>399)
Set_mouse(NORMAL_MOUSE_ID);
//----------------------------------------------------------------------------------------------
// enabled/disabled from console; status printed with on-screen debug info
#ifdef _SWORD2_DEBUG
if (testingSnR) // testing logic scripts by simulating an instant Save & Restore
{
BS2_SetPalette(0, 1, white, RDPAL_INSTANT);
Clear_fx_queue(); // stops all fx & clears the queue - eg. when leaving a location
res_man.Kill_all_objects(0); // ie. trashing all object resources so they load in fresh & restart their logic scripts
BS2_SetPalette(0, 1, black, RDPAL_INSTANT);
}
#endif // ('0' means don't print to console, but console isn't up anyway)
//----------------------------------------------------------------------------------------------
if (params);
return(1); //script continue
}
//------------------------------------------------------------------------------------
int32 FN_register_mouse(int32 *params) //Tony29Oct96
{
//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
//param 0 pointer to Object_mouse or 0 for no write to mouse list
// Zdebug(1,"cur_mouse = %d", cur_mouse);
Object_mouse *ob_mouse;
ob_mouse = (Object_mouse *) params[0]; // param 1 is pointer to mouse structure
if (ob_mouse->pointer) // only if 'pointer' isn't NULL
{
#ifdef _SWORD2_DEBUG
if (cur_mouse==TOTAL_mouse_list)
Con_fatal_error("ERROR: mouse_list full [%s line %u]",__FILE__,__LINE__);
#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 ((mouse_list[cur_mouse].pointer_text) && (mouse_list[cur_mouse].id != (int32)ID)) // if 'pointer_text' field is set, but the 'id' field isn't same is current id
mouse_list[cur_mouse].pointer_text=0; // then we don't want this "left over" pointer text
//-----------------------------------------------
mouse_list[cur_mouse].id = ID; // get id from system variable 'id' which is correct for current object
mouse_list[cur_mouse].anim_resource = 0; // not using sprite as mask - this is only done from FN_register_frame()
mouse_list[cur_mouse].anim_pc = 0;
//Zdebug("mouse id %d", mouse_list[cur_mouse].id);
cur_mouse++;
}
return(IR_CONT); // continue script
}
//------------------------------------------------------------------------------------
// use this in the object's service script prior to registering the mouse area
// ie. before FN_register_mouse or FN_register_frame
// - best if kept at very top of service script
int32 FN_register_pointer_text(int32 *params) // James16jun97
{
// param 0 local id of text line to use as pointer text
#ifdef _SWORD2_DEBUG
if (cur_mouse==TOTAL_mouse_list)
Con_fatal_error("ERROR: mouse_list full [%s line %u]",__FILE__,__LINE__);
#endif
mouse_list[cur_mouse].id = ID; // current object id - used for checking pointer_text when mouse area registered (in FN_register_mouse & FN_register_frame)
mouse_list[cur_mouse].pointer_text = params[0];
return(IR_CONT); // continue script
}
//------------------------------------------------------------------------------------
int32 FN_blank_mouse(int32 *params) //Tony29Oct96
{
//set mouse to normal pointer - used in speech
//no params
Set_mouse(0);
if (params);
return(1); //cont
}
//------------------------------------------------------------------------------------
int32 FN_init_floor_mouse(int32 *params) // James29nov96
{
// params 0 pointer to object's mouse structure
Object_mouse *ob_mouse = (Object_mouse *) params[0];
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; // floor is always lowest priority
ob_mouse->pointer = NORMAL_MOUSE_ID; // normal pointer
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
#define SCROLL_MOUSE_WIDTH 20 // James13feb97 (updated by James 25mar97)
//---------------------------------------------------------------------------------------------------------------------
int32 FN_set_scroll_left_mouse(int32 *params) // James13feb97
{
// params 0 pointer to object's mouse structure
Object_mouse *ob_mouse = (Object_mouse *) params[0];
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; // highest priority
if (this_screen.scroll_offset_x > 0) // if not fully scrolled to the left
ob_mouse->pointer = SCROLL_LEFT_MOUSE_ID;
else
ob_mouse->pointer = 0; // so the mouse area doesn't get registered
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_set_scroll_right_mouse(int32 *params) // James13feb97
{
// params 0 pointer to object's mouse structure
Object_mouse *ob_mouse = (Object_mouse *) params[0];
ob_mouse->x1 = this_screen.scroll_offset_x + 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; // highest priority
if (this_screen.scroll_offset_x < this_screen.max_scroll_offset_x) // if not fully scrolled to the right
ob_mouse->pointer = SCROLL_RIGHT_MOUSE_ID;
else
ob_mouse->pointer = 0; // so the mouse area doesn't get registered
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_set_object_held(int32 *params) //tony19May97
{
//params 0 luggage icon to set
Set_luggage(params[0]);
OBJECT_HELD=params[0];
current_luggage_resource=params[0];
mouse_mode_locked=1; //mode locked - no top menu available
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
// called from speech scripts to remove the chooser bar when it's not appropriate to keep it displayed
int32 FN_remove_chooser(int32 *params) // James13aug97
{
HideMenu(RDMENU_BOTTOM);
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_disable_menu(int32 *params) //Tony1Oct96
{
mouse_mode_locked=1; //mode locked - no top menu available
mouse_mode=MOUSE_normal;
HideMenu(RDMENU_TOP);
HideMenu(RDMENU_BOTTOM);
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_enable_menu(int32 *params) //tony4June97
{
mouse_mode_locked=0; //mode locked - no top menu available
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_check_player_activity(int32 *params) // James23july97
{
// 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 (player_activity_delay >= threshold) // if the actual delay is at or above the given threshold
{
player_activity_delay=0; // reset activity delay counter, now that we've got a positive check
RESULT=1;
}
else
RESULT=0;
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
int32 FN_reset_player_activity_delay(int32 *params) // James23july97
{
// Use if you want to deliberately reset the "no player activity" counter for any reason
// no params
player_activity_delay=0; // reset activity delay counter
return(IR_CONT); // continue script
}
//---------------------------------------------------------------------------------------------------------------------
void Monitor_player_activity(void) // James23july97
{
if (CheckForMouseEvents()) // if there is at least one mouse event outstanding
player_activity_delay=0; // reset activity delay counter
else
player_activity_delay++; // no. of game cycles since mouse event queue last empty
}
//---------------------------------------------------------------------------------------------------------------------