scummvm/engines/sci/engine/kgraphics.cpp

1903 lines
53 KiB
C++
Raw Normal View History

2009-02-17 15:02:16 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/system.h"
#include "common/events.h"
#include "graphics/cursorman.h"
#include "graphics/video/avi_player.h"
#include "graphics/surface.h"
#include "sci/sci.h"
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/resource.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/gfx/gfx_gui.h"
#include "sci/gfx/gfx_widgets.h"
#include "sci/gfx/gfx_state_internal.h" // required for GfxContainer, GfxPort, GfxVisual
#include "sci/gfx/seq_decoder.h"
#include "sci/gui/gui.h"
#include "sci/gui/gui_cursor.h"
namespace Sci {
#undef DEBUG_LSRECT
2009-02-26 02:21:55 +00:00
// This is the real width of a text with a specified width of 0
#define MAX_TEXT_WIDTH_MAGIC_VALUE 192
2009-02-19 20:50:55 +00:00
// Graph subfunctions
enum {
K_GRAPH_GET_COLORS_NR = 2,
K_GRAPH_DRAW_LINE = 4,
K_GRAPH_SAVE_BOX = 7,
K_GRAPH_RESTORE_BOX = 8,
K_GRAPH_FILL_BOX_BACKGROUND = 9,
K_GRAPH_FILL_BOX_FOREGROUND = 10,
K_GRAPH_FILL_BOX_ANY = 11,
K_GRAPH_UPDATE_BOX = 12,
K_GRAPH_REDRAW_BOX = 13,
K_GRAPH_ADJUST_PRIORITY = 14
};
2009-02-19 20:50:55 +00:00
// Control types and flags
enum {
K_CONTROL_BUTTON = 1,
K_CONTROL_TEXT = 2,
K_CONTROL_EDIT = 3,
K_CONTROL_ICON = 4,
K_CONTROL_CONTROL = 6,
K_CONTROL_CONTROL_ALIAS = 7,
K_CONTROL_BOX = 10
};
#define ADD_TO_CURRENT_PORT(widget) \
2009-02-22 13:11:43 +00:00
{if (s->port) \
s->port->add((GfxContainer *)s->port, widget); \
2009-02-22 13:11:43 +00:00
else \
s->picture_port->add((GfxContainer *)s->visual, widget);}
#define ADD_TO_CURRENT_PICTURE_PORT(widget) \
2009-02-22 13:11:43 +00:00
{if (s->port) \
s->port->add((GfxContainer *)s->port, widget); \
2009-02-22 13:11:43 +00:00
else \
s->picture_port->add((GfxContainer *)s->picture_port, widget);}
#define ADD_TO_WINDOW_PORT(widget) \
s->wm_port->add((GfxContainer *)s->wm_port, widget);
#define FULL_REDRAW()\
2009-02-22 13:11:43 +00:00
if (s->visual) \
s->visual->draw(gfxw_point_zero); \
2009-02-22 13:11:43 +00:00
gfxop_update(s->gfx_state);
#if 0
// Used for debugging
#define FULL_INSPECTION()\
2009-02-22 13:11:43 +00:00
if (s->visual) \
s->visual->print(s->visual, 0);
#endif
2009-02-19 20:50:55 +00:00
static inline int sign_extend_byte(int value) {
if (value & 0x80)
return value - 256;
else
return value;
}
// was static
void assert_primary_widget_lists(EngineState *s) {
if (!s->dyn_views) {
rect_t bounds = s->picture_port->_bounds;
s->dyn_views = gfxw_new_list(bounds, GFXW_LIST_SORTED);
s->dyn_views->_flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
ADD_TO_CURRENT_PICTURE_PORT(s->dyn_views);
}
if (!s->drop_views) {
rect_t bounds = s->picture_port->_bounds;
s->drop_views = gfxw_new_list(bounds, GFXW_LIST_SORTED);
s->drop_views->_flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
ADD_TO_CURRENT_PICTURE_PORT(s->drop_views);
}
}
// static
void reparentize_primary_widget_lists(EngineState *s, GfxPort *newport) {
if (!newport)
newport = s->picture_port;
if (s->dyn_views) {
gfxw_remove_widget_from_container(s->dyn_views->_parent, s->dyn_views);
newport->add((GfxContainer *)newport, s->dyn_views);
}
}
int _find_view_priority(EngineState *s, int y) {
2009-02-19 20:50:55 +00:00
/*if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1)
++y; */
2009-02-19 20:50:55 +00:00
if (s->pic_priority_table) { // SCI01 priority table set?
int j;
for (j = 0; j < 15; j++)
if (y < s->pic_priority_table[j+1])
return j;
2009-02-19 20:50:55 +00:00
return 14; // Maximum
} else {
if (!s->_kernel->usesOldGfxFunctions())
return SCI0_VIEW_PRIORITY_14_ZONES(y);
else
2009-02-19 20:50:55 +00:00
return SCI0_VIEW_PRIORITY(y) == 15 ? 14 : SCI0_VIEW_PRIORITY(y);
}
}
int _find_priority_band(EngineState *s, int nr) {
if (!s->_kernel->usesOldGfxFunctions() && (nr < 0 || nr > 14)) {
if (nr == 15)
return 0xffff;
else {
warning("Attempt to get priority band %d", nr);
}
return 0;
}
if (s->_kernel->usesOldGfxFunctions() && (nr < 0 || nr > 15)) {
warning("Attempt to get priority band %d", nr);
return 0;
}
2009-02-19 20:50:55 +00:00
if (s->pic_priority_table) // SCI01 priority table set?
return s->pic_priority_table[nr];
else {
int retval;
if (!s->_kernel->usesOldGfxFunctions())
retval = SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr);
else
retval = SCI0_PRIORITY_BAND_FIRST(nr);
2009-02-19 20:50:55 +00:00
/* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1)
--retval; */
return retval;
}
}
reg_t graph_save_box(EngineState *s, rect_t area) {
reg_t handle = kalloc(s->_segMan, "graph_save_box()", sizeof(gfxw_snapshot_t *));
gfxw_snapshot_t **ptr = (gfxw_snapshot_t **)kmem(s->_segMan, handle);
// FIXME: gfxw_make_snapshot returns a pointer. Now why do we store a
// pointer to real memory inside the SCI heap?
// If we save and the load again, this cannot work in general.
// This seems like bad design. Either the snapshot data itself should be
// stored in the heap, or a unique persistent id.
*ptr = gfxw_make_snapshot(s->visual, area);
return handle;
}
void graph_restore_box(EngineState *s, reg_t handle) {
gfxw_snapshot_t **ptr;
int port_nr = s->port->_ID;
if (!handle.segment) {
warning("Attempt to restore box with zero handle");
return;
}
ptr = (gfxw_snapshot_t **)kmem(s->_segMan, handle);
if (!ptr) {
warning("Attempt to restore invalid handle %04x:%04x", PRINT_REG(handle));
return;
}
while (port_nr > 2 && !(s->port->_flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS) && (gfxw_widget_matches_snapshot(*ptr, s->port))) {
2009-02-19 20:50:55 +00:00
// This shouldn't ever happen, actually, since windows (ports w/ ID > 2) should all be immune
GfxPort *newport = s->visual->getPort(port_nr);
error("Port %d is not immune against snapshots", s->port->_ID);
port_nr--;
if (newport)
s->port = newport;
}
if (s->dyn_views && gfxw_widget_matches_snapshot(*ptr, s->dyn_views->_parent)) {
GfxContainer *parent = s->dyn_views->_parent;
do {
parent = parent->_parent;
} while (parent && (gfxw_widget_matches_snapshot(*ptr, parent)));
if (!parent) {
error("Attempted widget mass destruction by a snapshot");
}
reparentize_primary_widget_lists(s, (GfxPort *) parent);
}
if (!ptr) {
error("Attempt to restore invalid snaphot with handle %04x:%04x", PRINT_REG(handle));
return;
}
gfxw_restore_snapshot(s->visual, *ptr);
free(*ptr);
*ptr = NULL;
kfree(s->_segMan, handle);
}
PaletteEntry get_pic_color(EngineState *s, int color) {
if (!s->resMan->isVGA())
return s->ega_colors[color].visual;
if (color == -1 || color == 255) // -1 occurs in Eco Quest 1. Not sure if this is the best approach, but it seems to work
return PaletteEntry(255,255,255);
else if (color < s->gfx_state->gfxResMan->getColorCount())
return s->gfx_state->gfxResMan->getColor(color);
else {
// Happens in the beginning of EcoQuest 2, when the dialog box of the customs officer shows up
warning("Color index %d out of bounds for pic %d (%d max)", color, s->gfx_state->pic_nr, s->gfx_state->gfxResMan->getColorCount());
return PaletteEntry(0,0,0);
}
}
static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) {
Common::Point pos;
GuiResourceId cursorId = argv[0].toSint16();
// Set pointer position, if requested
if (argc >= 4) {
pos.y = argv[3].toSint16();
pos.x = argv[2].toSint16();
s->_gui->setCursorPos(pos);
}
if ((argc >= 2) && (argv[1].toSint16() == 0)) {
cursorId = -1;
}
s->_gui->setCursorShape(cursorId);
return s->r_acc;
}
static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
Common::Point pos;
Common::Point *hotspot = NULL;
switch (argc) {
case 1:
if (argv[0].isNull())
s->_gui->hideCursor();
else
s->_gui->showCursor();
break;
case 2:
pos.y = argv[1].toSint16();
pos.x = argv[0].toSint16();
s->_gui->setCursorPos(pos);
break;
case 4: {
int16 top = argv[0].toSint16();
int16 left = argv[1].toSint16();
int16 bottom = argv[2].toSint16();
int16 right = argv[3].toSint16();
if ((right >= left) && (bottom >= top)) {
Common::Rect rect = Common::Rect(left, top, right, bottom);
s->_cursor->setMoveZone(rect);
} else {
warning("kSetCursor: Ignoring invalid mouse zone (%i, %i)-(%i, %i)", left, top, right, bottom);
}
break;
}
case 5:
case 9:
hotspot = new Common::Point(argv[3].toSint16(), argv[4].toSint16());
// Fallthrough
case 3:
gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot);
if (hotspot)
delete hotspot;
break;
default :
warning("kSetCursor: Unhandled case: %d arguments given", argc);
break;
}
return s->r_acc;
}
reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) {
switch (s->detectSetCursorType()) {
case SCI_VERSION_0_EARLY:
return kSetCursorSci0(s, argc, argv);
case SCI_VERSION_1_1:
return kSetCursorSci11(s, argc, argv);
default:
warning("Unknown SetCursor type");
return NULL_REG;
}
}
reg_t kMoveCursor(EngineState *s, int argc, reg_t *argv) {
Common::Point pos;
if (argc == 2) {
pos.y = argv[1].toSint16();
pos.x = argv[0].toSint16();
s->_gui->moveCursor(pos);
}
return s->r_acc;
}
reg_t kShow(EngineState *s, int argc, reg_t *argv) {
int old_map = s->pic_visible_map;
s->pic_visible_map = (argc > 0) ? (gfx_map_mask_t) argv[0].toUint16() : GFX_MASK_VISUAL;
switch (s->pic_visible_map) {
case GFX_MASK_VISUAL:
case GFX_MASK_PRIORITY:
case GFX_MASK_CONTROL:
gfxop_set_visible_map(s->gfx_state, s->pic_visible_map);
if (old_map != s->pic_visible_map) {
2009-02-19 20:50:55 +00:00
if (s->pic_visible_map == GFX_MASK_VISUAL) // Full widget redraw
s->visual->draw(Common::Point(0, 0));
gfxop_update(s->gfx_state);
debugC(2, kDebugLevelGraphics, "Switching visible map to %x\n", s->pic_visible_map);
}
break;
default:
warning("Show(%x) selects unknown map", s->pic_visible_map);
}
s->pic_not_valid = 2;
return s->r_acc;
}
reg_t kPicNotValid(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, s->pic_not_valid);
if (argc)
s->pic_not_valid = (byte)argv[0].toUint16();
return s->r_acc;
}
void _k_redraw_box(EngineState *s, int x1, int y1, int x2, int y2) {
warning("_k_redraw_box(): Unimplemented");
#if 0
int i;
ViewObject *list = s->dyn_views;
printf("Reanimating views\n", s->dyn_views_nr);
for (i = 0;i < s->dyn_views_nr;i++) {
2009-02-19 20:50:55 +00:00
*(list[i].underBitsp) = graph_save_box(s, list[i].nsLeft, list[i].nsTop, list[i].nsRight - list[i].nsLeft,
list[i].nsBottom - list[i].nsTop, SCI_MAP_VISUAL | SCI_MAP_PRIORITY);
draw_view0(s->pic, s->ports[0], list[i].nsLeft, list[i].nsTop, list[i].priority, list[i].loop,
list[i].cel, 0, list[i].view);
}
graph_update_box(s, x1, y1, x2 - x1, y2 - y1);
for (i = 0;i < s->dyn_views_nr;i++) {
graph_restore_box(s, *(list[i].underBitsp));
list[i].underBits = 0;
}
#endif
}
reg_t kGraph(EngineState *s, int argc, reg_t *argv) {
int x = 0, y = 0, x1 = 0, y1 = 0;
uint16 flags;
int16 priority, control, color, colorMask;
Common::Rect rect;
if (argc >= 5) {
x = argv[2].toSint16();
y = argv[1].toSint16();
x1 = argv[4].toSint16();
y1 = argv[3].toSint16();
}
// old code, may be removed later after class migration
int redraw_port = 0;
rect_t area;
area = gfx_rect(argv[2].toSint16(), argv[1].toSint16() , argv[4].toSint16(), argv[3].toSint16());
area.width = area.width - area.x; // Since the actual coordinates are absolute
area.height = area.height - area.y;
switch (argv[0].toSint16()) {
case K_GRAPH_GET_COLORS_NR:
return make_reg(0, !s->resMan->isVGA() ? 0x10 : 0x100);
break;
case K_GRAPH_DRAW_LINE:
priority = (argc > 6) ? argv[6].toSint16() : -1;
control = (argc > 7) ? argv[7].toSint16() : -1;
color = argv[5].toSint16();
// FIXME: rect must be changed to 2 Common::Point
s->_gui->graphDrawLine(Common::Point(x, y), Common::Point(x1, y1), color, priority, control);
break;
case K_GRAPH_SAVE_BOX:
rect = Common::Rect(x, y, x1, y1);
flags = (argc > 5) ? argv[5].toUint16() : 0;
return s->_gui->graphSaveBox(rect, flags);
break;
case K_GRAPH_RESTORE_BOX:
s->_gui->graphRestoreBox(argv[1]);
break;
case K_GRAPH_FILL_BOX_BACKGROUND:
rect = Common::Rect(x, y, x1, y1);
s->_gui->graphFillBoxBackground(rect);
break;
case K_GRAPH_FILL_BOX_FOREGROUND:
rect = Common::Rect(x, y, x1, y1);
s->_gui->graphFillBoxForeground(rect);
break;
case K_GRAPH_FILL_BOX_ANY:
priority = (argc > 7) ? argv[7].toSint16() : -1;
control = (argc > 8) ? argv[8].toSint16() : -1;
color = argv[6].toSint16();
colorMask = argv[5].toUint16();
rect = Common::Rect(x, y, x1, y1);
s->_gui->graphFillBox(rect, colorMask, color, priority, control);
break;
case K_GRAPH_UPDATE_BOX: {
debugC(2, kDebugLevelGraphics, "update_box(%d, %d, %d, %d)\n", argv[1].toSint16(), argv[2].toSint16(), argv[3].toSint16(), argv[4].toSint16());
area.x += s->port->zone.x;
area.y += s->port->zone.y;
// FIXME: Change to class calling
//gfxop_update_box(s->gfx_state, area);
}
break;
case K_GRAPH_REDRAW_BOX: {
debugC(2, kDebugLevelGraphics, "redraw_box(%d, %d, %d, %d)\n", argv[1].toSint16(), argv[2].toSint16(), argv[3].toSint16(), argv[4].toSint16());
area.x += s->port->zone.x;
area.y += s->port->zone.y;
if (s->dyn_views && s->dyn_views->_parent == (GfxContainer *)s->port)
s->dyn_views->draw(Common::Point(0, 0));
// FIXME: Change to class calling
//gfxop_update_box(s->gfx_state, area);
}
break;
case K_GRAPH_ADJUST_PRIORITY:
debugC(2, kDebugLevelGraphics, "adjust_priority(%d, %d)\n", argv[1].toSint16(), argv[2].toSint16());
s->priority_first = argv[1].toSint16() - 10;
s->priority_last = argv[2].toSint16() - 10;
break;
default:
warning("Unhandled Graph() operation %04x", argv[0].toSint16());
}
if (redraw_port)
FULL_REDRAW();
gfxop_update(s->gfx_state);
2009-02-19 20:50:55 +00:00
return s->r_acc;
}
reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
int16 textWidth, textHeight;
Common::String text = s->_segMan->getString(argv[1]);
reg_t *dest = s->_segMan->derefRegPtr(argv[0], 4);
int maxwidth = (argc > 3) ? argv[3].toUint16() : 0;
int font_nr = argv[2].toUint16();
Common::String sep_str;
const char *sep = NULL;
if ((argc > 4) && (argv[4].segment)) {
sep_str = s->_segMan->getString(argv[4]);
sep = sep_str.c_str();
}
dest[0] = dest[1] = NULL_REG;
if (text.empty() || !dest) { // Empty text
dest[2] = dest[3] = make_reg(0, 0);
debugC(2, kDebugLevelStrings, "GetTextSize: Empty string\n");
return s->r_acc;
}
textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16();
s->_gui->textSize(s->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
debugC(2, kDebugLevelStrings, "GetTextSize '%s' -> %dx%d\n", text.c_str(), textWidth, textHeight);
dest[2] = make_reg(0, textHeight);
dest[3] = make_reg(0, textWidth);
return s->r_acc;
}
reg_t kWait(EngineState *s, int argc, reg_t *argv) {
int sleep_time = argv[0].toUint16();
#if 0
uint32 time;
time = g_system->getMillis();
s->r_acc = make_reg(0, ((long)time - (long)s->last_wait_time) * 60 / 1000);
s->last_wait_time = time;
sleep_time *= g_debug_sleeptime_factor;
gfxop_sleep(s->gfx_state, sleep_time * 1000 / 60);
// Reset speed throttler: Game is playing along nicely anyway
if (sleep_time > 0)
s->speedThrottler->reset();
#endif
// FIXME: we should not be asking from the GUI to wait. The kernel sounds
// like a better place
s->_gui->wait(sleep_time);
return s->r_acc;
}
reg_t kCoordPri(EngineState *s, int argc, reg_t *argv) {
int y = argv[0].toSint16();
return make_reg(0, _find_view_priority(s, y));
}
reg_t kPriCoord(EngineState *s, int argc, reg_t *argv) {
int priority = argv[0].toSint16();
return make_reg(0, _find_priority_band(s, priority));
}
void _k_dirloop(reg_t obj, uint16 angle, EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
int view = GET_SEL32V(obj, view);
int signal = GET_SEL32V(obj, signal);
int loop;
int maxloops;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (signal & _K_VIEW_SIG_FLAG_DOESNT_TURN)
return;
angle %= 360;
if (!oldScriptHeader) {
if (angle < 45)
loop = 3;
else if (angle < 136)
loop = 0;
else if (angle < 225)
loop = 2;
else if (angle < 316)
loop = 1;
else
loop = 3;
} else {
if (angle >= 330 || angle <= 30)
loop = 3;
else if (angle <= 150)
loop = 0;
else if (angle <= 210)
loop = 2;
else if (angle < 330)
loop = 1;
else loop = 0xffff;
}
maxloops = gfxop_lookup_view_get_loops(s->gfx_state, view);
if ((loop > 1) && (maxloops < 4))
return;
PUT_SEL32V(obj, loop, loop);
}
reg_t kDirLoop(EngineState *s, int argc, reg_t *argv) {
_k_dirloop(argv[0], argv[1].toUint16(), s, argc, argv);
return s->r_acc;
}
#define GASEOUS_VIEW_MASK_ACTIVE (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR)
#define GASEOUS_VIEW_MASK_PASSIVE (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR)
static Common::Rect nsrect_clip(EngineState *s, int y, Common::Rect retval, int priority);
static int collides_with(EngineState *s, Common::Rect area, reg_t other_obj, int use_nsrect, int view_mask, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
int other_signal = GET_SEL32V(other_obj, signal);
int other_priority = GET_SEL32V(other_obj, priority);
int y = (int16)GET_SEL32V(other_obj, y);
Common::Rect other_area;
if (use_nsrect) {
other_area = get_nsrect(s, other_obj, 0);
other_area = nsrect_clip(s, y, other_area, other_priority);
} else {
other_area.left = GET_SEL32V(other_obj, brLeft);
other_area.right = GET_SEL32V(other_obj, brRight);
other_area.top = GET_SEL32V(other_obj, brTop);
other_area.bottom = GET_SEL32V(other_obj, brBottom);
}
if (other_area.right < 0 || other_area.bottom < 0 || area.right < 0 || area.bottom < 0)
2009-02-19 20:50:55 +00:00
return 0; // Out of scope
if (other_area.left >= 320 || other_area.top >= 190 || area.right >= 320 || area.bottom >= 190)
2009-02-19 20:50:55 +00:00
return 0; // Out of scope
debugC(2, kDebugLevelBresen, "OtherSignal=%04x, z=%04x obj=%04x:%04x\n", other_signal, (other_signal & view_mask), PRINT_REG(other_obj));
if ((other_signal & (view_mask)) == 0) {
2009-02-19 20:50:55 +00:00
// check whether the other object ignores actors
debugC(2, kDebugLevelBresen, " against (%d,%d) to (%d,%d)\n", other_area.left, other_area.top, other_area.right, other_area.bottom);
if (area.intersects(other_area))
return 1;
/* CR (from :Bob Heitman:) Collision rects have Mac semantics, ((0,0),(1,1)) only
2009-02-19 20:50:55 +00:00
** covers the coordinate (0,0) */
}
debugC(2, kDebugLevelBresen, " (no)\n");
return 0;
}
reg_t kCanBeHere(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t obj = argv[0];
reg_t cliplist_ref = (argc > 1) ? argv[1] : NULL_REG;
List *cliplist = NULL;
GfxPort *port = s->picture_port;
uint16 signal;
int retval;
Common::Rect abs_zone;
rect_t zone;
uint16 edgehit;
uint16 illegal_bits;
abs_zone.left = (int16)GET_SEL32V(obj, brLeft);
abs_zone.right = (int16)GET_SEL32V(obj, brRight);
abs_zone.top = (int16)GET_SEL32V(obj, brTop);
abs_zone.bottom = (int16)GET_SEL32V(obj, brBottom);
zone = gfx_rect(abs_zone.left + port->zone.x, abs_zone.top + port->zone.y, abs_zone.width(), abs_zone.height());
signal = GET_SEL32V(obj, signal);
debugC(2, kDebugLevelBresen, "Checking collision: (%d,%d) to (%d,%d) ([%d..%d]x[%d..%d]), obj=%04x:%04x, sig=%04x, cliplist=%04x:%04x\n",
GFX_PRINT_RECT(zone), abs_zone.left, abs_zone.right, abs_zone.top, abs_zone.bottom,
PRINT_REG(obj), signal, PRINT_REG(cliplist_ref));
illegal_bits = GET_SEL32V(obj, illegalBits);
2009-02-19 20:50:55 +00:00
retval = !(illegal_bits & (edgehit = gfxop_scan_bitmask(s->gfx_state, zone, GFX_MASK_CONTROL)));
debugC(2, kDebugLevelBresen, "edgehit = %04x (illegalBits %04x)\n", edgehit, illegal_bits);
if (retval == 0) {
debugC(2, kDebugLevelBresen, " -> %04x\n", retval);
return NULL_REG; // Can't BeHere
}
retval = 0;
2009-02-19 20:50:55 +00:00
if ((illegal_bits & 0x8000) // If we are vulnerable to those views at all...
&& s->dyn_views) { // ...check against all stop-updated dynviews
GfxDynView *widget = (GfxDynView *)s->dyn_views->_contents;
debugC(2, kDebugLevelBresen, "Checking vs dynviews:\n");
while (widget) {
if (widget->_ID && (widget->signal & _K_VIEW_SIG_FLAG_STOPUPD)
&& ((widget->_ID != obj.segment) || (widget->_subID != obj.offset))
&& s->_segMan->isObject(make_reg(widget->_ID, widget->_subID)))
if (collides_with(s, abs_zone, make_reg(widget->_ID, widget->_subID), 1, GASEOUS_VIEW_MASK_ACTIVE, argc, argv))
return NULL_REG;
widget = (GfxDynView *)widget->_next;
}
}
if (signal & GASEOUS_VIEW_MASK_ACTIVE) {
2009-02-19 20:50:55 +00:00
retval = signal & GASEOUS_VIEW_MASK_ACTIVE; // CanBeHere- it's either being disposed, or it ignores actors anyway
debugC(2, kDebugLevelBresen, " -> %04x\n", retval);
return make_reg(0, retval); // CanBeHere
}
if (cliplist_ref.segment)
cliplist = s->_segMan->lookupList(cliplist_ref);
if (cliplist) {
Node *node = s->_segMan->lookupNode(cliplist->first);
2009-02-19 20:50:55 +00:00
retval = 0; // Assume that we Can'tBeHere...
2009-02-19 20:50:55 +00:00
while (node) { // Check each object in the list against our bounding rectangle
reg_t other_obj = node->value;
debugC(2, kDebugLevelBresen, " comparing against %04x:%04x\n", PRINT_REG(other_obj));
if (!s->_segMan->isObject(other_obj)) {
warning("CanBeHere() cliplist contains non-object %04x:%04x", PRINT_REG(other_obj));
} else if (other_obj != obj) { // Clipping against yourself is not recommended
if (collides_with(s, abs_zone, other_obj, 0, GASEOUS_VIEW_MASK_PASSIVE, argc, argv)) {
debugC(2, kDebugLevelBresen, " -> %04x\n", retval);
return NULL_REG;
}
2009-02-19 20:50:55 +00:00
} // if (other_obj != obj)
node = s->_segMan->lookupNode(node->succ); // move on
}
}
if (!retval)
retval = 1;
debugC(2, kDebugLevelBresen, " -> %04x\n", retval);
return make_reg(0, retval);
2009-02-19 20:50:55 +00:00
} // CanBeHere
reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) {
// kCantBeHere does the same thing as kCanBeHere, except that
// it returns the opposite result.
reg_t result = kCanBeHere(s, argc, argv);
result.offset = !result.offset;
return result;
}
reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) {
int view = argv[0].toSint16();
int loop = argv[1].toSint16();
int cel = argv[2].toSint16();
int y = argv[3].toUint16();
int x = argv[4].toUint16();
gfxr_view_t *res = NULL;
gfx_pixmap_t *pxm = NULL;
res = s->gfx_state->gfxResMan->getView(view, &loop, &cel, 0);
if (!res) {
warning("[GFX] Attempt to get cel parameters for invalid view %d", view);
return SIGNAL_REG;
}
pxm = res->loops[loop].cels[cel];
if (x > pxm->index_width)
x = pxm->index_width - 1;
if (y > pxm->index_height)
y = pxm->index_height - 1;
return make_reg(0, pxm->index_data[y * pxm->index_width + x] == pxm->color_key);
}
reg_t kCelHigh(EngineState *s, int argc, reg_t *argv) {
int view = argv[0].toSint16();
int loop = argv[1].toSint16();
int cel = argv[2].toSint16();
int height, width;
Common::Point offset;
if (argc != 3) {
warning("CelHigh called with %d parameters", argc);
}
gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset);
return make_reg(0, height);
}
reg_t kCelWide(EngineState *s, int argc, reg_t *argv) {
int view = argv[0].toSint16();
int loop = argv[1].toSint16();
int cel = argv[2].toSint16();
int height, width;
Common::Point offset;
if (argc != 3) {
warning("CelHigh called with %d parameters", argc);
}
gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset);
return make_reg(0, width);
}
reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t obj = argv[0];
int view = GET_SEL32V(obj, view);
int loops_nr = gfxop_lookup_view_get_loops(s->gfx_state, view);
if (loops_nr < 0) {
error("view.%d (0x%x) not found", view, view);
return NULL_REG;
}
debugC(2, kDebugLevelGraphics, "NumLoops(view.%d) = %d\n", view, loops_nr);
return make_reg(0, loops_nr);
}
reg_t kNumCels(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t obj = argv[0];
int loop = GET_SEL32V(obj, loop);
int view = GET_SEL32V(obj, view);
int cel = 0xffff;
gfxop_check_cel(s->gfx_state, view, &loop, &cel);
debugC(2, kDebugLevelGraphics, "NumCels(view.%d, %d) = %d\n", view, loop, cel + 1);
return make_reg(0, cel + 1);
}
reg_t kOnControl(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect;
byte screenMask;
int argBase = 0;
if ((argc == 2) || (argc == 4)) {
screenMask = GFX_MASK_CONTROL;
} else {
screenMask = argv[0].toUint16();
argBase = 1;
}
rect.left = argv[argBase].toSint16();
rect.top = argv[argBase + 1].toSint16();
if (argc > 3) {
rect.right = argv[argBase + 2].toSint16();
rect.bottom = argv[argBase + 3].toSint16();
} else {
rect.right = rect.left + 1;
rect.bottom = rect.top + 1;
}
return make_reg(0, s->_gui->onControl(screenMask, rect));
}
void _k_view_list_free_backgrounds(EngineState *s, ViewObject *list, int list_nr);
#define K_DRAWPIC_FLAG_MIRRORED (1 << 14)
reg_t kDrawPic(EngineState *s, int argc, reg_t *argv) {
GuiResourceId pictureId = argv[0].toUint16();
int16 flags = 0;
int16 animationNr = -1;
bool mirroredFlag = false;
bool addToFlag = false;
int16 EGApaletteNo = 0; // default needs to be 0
if (argc >= 2) {
flags = argv[1].toSint16();
animationNr = flags & 0xFF;
if (flags & K_DRAWPIC_FLAG_MIRRORED)
mirroredFlag = true;
}
if (argc >= 3) {
if (!argv[2].isNull())
addToFlag = true;
// FIXME: usesOldGfxFunctions() seems to be broken, cause sq3 has it set, but uses bit 0 correctly
if (!s->_kernel->usesOldGfxFunctions())
addToFlag = !addToFlag; // later engines set the bit, but dont want to add to picture
}
if (argc >= 4)
EGApaletteNo = argv[3].toUint16();
s->_gui->drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo);
return s->r_acc;
}
Common::Rect set_base(EngineState *s, reg_t object) {
SegManager *segMan = s->_segMan;
int x, y, original_y, z, ystep, xsize, ysize;
int xbase, ybase, xend, yend;
int view, loop, cel;
int oldloop, oldcel;
int xmod = 0, ymod = 0;
Common::Rect retval;
x = (int16)GET_SEL32V(object, x);
original_y = y = (int16)GET_SEL32V(object, y);
if (s->_kernel->_selectorCache.z > -1)
z = (int16)GET_SEL32V(object, z);
else
z = 0;
2009-02-19 20:50:55 +00:00
y -= z; // Subtract z offset
ystep = (int16)GET_SEL32V(object, yStep);
view = (int16)GET_SEL32V(object, view);
oldloop = loop = sign_extend_byte(GET_SEL32V(object, loop));
oldcel = cel = sign_extend_byte(GET_SEL32V(object, cel));
Common::Point offset = Common::Point(0, 0);
if (loop != oldloop) {
loop = 0;
PUT_SEL32V(object, loop, 0);
debugC(2, kDebugLevelGraphics, "Resetting loop for %04x:%04x!\n", PRINT_REG(object));
}
if (cel != oldcel) {
cel = 0;
PUT_SEL32V(object, cel, 0);
}
gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &xsize, &ysize, &offset);
xmod = offset.x;
ymod = offset.y;
xbase = x - xmod - (xsize >> 1);
xend = xbase + xsize;
yend = y /* - ymod */ + 1;
ybase = yend - ystep;
debugC(2, kDebugLevelBaseSetter, "(%d,%d)+/-(%d,%d), (%d x %d) -> (%d, %d) to (%d, %d)\n",
x, y, xmod, ymod, xsize, ysize, xbase, ybase, xend, yend);
retval.left = xbase;
retval.top = ybase;
retval.right = xend;
retval.bottom = yend;
return retval;
}
void _k_base_setter(EngineState *s, reg_t object) {
SegManager *segMan = s->_segMan;
Common::Rect absrect = set_base(s, object);
if (lookup_selector(s->_segMan, object, s->_kernel->_selectorCache.brLeft, NULL, NULL) != kSelectorVariable)
2009-02-19 20:50:55 +00:00
return; // non-fatal
// Note: there was a check here for a very old version of SCI, which supposedly needed
// to subtract 1 from absrect.top. The original check was for version 0.000.256, which
// does not exist (earliest one was KQ4 SCI, version 0.000.274). This code is left here
// for reference only
#if 0
if (getSciVersion() <= SCI_VERSION_0)
--absrect.top; // Compensate for early SCI OB1 'bug'
#endif
PUT_SEL32V(object, brLeft, absrect.left);
PUT_SEL32V(object, brRight, absrect.right);
PUT_SEL32V(object, brTop, absrect.top);
PUT_SEL32V(object, brBottom, absrect.bottom);
}
reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
_k_base_setter(s, object);
return s->r_acc;
2009-02-19 20:50:55 +00:00
} // kBaseSetter
static Common::Rect nsrect_clip(EngineState *s, int y, Common::Rect retval, int priority) {
int pri_top;
if (priority == -1)
priority = _find_view_priority(s, y);
pri_top = _find_priority_band(s, priority) + 1;
2009-02-19 20:50:55 +00:00
// +1: Don't know why, but this seems to be happening
if (retval.top < pri_top)
retval.top = pri_top;
if (retval.bottom < retval.top)
retval.top = retval.bottom - 1;
return retval;
}
static Common::Rect calculate_nsrect(EngineState *s, int x, int y, int view, int loop, int cel) {
int xbase, ybase, xend, yend, xsize, ysize;
int xmod = 0, ymod = 0;
Common::Rect retval(0, 0, 0, 0);
Common::Point offset = Common::Point(0, 0);
gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &xsize, &ysize, &offset);
xmod = offset.x;
ymod = offset.y;
xbase = x - xmod - (xsize >> 1);
xend = xbase + xsize;
2009-02-19 20:50:55 +00:00
yend = y - ymod + 1; // +1: magic modifier
ybase = yend - ysize;
retval.left = xbase;
retval.top = ybase;
retval.right = xend;
retval.bottom = yend;
return retval;
}
Common::Rect get_nsrect(EngineState *s, reg_t object, byte clip) {
SegManager *segMan = s->_segMan;
int x, y, z;
int view, loop, cel;
Common::Rect retval;
x = (int16)GET_SEL32V(object, x);
y = (int16)GET_SEL32V(object, y);
if (s->_kernel->_selectorCache.z > -1)
z = (int16)GET_SEL32V(object, z);
else
z = 0;
2009-02-19 20:50:55 +00:00
y -= z; // Subtract z offset
view = (int16)GET_SEL32V(object, view);
loop = sign_extend_byte((int16)GET_SEL32V(object, loop));
cel = sign_extend_byte((int16)GET_SEL32V(object, cel));
retval = calculate_nsrect(s, x, y, view, loop, cel);
if (clip) {
int priority = (int16)GET_SEL32V(object, priority);
return nsrect_clip(s, y, retval, priority);
}
return retval;
}
reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
s->_gui->setNowSeen(argv[0]);
return s->r_acc;
2009-02-26 02:21:55 +00:00
}
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
// warning("kPalette %d", argv[0].toUint16());
switch (argv[0].toUint16()) {
case 1:
if (argc==3) {
int resourceNo = argv[1].toUint16();
int flags = argv[2].toUint16();
s->_gui->paletteSet(resourceNo, flags);
}
break;
case 2:
debug(5, "STUB: kPalette() effect 2, set flag to colors");
break;
case 3:
debug(5, "STUB: kPalette() effect 3, clear flag to colors");
break;
case 4: { // Set palette intensity
if (argc >= 4) {
int fromColor = CLIP<int>(1, 255, argv[1].toUint16());
int toColor = CLIP<int>(1, 255, argv[2].toUint16());
int intensity = argv[3].toUint16();
bool setPalette = (argc < 5) ? true : (argv[5].isNull()) ? true : false;
s->_gui->paletteSetIntensity(fromColor, toColor, intensity, setPalette);
}
break;
}
case 5: { // Find closest color
int r = argv[1].toUint16();
int g = argv[2].toUint16();
int b = argv[3].toUint16();
return make_reg(0, s->_gui->paletteFind(r, g, b));
}
case 6:
if (argc==4) {
int fromColor = argv[1].toUint16();
int toColor = argv[2].toUint16();
int speed = argv[3].toSint16();
s->_gui->paletteAnimate(fromColor, toColor, speed);
}
break;
case 7:
debug(5, "STUB: kPalette() effect 7, save palette to heap");
break;
case 8:
debug(5, "STUB: kPalette() effect 8, set stored palette");
break;
default:
warning("kPalette(): Unimplemented subfunction: %d", argv[0].toUint16());
}
2009-02-19 20:50:55 +00:00
return s->r_acc;
}
static void _k_draw_control(EngineState *s, reg_t obj, bool inverse);
static void disableCertainButtons(SegManager *segMan, Common::String gameName, reg_t obj) {
reg_t text_pos = GET_SEL32(obj, text);
Common::String text;
if (!text_pos.isNull())
text = segMan->getString(text_pos);
int type = GET_SEL32V(obj, type);
int state = GET_SEL32V(obj, state);
/*
* WORKAROUND: The function is a "prevent the user from doing something
* nasty" type of thing, and goes back to the ugly way in which savegame
* deletion is implemented in SCI (and even worse in SQ4/Floppy, for
* which the workaround is intended). The result is basically that you
* can't implement savegame deletion for SQ4/Floppy unless you duplicate
* the exact naming scheme of savefiles (i.e. savefiles must be named
* SQ4SG.<number>) and the exact file format of the savegame index
* (SQ4SG.DIR). From the earlier discussions on file I/O handling -
* before as well as after the merge - I gather that this is not an
* option.
*
* SQ4/Floppy is special, being the first game to implement savegame
* deletion at all. For later games, we manage to implement deletion by
* using gross hacks in kDeviceInfo() (essentially repurposing a few
* subfunctions). I decided at the time that SQ4/Floppy was not worth the
* effort (see above), and to simply disable the delete functionality for
* that game - bringing the save/load dialog on a par with SCI0.
*/
// NOTE: This _only_ works with the English version
if (type == K_CONTROL_BUTTON && (gameName == "sq4") &&
getSciVersion() < SCI_VERSION_1_1 && text == " Delete ") {
PUT_SEL32V(obj, state, (state | kControlStateDisabled) & ~kControlStateEnabled);
}
// Disable the "Change Directory" button, as we don't allow the game engine to
// change the directory where saved games are placed
// NOTE: This _only_ works with the English version
if (type == K_CONTROL_BUTTON && text == "Change\r\nDirectory") {
PUT_SEL32V(obj, state, (state | kControlStateDisabled) & ~kControlStateEnabled);
2009-02-19 20:50:55 +00:00
}
}
reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
disableCertainButtons(s->_segMan, s->_gameName, obj);
_k_draw_control(s, obj, false);
// FULL_REDRAW();
return NULL_REG;
}
reg_t kHiliteControl(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
_k_draw_control(s, obj, true);
2009-02-19 20:50:55 +00:00
return s->r_acc;
}
2009-02-19 20:50:55 +00:00
void update_cursor_limits(int *display_offset, int *cursor, int max_displayed) {
if (*cursor < *display_offset + 4) {
if (*cursor < 8)
*display_offset = 0;
else
*display_offset = *cursor - 8;
} else if (*cursor - *display_offset > max_displayed - 8)
*display_offset = 12 + *cursor - max_displayed;
}
#define _K_EDIT_DELETE \
2009-02-19 20:50:55 +00:00
if (cursor < textlen) { \
text.deleteChar(cursor); \
2009-02-19 20:50:55 +00:00
}
#define _K_EDIT_BACKSPACE \
2009-02-19 20:50:55 +00:00
if (cursor) { \
--cursor; \
text.deleteChar(cursor); \
2009-02-19 20:50:55 +00:00
--textlen; \
}
reg_t kEditControl(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t obj = argv[0];
reg_t event = argv[1];
if (obj.segment) {
uint16 ct_type = GET_SEL32V(obj, type);
switch (ct_type) {
case 0:
2009-02-19 20:50:55 +00:00
break; // NOP
case K_CONTROL_EDIT:
if (event.segment && ((GET_SEL32V(event, type)) == SCI_EVT_KEYBOARD)) {
int max_displayed = GET_SEL32V(obj, max);
int max = max_displayed;
int cursor = GET_SEL32V(obj, cursor);
int modifiers = GET_SEL32V(event, modifiers);
int key = GET_SEL32V(event, message);
reg_t text_pos = GET_SEL32(obj, text);
int display_offset = 0;
Common::String text = s->_segMan->getString(text_pos);
int textlen;
#if 0
if (!text) {
warning("Could not draw control: %04x:%04x does not reference text", PRINT_REG(text_pos));
return s->r_acc;
}
#endif
textlen = text.size();
cursor += display_offset;
if (cursor > textlen)
cursor = textlen;
if (modifiers & SCI_EVM_CTRL) {
switch (tolower((char)key)) {
case 'a':
cursor = 0;
break;
case 'e':
cursor = textlen;
break;
case 'f':
if (cursor < textlen) ++cursor;
break;
case 'b':
if (cursor > 0) --cursor;
break;
case 'k':
text = Common::String(text.c_str(), cursor);
2009-02-19 20:50:55 +00:00
break; // Terminate string
case 'h':
_K_EDIT_BACKSPACE;
break;
case 'd':
_K_EDIT_DELETE;
break;
}
PUT_SEL32V(event, claimed, 1);
2009-02-19 20:50:55 +00:00
} else if (modifiers & SCI_EVM_ALT) { // Ctrl has precedence over Alt
switch (key) {
case 0x2100 /* A-f */:
while ((cursor < textlen) && (text[cursor++] != ' '))
;
break;
case 0x3000 /* A-b */:
while ((cursor > 0) && (text[--cursor - 1] != ' '))
;
break;
case 0x2000 /* A-d */: {
while ((cursor < textlen) && (text[cursor] == ' ')) {
_K_EDIT_DELETE;
textlen--;
}
while ((cursor < textlen) && (text[cursor] != ' ')) {
_K_EDIT_DELETE;
textlen--;
}
break;
}
}
PUT_SEL32V(event, claimed, 1);
} else if (key < 31) {
PUT_SEL32V(event, claimed, 1);
switch (key) {
case SCI_K_BACKSPACE:
_K_EDIT_BACKSPACE;
break;
default:
PUT_SEL32V(event, claimed, 0);
}
} else if (key & 0xff00) {
switch (key) {
case SCI_K_HOME:
cursor = 0;
break;
case SCI_K_END:
cursor = textlen;
break;
case SCI_K_RIGHT:
2009-02-22 13:11:43 +00:00
if (cursor + 1 <= textlen)
2009-02-19 20:50:55 +00:00
++cursor;
break;
case SCI_K_LEFT:
2009-02-19 20:50:55 +00:00
if (cursor > 0)
--cursor;
break;
case SCI_K_DELETE:
_K_EDIT_DELETE;
break;
}
PUT_SEL32V(event, claimed, 1);
} else if ((key > 31) && (key < 128)) {
int inserting = (modifiers & SCI_EVM_INSERT);
modifiers &= ~(SCI_EVM_RSHIFT | SCI_EVM_LSHIFT | SCI_EVM_CAPSLOCK);
if (cursor == textlen) {
if (textlen < max) {
text += key;
cursor++;
}
} else if (inserting) {
if (textlen < max) {
int i;
for (i = textlen + 2; i >= cursor; i--)
text.setChar(text[i - 1], i);
text.setChar(key, cursor++);
}
2009-02-19 20:50:55 +00:00
} else { // Overwriting
text.setChar(key, cursor++);
}
if (max_displayed < max)
update_cursor_limits(&display_offset, &cursor, max_displayed);
cursor -= display_offset;
PUT_SEL32V(event, claimed, 1);
}
2009-02-19 20:50:55 +00:00
PUT_SEL32V(obj, cursor, cursor); // Write back cursor position
s->_segMan->strcpy(text_pos, text.c_str()); // Write back string
}
if (event.segment) PUT_SEL32V(event, claimed, 1);
_k_draw_control(s, obj, false);
return NULL_REG;
case K_CONTROL_ICON:
case K_CONTROL_BOX:
case K_CONTROL_BUTTON:
return NULL_REG;
case K_CONTROL_TEXT: {
int state = GET_SEL32V(obj, state);
PUT_SEL32V(obj, state, state | kControlStateDitherFramed);
_k_draw_control(s, obj, false);
PUT_SEL32V(obj, state, state);
}
break;
default:
warning("Attempt to edit control type %d", ct_type);
}
}
return s->r_acc;
}
static void _k_draw_control(EngineState *s, reg_t obj, bool hilite) {
SegManager *segMan = s->_segMan;
int x = (int16)GET_SEL32V(obj, nsLeft);
int y = (int16)GET_SEL32V(obj, nsTop);
int xl = (int16)GET_SEL32V(obj, nsRight) - x;
int yl = (int16)GET_SEL32V(obj, nsBottom) - y;
rect_t area = gfx_rect(x, y, xl, yl);
Common::Rect rect;
rect = Common::Rect (x, y, (int16)GET_SEL32V(obj, nsRight), (int16)GET_SEL32V(obj, nsBottom));
int font_nr = GET_SEL32V(obj, font);
reg_t text_pos = GET_SEL32(obj, text);
Common::String text;
if (!text_pos.isNull())
text = s->_segMan->getString(text_pos);
int view = GET_SEL32V(obj, view);
int cel = sign_extend_byte(GET_SEL32V(obj, cel));
int loop = sign_extend_byte(GET_SEL32V(obj, loop));
int mode;
int type = GET_SEL32V(obj, type);
int state = GET_SEL32V(obj, state);
int cursor;
int max;
switch (type) {
case K_CONTROL_BUTTON:
debugC(2, kDebugLevelGraphics, "drawing button %04x:%04x to %d,%d\n", PRINT_REG(obj), x, y);
s->_gui->drawControlButton(rect, obj, s->strSplit(text.c_str(), NULL).c_str(), font_nr, state, hilite);
return;
case K_CONTROL_TEXT:
mode = (gfx_alignment_t) GET_SEL32V(obj, mode);
debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d\n", PRINT_REG(obj), text.c_str(), x, y, mode);
s->_gui->drawControlText(rect, obj, s->strSplit(text.c_str(), NULL).c_str(), font_nr, mode, state, hilite);
return;
case K_CONTROL_EDIT:
debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d\n", PRINT_REG(obj), PRINT_REG(text_pos), text.c_str(), x, y);
max = GET_SEL32V(obj, max);
cursor = GET_SEL32V(obj, cursor);
if (cursor > (signed)text.size())
cursor = text.size();
// update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); FIXME: get rid of this?
ADD_TO_CURRENT_PICTURE_PORT(sciw_new_edit_control(s->port, obj, area, text.c_str(), font_nr, (unsigned)cursor, (int8)hilite));
break;
case K_CONTROL_ICON:
debugC(2, kDebugLevelGraphics, "drawing icon control %04x:%04x to %d,%d\n", PRINT_REG(obj), x, y - 1);
s->_gui->drawControlIcon(rect, obj, view, loop, cel, state, hilite);
return;
case K_CONTROL_CONTROL:
case K_CONTROL_CONTROL_ALIAS: {
int entries_nr;
int lsTop;
int list_top = 0;
int selection = 0;
int entry_size = GET_SEL32V(obj, x);
int i;
if (s->_kernel->_selectorCache.topString != -1) {
// Games from early SCI1 onwards use topString
lsTop = GET_SEL32V(obj, topString);
} else {
// Earlier games use lsTop
lsTop = GET_SEL32V(obj, lsTop);
}
lsTop -= text_pos.offset;
debugC(2, kDebugLevelGraphics, "drawing list control %04x:%04x to %d,%d, diff %d\n", PRINT_REG(obj), x, y, SCI_MAX_SAVENAME_LENGTH);
cursor = GET_SEL32V(obj, cursor) - text_pos.offset;
entries_nr = 0;
// NOTE: most types of pointer dereferencing don't like odd offsets
if (entry_size & 1) {
warning("List control with odd entry_size %d. This is not yet implemented for all types of segments", entry_size);
}
reg_t seeker = text_pos;
// Count string entries in NULL terminated string list
while (s->_segMan->strlen(seeker) > 0) {
++entries_nr;
seeker.offset += entry_size;
}
// TODO: This is rather convoluted... It would be a lot cleaner
// if sciw_new_list_control would take a list of Common::String
Common::String *strings = 0;
const char **entries_list = NULL;
2009-02-19 20:50:55 +00:00
if (entries_nr) { // determine list_top, selection, and the entries_list
seeker = text_pos;
entries_list = (const char**)malloc(sizeof(char *) * entries_nr);
strings = new Common::String[entries_nr];
for (i = 0; i < entries_nr; i++) {
strings[i] = s->_segMan->getString(seeker);
entries_list[i] = strings[i].c_str();
seeker.offset += entry_size;
if ((seeker.offset - text_pos.offset) == lsTop)
list_top = i + 1;
if ((seeker.offset - text_pos.offset) == cursor)
selection = i + 1;
}
}
ADD_TO_CURRENT_PICTURE_PORT(sciw_new_list_control(s->port, obj, area, font_nr, entries_list, entries_nr,
list_top, selection, (int8)hilite));
free(entries_list);
delete[] strings;
}
break;
case K_CONTROL_BOX:
break;
default:
warning("Unknown control type: %d at %04x:%04x, at (%d, %d) size %d x %d",
type, PRINT_REG(obj), x, y, xl, yl);
}
if (!s->pic_not_valid) {
FULL_REDRAW();
}
}
void _k_view_list_mark_free(EngineState *s, reg_t off) {
if (s->dyn_views) {
GfxDynView *w = (GfxDynView *)s->dyn_views->_contents;
while (w) {
if (w->_ID == off.segment
&& w->_subID == off.offset) {
w->under_bitsp.obj = NULL_REG;
}
w = (GfxDynView *)w->_next;
}
}
}
reg_t kAddToPic(EngineState *s, int argc, reg_t *argv) {
GuiResourceId viewId;
GuiViewLoopNo loopNo;
GuiViewCelNo celNo;
int16 leftPos, topPos, priority, control;
switch (argc) {
case 0:
break;
case 1:
s->_gui->addToPicList(argv[0], argc, argv);
break;
case 7:
viewId = argv[0].toUint16();
2009-10-04 16:35:02 +00:00
loopNo = argv[1].toSint16();
celNo = argv[2].toSint16();
leftPos = argv[3].toSint16();
topPos = argv[4].toSint16();
priority = argv[5].toSint16();
control = argv[6].toSint16();
s->_gui->addToPicView(viewId, loopNo, celNo, leftPos, topPos, priority, control);
break;
default:
error("kAddToPic with unsupported parameter count %d", argc);
}
return s->r_acc;
}
reg_t kGetPort(EngineState *s, int argc, reg_t *argv) {
return s->_gui->getPort();
}
reg_t kSetPort(EngineState *s, int argc, reg_t *argv) {
uint16 portPtr;
Common::Rect picRect;
int16 picTop, picLeft;
switch (argc) {
case 1:
portPtr = argv[0].toSint16();
s->_gui->setPort(portPtr);
break;
case 6:
picRect.top = argv[0].toSint16();
picRect.left = argv[1].toSint16();
picRect.bottom = argv[2].toSint16();
picRect.right = argv[3].toSint16();
picTop = argv[4].toSint16();
picLeft = argv[5].toSint16();
s->_gui->setPortPic(picRect, picTop, picLeft);
break;
default:
error("SetPort was called with %d parameters", argc);
break;
}
return NULL_REG;
}
reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) {
GuiResourceId viewId = argv[0].toSint16();
GuiViewLoopNo loopNo = argv[1].toSint16();
GuiViewCelNo celNo = argv[2].toSint16();
int x = argv[3].toSint16();
int y = argv[4].toSint16();
int priority = (argc > 5) ? argv[5].toUint16() : -1;
int paletteNo = (argc > 6) ? argv[6].toSint16() : 0;
s->_gui->drawCel(viewId, loopNo, celNo, x, y, priority, paletteNo);
return s->r_acc;
}
reg_t kDisposeWindow(EngineState *s, int argc, reg_t *argv) {
int goner_nr = argv[0].toSint16();
int arg2 = (argc != 2 || argv[2].toUint16() == 0 ? 0 : 1);
2009-02-19 20:50:55 +00:00
s->_gui->disposeWindow(goner_nr, arg2);
return s->r_acc;
}
reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect1 (argv[1].toSint16(), argv[0].toSint16(), argv[3].toSint16(), argv[2].toSint16());
Common::Rect rect2;
int argextra = argc == 13 ? 4 : 0; // Triggers in PQ3 and SCI1.1 games
int style = argv[5 + argextra].toSint16();
int priority = (argc > 6 + argextra) ? argv[6 + argextra].toSint16() : -1;
int colorPen = (argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0;
int colorBack = (argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255;
// const char *title = argv[4 + argextra].segment ? kernel_dereference_char_pointer(s, argv[4 + argextra], 0) : NULL;
if (argc==13) {
rect2 = Common::Rect (argv[5].toSint16(), argv[4].toSint16(), argv[7].toSint16(), argv[6].toSint16());
}
Common::String title;
if (argv[4 + argextra].segment) {
title = s->_segMan->getString(argv[4 + argextra]);
title = s->strSplit(title.c_str(), NULL);
}
return s->_gui->newWindow(rect1, rect2, style, priority, colorPen, colorBack, title.c_str());
}
reg_t kAnimate(EngineState *s, int argc, reg_t *argv) {
reg_t castListReference = (argc > 0) ? argv[0] : NULL_REG;
bool cycle = (argc > 1) ? ((argv[1].toUint16()) ? true : false) : false;
s->_gui->animate(castListReference, cycle, argc, argv);
return s->r_acc;
}
#define SHAKE_DOWN 1
#define SHAKE_RIGHT 2
reg_t kShakeScreen(EngineState *s, int argc, reg_t *argv) {
int shakes = (argc > 0) ? argv[0].toSint16() : 1;
int directions = (argc > 1) ? argv[1].toSint16() : 1;
gfx_pixmap_t *screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 0, 320, 200));
int i;
if (directions & ~3)
debugC(2, kDebugLevelGraphics, "ShakeScreen(): Direction bits are %x (unknown)\n", directions);
gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
for (i = 0; i < shakes; i++) {
int shake_down = (directions & SHAKE_DOWN) ? 10 : 0;
int shake_right = (directions & SHAKE_RIGHT) ? 10 : 0;
if (directions & SHAKE_DOWN)
gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 320, 10), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT);
if (directions & SHAKE_RIGHT)
gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 10, 200), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT);
gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320 - shake_right, 200 - shake_down),
Common::Point(shake_right, shake_down));
gfxop_update(s->gfx_state);
gfxop_sleep(s->gfx_state, 50);
gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320, 200), Common::Point(0, 0));
gfxop_update(s->gfx_state);
gfxop_sleep(s->gfx_state, 50);
}
gfxop_free_pixmap(s->gfx_state, screen);
gfxop_update(s->gfx_state);
return s->r_acc;
}
reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
reg_t textp = argv[0];
int index = (argc > 1) ? argv[1].toUint16() : 0;
Common::String text;
if (textp.segment) {
argc--; argv++;
text = s->_segMan->getString(textp);
} else {
argc--; argc--; argv++; argv++;
text = kernel_lookup_text(s, textp, index);
}
s->_gui->display(s->strSplit(text.c_str()).c_str(), argc, argv);
return s->r_acc;
}
static reg_t kShowMovie_Windows(EngineState *s, int argc, reg_t *argv) {
Common::String filename = s->_segMan->getString(argv[1]);
Graphics::AVIPlayer *player = new Graphics::AVIPlayer(g_system);
if (!player->open(filename)) {
warning("Failed to open movie file %s", filename.c_str());
return s->r_acc;
}
uint32 startTime = g_system->getMillis();
bool play = true;
while (play && player->getCurFrame() < player->getFrameCount()) {
uint32 elapsed = g_system->getMillis() - startTime;
if (elapsed >= player->getCurFrame() * 1000 / player->getFrameRate()) {
Graphics::Surface *surface = player->getNextFrame();
Palette *palette = NULL;
if (player->dirtyPalette()) {
byte *rawPalette = player->getPalette();
Palette *colors = new Palette(256);
byte r, g, b;
for (uint16 i = 0; i < 256; i++) {
r = rawPalette[i * 4];
g = rawPalette[i * 4 + 1];
b = rawPalette[i * 4 + 2];
colors->setColor(i, r, g, b);
}
palette->forceInto(s->gfx_state->driver->getMode()->palette);
}
if (surface) {
// Allocate a pixmap
gfx_pixmap_t *pixmap = gfx_new_pixmap(surface->w, surface->h, 0, 0, 0);
assert(pixmap);
gfx_pixmap_alloc_index_data(pixmap);
// Copy data from the surface
memcpy(pixmap->index_data, surface->pixels, surface->w * surface->h);
pixmap->xoffset = (g_system->getWidth() - surface->w) / 2;
pixmap->yoffset = (g_system->getHeight() - surface->h) / 2;
pixmap->palette = palette;
// Copy the frame to the screen
gfx_xlate_pixmap(pixmap, s->gfx_state->driver->getMode());
gfxop_draw_pixmap(s->gfx_state, pixmap, gfx_rect(0, 0, 320, 200), Common::Point(pixmap->xoffset, pixmap->yoffset));
gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
gfx_free_pixmap(pixmap);
// Surface is freed when the codec in the video is deleted
}
}
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_QUIT:
play = false;
quit_vm();
break;
default:
break;
}
}
g_system->delayMillis(10);
}
delete player;
return s->r_acc;
}
static reg_t kShowMovie_DOS(EngineState *s, int argc, reg_t *argv) {
Common::String filename = s->_segMan->getString(argv[0]);
int delay = argv[1].toUint16(); // Time between frames in ticks
int frameNr = 0;
SeqDecoder seq;
if (!seq.loadFile(filename) && !seq.loadFile(Common::String("SEQ/") + filename)) {
warning("Failed to open movie file %s", filename.c_str());
return s->r_acc;
}
bool play = true;
while (play) {
uint32 startTime = g_system->getMillis();
gfx_pixmap_t *pixmap = seq.getFrame(play);
if (frameNr++ == 0)
pixmap->palette->forceInto(s->gfx_state->driver->getMode()->palette);
gfx_xlate_pixmap(pixmap, s->gfx_state->driver->getMode());
gfxop_draw_pixmap(s->gfx_state, pixmap, gfx_rect(0, 0, 320, 200), Common::Point(pixmap->xoffset, pixmap->yoffset));
gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
gfx_free_pixmap(pixmap);
// Wait before showing the next frame
while (play && (g_system->getMillis() < startTime + (delay * 1000 / 60))) {
// FIXME: we should probably make a function that handles quitting in these kinds of situations
Common::Event curEvent;
Common::EventManager *eventMan = g_system->getEventManager();
// Process quit events
while (eventMan->pollEvent(curEvent)) {
if (curEvent.type == Common::EVENT_QUIT) {
play = false;
quit_vm();
}
}
g_system->delayMillis(10);
}
}
return s->r_acc;
}
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
// KQ6 Windows calls this with one argument. It doesn't seem
// to have a purpose...
if (argc == 1)
return NULL_REG;
// The Windows and DOS versions use different video format as well
// as a different argument set.
if (argv[0].toUint16() == 0)
return kShowMovie_Windows(s, argc, argv);
return kShowMovie_DOS(s, argc, argv);
}
reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
// This call is used for KQ6's intro. It has one parameter, which is
// 1 when the intro begins, and 0 when it ends. It is suspected that
// this is actually a flag to enable video planar memory access, as
// the video decoder in KQ6 is specifically written for the planar
// memory model. Planar memory mode access was used for VGA "Mode X"
// (320x240 resolution, although the intro in KQ6 is 320x200).
// Refer to http://en.wikipedia.org/wiki/Mode_X
warning("STUB: SetVideoMode %d", argv[0].toUint16());
return s->r_acc;
}
// New calls for SCI11. Using those is only needed when using text-codes so that one is able to change
// font and/or color multiple times during kDisplay and kDrawControl
reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
s->_gui->textFonts(argc, argv);
return s->r_acc;
}
reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
s->_gui->textColors(argc, argv);
return s->r_acc;
}
} // End of namespace Sci