scummvm/engines/glk/frotz/processor_screen.cpp
2019-08-18 15:18:57 -07:00

508 lines
12 KiB
C++

/* 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.
*
*/
#include "glk/frotz/processor.h"
#include "glk/frotz/frotz.h"
#include "glk/conf.h"
#include "glk/events.h"
namespace Glk {
namespace Frotz {
void Processor::screen_mssg_on() {
Window &w = _wp.currWin();
if (w == _wp._lower) {
w._oldStyle = w._currStyle;
glk_set_style(style_Preformatted);
glk_put_string("\n ");
}
}
void Processor::screen_mssg_off() {
Window &w = _wp.currWin();
if (w == _wp._lower) {
glk_put_char('\n');
w.setStyle(0);
w.setStyle(w._oldStyle);
}
}
static const uint32 zchar_runes[] = {
// This mapping is based on the Amiga font in the Z-Machine
// specification, with some liberties taken.
0x16AA, // RUNIC LETTER AC A
0x16D2, // RUNIC LETTER BERKANAN BEORC BJARKAN B
0x16C7, // RUNIC LETTER IWAZ EOH
0x16D1, // RUNIC LETTER DAGAZ DAEG D
0x16D6, // RUNIC LETTER EHWAZ EH E
0x16A0, // RUNIC LETTER FEHU FEOH FE F
0x16B7, // RUNIC LETTER GEBO GYFU G
0x16BB, // RUNIC LETTER HAEGL H
0x16C1, // RUNIC LETTER ISAZ IS ISS I
0x16C4, // RUNIC LETTER GER
0x16E6, // RUNIC LETTER LONG-BRANCH-YR
0x16DA, // RUNIC LETTER LAUKAZ LAGU LOGR L
0x16D7, // RUNIC LETTER MANNAZ MAN M
0x16BE, // RUNIC LETTER NAUDIZ NYD NAUD N
0x16A9, // RUNIC LETTER OS O
0x16C8, // RUNIC LETTER PERTHO PEORTH P
0x16B3, // RUNIC LETTER CEN
0x16B1, // RUNIC LETTER RAIDO RAD REID R
0x16CB, // RUNIC LETTER SIGEL LONG-BRANCH-SOL S
0x16CF, // RUNIC LETTER TIWAZ TIR TYR T
0x16A2, // RUNIC LETTER URUZ UR U
0x16E0, // RUNIC LETTER EAR
0x16B9, // RUNIC LETTER WUNJO WYNN W
0x16C9, // RUNIC LETTER ALGIZ EOLHX
0x16A5, // RUNIC LETTER W
0x16DF // RUNIC LETTER OTHALAN ETHEL O
};
uint32 Processor::zchar_to_unicode_rune(zchar c) {
// There are only runic characters for a-z. Some versions of Beyond
// Zork will render the conversation between Prince Foo and the black
// rider in runic script, even though it contained upper case letters.
// This produced an ugly mix of runes and map-drawing characters, etc.
// which is probably why it was removed in later versions.
//
// Still, it's probably a good idea to convert the upper case letters
// to lower case to get an appropriate rune. As far as I can tell, the
// upper case letters are all used for drawing maps and progress bars.
// I don't think they're ever intended for the lower window.
//
// Apart from the runes, the arrow glyphs could perhaps also be
// sensibly converted to Unicode?
if (c >= 'a' && c <= 'z')
return zchar_runes[c - 'a'];
else if (c >= 'A' && c <= 'Z')
return zchar_runes[c - 'A'];
else
return 0;
}
void Processor::screen_char(zchar c) {
Window &w = _wp.currWin();
w.ensureTextWindow();
if (h_version == V6)
_wp.showTextWindows();
if (gos_linepending && (w == gos_linewin)) {
gos_cancel_pending_line();
if (_wp.currWin() == _wp._upper) {
_wp._upper.setCursor(Point(1, _wp._upper[Y_CURSOR] + 1));
}
if (c == '\n')
return;
}
// check fixed flag in header, game can change it at whim
int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
int curfix = ((w._currStyle & FIXED_WIDTH_STYLE) != 0);
if (forcefix && !curfix) {
w.setStyle();
fixforced = true;
} else if (!forcefix && fixforced) {
w.setStyle();
fixforced = false;
}
if (_wp._upper && _wp.currWin() == _wp._upper) {
if (c == '\n' || c == ZC_RETURN) {
glk_put_char('\n');
_wp._upper.setCursor(Point(1, _wp._upper[Y_CURSOR] + 1));
} else {
int curx = _wp._upper[X_CURSOR], cury = _wp._upper[Y_CURSOR];
if (cury == 1) {
if (curx <= (int)((sizeof statusline / sizeof(zchar)) - 1)) {
statusline[curx - 1] = c;
statusline[curx] = 0;
}
if (curx < h_screen_cols) {
glk_put_char_uni(c);
} else if (curx == h_screen_cols) {
glk_put_char_uni(c);
glk_window_move_cursor(_wp.currWin(), curx-1, cury-1);
} else {
smartstatusline();
}
curx++;
} else {
if (curx < h_screen_cols) {
glk_put_char_uni(c);
} else if (curx == (h_screen_cols)) {
glk_put_char_uni(c);
glk_window_move_cursor(_wp.currWin(), curx-1, cury-1);
}
curx++;
}
}
} else if (w == _wp._lower) {
if (c == ZC_RETURN)
glk_put_char('\n');
else {
if (w._currFont == GRAPHICS_FONT) {
uint32 runic_char = zchar_to_unicode_rune(c);
if (runic_char != 0) {
glk_set_style(style_User2);
glk_put_char_uni(runic_char);
glk_set_style(style_User1);
} else
glk_put_char_uni(c);
} else
glk_put_char_uni(c);
}
}
}
void Processor::screen_new_line() {
screen_char('\n');
}
void Processor::screen_word(const zchar *s) {
zchar c;
while ((c = *s++) != 0) {
if (c == ZC_NEW_FONT)
s++;
else if (c == ZC_NEW_STYLE)
s++;
else
screen_char(c);
}
}
void Processor::erase_screen(zword win) {
if ((short)win == -1) {
if (_wp._upper) {
_wp._upper.updateColors();
_wp._upper.clear();
}
_wp._lower.clear();
split_window(0);
_wp.setWindow(0);
}
}
void Processor::erase_window(zword win) {
if (h_version == V6 && win != _wp._cwin && h_interpreter_number != INTERP_AMIGA)
_wp[win].updateColors();
_wp[win].clear();
if (h_version == V6 && win != _wp._cwin && h_interpreter_number != INTERP_AMIGA)
_wp[_wp._cwin].updateColors();
}
void Processor::z_buffer_mode() {
// No implementation
}
void Processor::z_buffer_screen() {
store(0);
}
void Processor::z_erase_line() {
int i;
flush_buffer();
if (_wp._upper && _wp.currWin() == _wp._upper) {
int curx = _wp[_wp._cwin][X_CURSOR], cury = _wp[_wp._cwin][Y_CURSOR];
for (i = 0; i < h_screen_cols + 1 - curx; i++)
glk_put_char(' ');
_wp._upper.setCursor(Point(curx, cury));
}
}
void Processor::z_erase_window() {
short w = (short)zargs[0];
flush_buffer();
if (w == -1 || w == -2)
erase_screen(w);
else
erase_window(winarg0());
}
void Processor::z_get_cursor() {
zword y, x;
flush_buffer();
x = _wp[_wp._cwin][X_CURSOR];
y = _wp[_wp._cwin][Y_CURSOR];
if (h_version != V6) {
// convert to grid positions
y = (y - 1) / h_font_height + 1;
x = (x - 1) / h_font_width + 1;
}
storew((zword)(zargs[0] + 0), y);
storew((zword)(zargs[0] + 2), x);
}
void Processor::z_print_table() {
zword addr = zargs[0];
int curx = _wp[_wp._cwin][X_CURSOR], cury = _wp[_wp._cwin][Y_CURSOR];
zword xs = curx;
int i, j;
zbyte c;
// Supply default arguments
if (zargc < 3)
zargs[2] = 1;
if (zargc < 4)
zargs[3] = 0;
// Write text in width x height rectangle
for (i = 0; i < zargs[2]; i++, curx = xs, cury++) {
_wp[_wp._cwin].setCursor(Point(xs, cury));
for (j = 0; j < zargs[1]; j++) {
LOW_BYTE(addr, c);
addr++;
print_char(c);
}
addr += zargs[3];
}
}
void Processor::z_set_true_colour() {
int zfore = zargs[0];
int zback = zargs[1];
if (!(zfore < 0))
zfore = zRGB(zargs[0]);
if (!(zback < 0))
zback = zRGB(zargs[1]);
_wp[_wp._cwin].updateColors(zfore, zback);
}
void Processor::z_set_colour() {
int fg = (short)zargs[0];
int bg = (short)zargs[1];
zword win = (h_version == V6) ? winarg2() : 0;
if (win == 1 && h_version == V6)
bg = zcolor_Transparent;
flush_buffer();
if (fg == -1)
// Get color at cursor
fg = os_peek_color();
if (bg == -1)
bg = zcolor_Transparent;
if (fg == 0)
// keep current colour
fg = _wp[win][TRUE_FG_COLOR];
if (bg == 0)
bg = _wp[win][TRUE_BG_COLOR];
if (fg == 1)
fg = h_default_foreground;
if (bg == 1)
bg = h_default_background;
if (fg >= 0 && fg < zcolor_NUMCOLORS)
fg = zcolors[fg];
if (bg >= 0 && bg < zcolor_NUMCOLORS)
bg = zcolors[bg];
if (h_version == V6 && h_interpreter_number == INTERP_AMIGA) {
// Changing colours of window 0 affects the entire screen
if (win == 0) {
for (int i = 1; i < 8; ++i) {
int bg2 = _wp[i][TRUE_BG_COLOR];
int fg2 = _wp[i][TRUE_FG_COLOR];
if (bg2 < 16)
bg2 = (bg2 == (int)_wp[0][TRUE_BG_COLOR]) ? fg : bg;
if (fg2 < 16)
fg2 = (fg2 == (int)_wp[0][TRUE_FG_COLOR]) ? fg : bg;
_wp[i][TRUE_FG_COLOR] = fg2;
_wp[i][TRUE_BG_COLOR] = bg2;
}
}
}
_wp[win][TRUE_FG_COLOR] = fg;
_wp[win][TRUE_BG_COLOR] = bg;
if (win == _wp._cwin || h_version != V6)
_wp.currWin().updateColors(fg, bg);
}
void Processor::z_set_font() {
zword font = zargs[0];
store(_wp.currWin().setFont(font));
}
void Processor::z_set_cursor() {
int x = (int16)zargs[1], y = (int16)zargs[0];
int win = (h_version == V6) ? winarg2() : _wp._cwin;
if (zargc < 3)
zargs[2] = (zword)-3;
flush_buffer();
_wp[win].setCursor(Point(x, y));
if (_wp.currWin() == _wp._upper && _wp[win][Y_CURSOR] > (uint)mach_status_ht) {
mach_status_ht = _wp[win][Y_CURSOR];
reset_status_ht();
}
}
void Processor::z_set_text_style() {
_wp[_wp._cwin].setStyle(zargs[0]);
}
void Processor::z_set_window() {
_wp.setWindow(zargs[0]);
if (_wp._cwin == 0)
enable_scripting = true;
else
enable_scripting = false;
zargs[0] = 0xf000; // tickle tickle!
z_set_text_style();
}
void Processor::pad_status_line(int column) {
int curx = _wp._upper[X_CURSOR];
int spaces = (h_screen_cols + 1 - curx) - column;
while (spaces-- > 0)
print_char(' ');
}
void Processor::z_show_status() {
zword global0;
zword global1;
zword global2;
zword addr;
bool brief = false;
if (!_wp._upper)
return;
// One V5 game (Wishbringer Solid Gold) contains this opcode by accident,
// so just return if the version number does not fit
if (h_version >= V4)
return;
// Read all relevant global variables from the memory of the Z-machine
// into local variables
addr = h_globals;
LOW_WORD(addr, global0);
addr += 2;
LOW_WORD(addr, global1);
addr += 2;
LOW_WORD(addr, global2);
// Move to top of the status window, and print in reverse style.
_wp.setWindow(1);
_wp._upper.setReverseVideo(true);
_wp._upper.setCursor(Point(1, 1));
// If the screen width is below 55 characters then we have to use
// the brief status line format
if (h_screen_cols < 55)
brief = true;
// Print the object description for the global variable 0
print_char(' ');
print_object(global0);
// A header flag tells us whether we have to display the current
// time or the score/moves information
if (h_config & CONFIG_TIME) {
// print hours and minutes
zword hours = (global1 + 11) % 12 + 1;
pad_status_line (brief ? 15 : 20);
print_string("Time: ");
if (hours < 10)
print_char(' ');
print_num(hours);
print_char(':');
if (global2 < 10)
print_char('0');
print_num(global2);
print_char(' ');
print_char((global1 >= 12) ? 'p' : 'a');
print_char('m');
} else {
// print score and moves
pad_status_line (brief ? 15 : 30);
print_string(brief ? "S: " : "Score: ");
print_num(global1);
pad_status_line (brief ? 8 : 14);
print_string(brief ? "M: " : "Moves: ");
print_num(global2);
}
// Pad the end of the status line with spaces
pad_status_line (0);
// Return to the lower window
_wp.setWindow(0);
}
void Processor::z_split_window() {
split_window(zargs[0]);
}
} // End of namespace Frotz
} // End of namespace Glk